001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.servicemix.cxfse;
018
019 import java.lang.reflect.Field;
020 import java.lang.reflect.Method;
021 import java.util.HashMap;
022 import java.util.List;
023 import java.util.Map;
024 import java.util.concurrent.CopyOnWriteArrayList;
025
026 import javax.annotation.PostConstruct;
027 import javax.annotation.PreDestroy;
028 import javax.jbi.component.ComponentContext;
029 import javax.jbi.management.DeploymentException;
030 import javax.jbi.messaging.DeliveryChannel;
031 import javax.jbi.messaging.ExchangeStatus;
032 import javax.jbi.messaging.MessageExchange;
033 import javax.wsdl.WSDLException;
034 import javax.wsdl.factory.WSDLFactory;
035 import javax.xml.namespace.QName;
036 import javax.xml.ws.WebServiceRef;
037
038 import org.apache.cxf.Bus;
039 import org.apache.cxf.interceptor.Fault;
040 import org.apache.cxf.interceptor.Interceptor;
041 import org.apache.cxf.interceptor.InterceptorProvider;
042 import org.apache.cxf.jaxws.EndpointImpl;
043 import org.apache.cxf.jaxws.JaxWsServerFactoryBean;
044 import org.apache.cxf.jaxws.ServiceImpl;
045 import org.apache.cxf.jaxws.support.JaxWsImplementorInfo;
046 import org.apache.cxf.jaxws.support.JaxWsServiceFactoryBean;
047 import org.apache.cxf.service.model.EndpointInfo;
048 import org.apache.cxf.transport.ConduitInitiatorManager;
049 import org.apache.cxf.transport.jbi.JBIDestination;
050 import org.apache.cxf.transport.jbi.JBIDispatcherUtil;
051 import org.apache.cxf.transport.jbi.JBITransportFactory;
052 import org.apache.cxf.wsdl11.ServiceWSDLBuilder;
053 import org.apache.servicemix.common.endpoints.ProviderEndpoint;
054 import org.apache.servicemix.cxfse.interceptors.AttachmentInInterceptor;
055 import org.apache.servicemix.cxfse.interceptors.AttachmentOutInterceptor;
056 import org.apache.servicemix.cxfse.support.ReflectionUtils;
057 import org.apache.servicemix.id.IdGenerator;
058 import org.springframework.util.ReflectionUtils.FieldCallback;
059
060 /**
061 *
062 * @author gnodet
063 * @org.apache.xbean.XBean element="endpoint" description="an endpoint using
064 * CXF's JAX-WS frontend"
065 */
066 public class CxfSeEndpoint extends ProviderEndpoint implements
067 InterceptorProvider {
068
069 private static final IdGenerator ID_GENERATOR = new IdGenerator();
070
071 private Object pojo;
072
073 private EndpointImpl endpoint;
074
075 private String address;
076
077 private List<Interceptor> in = new CopyOnWriteArrayList<Interceptor>();
078
079 private List<Interceptor> out = new CopyOnWriteArrayList<Interceptor>();
080
081 private List<Interceptor> outFault = new CopyOnWriteArrayList<Interceptor>();
082
083 private List<Interceptor> inFault = new CopyOnWriteArrayList<Interceptor>();
084
085 private Map properties;
086
087 private boolean mtomEnabled;
088
089 private boolean schemaValidationEnabled;
090
091 private boolean useJBIWrapper = true;
092
093 private boolean useSOAPEnvelope = true;
094
095 /**
096 * Returns the object implementing the endpoint's functionality. It is
097 * returned as a generic Java <code>Object</code> that can be cast to the
098 * proper type.
099 *
100 * @return the pojo
101 */
102 public Object getPojo() {
103 return pojo;
104 }
105
106 /**
107 * Specifies the object implementing the endpoint's functionality. This
108 * object should use the JAX-WS annotations.
109 *
110 * @param pojo
111 * a JAX-WS annotated object
112 *
113 * @org.apache.xbean.Property description="a bean configuring the JAX-WS
114 * annotated implementation for the endpoint"
115 */
116 public void setPojo(Object pojo) {
117 this.pojo = pojo;
118 }
119
120 /**
121 * Returns the list of interceptors used to process fault messages being
122 * sent back to the consumer.
123 *
124 * @return a list of <code>Interceptor</code> objects
125 */
126 public List<Interceptor> getOutFaultInterceptors() {
127 return outFault;
128 }
129
130 /**
131 * Returns the list of interceptors used to process fault messages being
132 * recieved by the endpoint.
133 *
134 * @return a list of <code>Interceptor</code> objects
135 */
136 public List<Interceptor> getInFaultInterceptors() {
137 return inFault;
138 }
139
140 /**
141 * Returns the list of interceptors used to process messages being recieved
142 * by the endpoint.
143 *
144 * @return a list of <code>Interceptor</code> objects
145 */
146 public List<Interceptor> getInInterceptors() {
147 return in;
148 }
149
150 /**
151 * Returns the list of interceptors used to process responses being sent
152 * back to the consumer.
153 *
154 * @return a list of <code>Interceptor</code> objects
155 */
156 public List<Interceptor> getOutInterceptors() {
157 return out;
158 }
159
160 /**
161 * Specifies a list of interceptors used to process requests recieved by the
162 * endpoint.
163 *
164 * @param interceptors
165 * a list of <code>Interceptor</code> objects
166 * @org.apache.xbean.Property description="a list of beans configuring
167 * interceptors that process incoming requests"
168 */
169 public void setInInterceptors(List<Interceptor> interceptors) {
170 in = interceptors;
171 }
172
173 /**
174 * Specifies a list of interceptors used to process faults recieved by the
175 * endpoint.
176 *
177 * @param interceptors
178 * a list of <code>Interceptor</code> objects
179 * @org.apache.xbean.Property description="a list of beans configuring
180 * interceptors that process incoming faults"
181 */
182 public void setInFaultInterceptors(List<Interceptor> interceptors) {
183 inFault = interceptors;
184 }
185
186 /**
187 * Specifies a list of interceptors used to process responses sent by the
188 * endpoint.
189 *
190 * @param interceptors
191 * a list of <code>Interceptor</code> objects
192 * @org.apache.xbean.Property description="a list of beans configuring
193 * interceptors that process response messages"
194 */
195 public void setOutInterceptors(List<Interceptor> interceptors) {
196 out = interceptors;
197 }
198
199 /**
200 * Specifies a list of interceptors used to process faults sent by the
201 * endpoint.
202 *
203 * @param interceptors
204 * a list of <code>Interceptor</code> objects
205 * @org.apache.xbean.Property description="a list of beans configuring
206 * interceptors that process fault messages being
207 * returned to the consumer"
208 */
209 public void setOutFaultInterceptors(List<Interceptor> interceptors) {
210 outFault = interceptors;
211 }
212
213 public Map getProperties() {
214 return properties;
215 }
216
217 public void setProperties(Map properties) {
218 this.properties = properties;
219 }
220
221 /*
222 * (non-Javadoc)
223 *
224 * @see org.apache.servicemix.common.Endpoint#validate()
225 */
226 @Override
227 public void validate() throws DeploymentException {
228 if (getPojo() == null) {
229 throw new DeploymentException("pojo must be set");
230 }
231 JaxWsServiceFactoryBean serviceFactory = new JaxWsServiceFactoryBean();
232 serviceFactory.setPopulateFromClass(true);
233 endpoint = new EndpointImpl(getBus(), getPojo(),
234 new JaxWsServerFactoryBean(serviceFactory));
235 if (isUseJBIWrapper()) {
236 endpoint
237 .setBindingUri(org.apache.cxf.binding.jbi.JBIConstants.NS_JBI_BINDING);
238 }
239 endpoint.setInInterceptors(getInInterceptors());
240 endpoint.setInFaultInterceptors(getInFaultInterceptors());
241 endpoint.setOutInterceptors(getOutInterceptors());
242 endpoint.setOutFaultInterceptors(getOutFaultInterceptors());
243 if (isMtomEnabled()) {
244 endpoint.getInInterceptors().add(new AttachmentInInterceptor());
245 endpoint.getOutInterceptors().add(new AttachmentOutInterceptor());
246 }
247 if (isSchemaValidationEnabled()) {
248 if (endpoint.getProperties() == null) {
249 endpoint.setProperties(new HashMap<String, Object>());
250 }
251 endpoint.getProperties().put(org.apache.cxf.message.Message.SCHEMA_VALIDATION_ENABLED, true);
252 }
253 JaxWsImplementorInfo implInfo = new JaxWsImplementorInfo(getPojo()
254 .getClass());
255 setService(implInfo.getServiceName());
256 setInterfaceName(implInfo.getInterfaceName());
257 setEndpoint(implInfo.getEndpointName().getLocalPart());
258 super.validate();
259 }
260
261 private void removeInterceptor(List<Interceptor> interceptors,
262 String whichInterceptor) {
263 for (Interceptor interceptor : interceptors) {
264 if (interceptor.getClass().getName().endsWith(whichInterceptor)) {
265 interceptors.remove(interceptor);
266 }
267 }
268 }
269
270 /*
271 * (non-Javadoc)
272 *
273 * @see org.apache.servicemix.common.endpoints.ProviderEndpoint#process(javax.jbi.messaging.MessageExchange)
274 */
275 @Override
276 public void process(MessageExchange exchange) throws Exception {
277
278 QName opeName = exchange.getOperation();
279 EndpointInfo ei = endpoint.getServer().getEndpoint().getEndpointInfo();
280 if (opeName == null) {
281 // if interface only have one operation, may not specify the opeName
282 // in MessageExchange
283 if (ei.getBinding().getOperations().size() == 1) {
284 opeName = ei.getBinding().getOperations().iterator().next()
285 .getName();
286 exchange.setOperation(opeName);
287 } else {
288 throw new Fault(new Exception(
289 "Operation not bound on this MessageExchange"));
290
291 }
292 }
293
294 JBITransportFactory jbiTransportFactory = (JBITransportFactory) getBus()
295 .getExtension(ConduitInitiatorManager.class)
296 .getConduitInitiator(CxfSeComponent.JBI_TRANSPORT_ID);
297
298 QName serviceName = exchange.getService();
299 if (serviceName == null) {
300 serviceName = getService();
301 exchange.setService(serviceName);
302 }
303 QName interfaceName = exchange.getInterfaceName();
304 if (interfaceName == null) {
305 interfaceName = getInterfaceName();
306 exchange.setInterfaceName(interfaceName);
307 }
308 JBIDestination jbiDestination = jbiTransportFactory
309 .getDestination(serviceName.toString()
310 + interfaceName.toString());
311 DeliveryChannel dc = getContext().getDeliveryChannel();
312 jbiTransportFactory.setDeliveryChannel(dc);
313
314 jbiDestination.setDeliveryChannel(dc);
315 if (exchange.getStatus() == ExchangeStatus.ACTIVE) {
316 jbiDestination.getJBIDispatcherUtil().dispatch(exchange);
317 }
318
319 }
320
321 /*
322 * (non-Javadoc)
323 *
324 * @see org.apache.servicemix.common.endpoints.ProviderEndpoint#start()
325 */
326 @Override
327 public void start() throws Exception {
328 super.start();
329 address = "jbi://" + ID_GENERATOR.generateSanitizedId();
330 try {
331 endpoint.publish(address);
332 } catch (Exception e) {
333 e.printStackTrace();
334 }
335
336 setService(endpoint.getServer().getEndpoint().getService().getName());
337 setEndpoint(endpoint.getServer().getEndpoint().getEndpointInfo()
338 .getName().getLocalPart());
339 if (!isUseJBIWrapper() && !isUseSOAPEnvelope()) {
340 removeInterceptor(endpoint.getServer().getEndpoint().getBinding()
341 .getInInterceptors(), "ReadHeadersInterceptor");
342 removeInterceptor(endpoint.getServer().getEndpoint().getBinding()
343 .getInFaultInterceptors(), "ReadHeadersInterceptor");
344 removeInterceptor(endpoint.getServer().getEndpoint().getBinding()
345 .getOutInterceptors(), "SoapOutInterceptor");
346 removeInterceptor(endpoint.getServer().getEndpoint().getBinding()
347 .getOutFaultInterceptors(), "SoapOutInterceptor");
348 removeInterceptor(endpoint.getServer().getEndpoint().getBinding()
349 .getOutInterceptors(), "StaxOutInterceptor");
350 }
351
352 try {
353 definition = new ServiceWSDLBuilder(getBus(), endpoint.getServer()
354 .getEndpoint().getService().getServiceInfos().iterator()
355 .next()).build();
356 description = WSDLFactory.newInstance().newWSDLWriter()
357 .getDocument(definition);
358 } catch (WSDLException e) {
359 throw new DeploymentException(e);
360 }
361 ReflectionUtils.doWithFields(getPojo().getClass(), new FieldCallback() {
362 public void doWith(Field field) throws IllegalArgumentException,
363 IllegalAccessException {
364 if (field.getAnnotation(WebServiceRef.class) != null) {
365 ServiceImpl s = new ServiceImpl(getBus(), null, null, field
366 .getType());
367 s.addPort(new QName("port"),
368 JBITransportFactory.TRANSPORT_ID, "jbi://"
369 + ID_GENERATOR.generateSanitizedId());
370 Object o = s.getPort(new QName("port"), field.getType());
371 field.setAccessible(true);
372 field.set(getPojo(), o);
373 }
374 }
375 });
376 ReflectionUtils.callLifecycleMethod(getPojo(), PostConstruct.class);
377 injectPojo();
378 }
379
380 /*
381 * (non-Javadoc)
382 *
383 * @see org.apache.servicemix.common.endpoints.ProviderEndpoint#stop()
384 */
385 @Override
386 public void stop() throws Exception {
387 endpoint.stop();
388 ReflectionUtils.callLifecycleMethod(getPojo(), PreDestroy.class);
389 JBIDispatcherUtil.clean();
390 JBITransportFactory jbiTransportFactory = (JBITransportFactory) getBus()
391 .getExtension(ConduitInitiatorManager.class)
392 .getConduitInitiator(CxfSeComponent.JBI_TRANSPORT_ID);
393 jbiTransportFactory.setDeliveryChannel(null);
394 jbiTransportFactory.removeDestination(getService().toString()
395 + getInterfaceName().toString());
396 super.stop();
397 }
398
399 protected Bus getBus() {
400 return ((CxfSeComponent) getServiceUnit().getComponent()).getBus();
401 }
402
403 @PostConstruct
404 protected void injectPojo() {
405 try {
406 ComponentContext context = getContext();
407 Method mth = pojo.getClass().getMethod("setContext",
408 new Class[] {ComponentContext.class});
409 if (mth != null) {
410 mth.invoke(pojo, new Object[] {context});
411 }
412 } catch (Exception e) {
413 logger
414 .debug("Unable to inject ComponentContext: "
415 + e.getMessage());
416 }
417
418 }
419
420 /**
421 * Specifies if the endpoint can process messages with binary data.
422 *
423 * @param mtomEnabled
424 * a <code>boolean</code>
425 * @org.apache.xbean.Property description="Specifies if the service can
426 * consume MTOM formatted binary data. The
427 * default is <code>false</code>."
428 */
429 public void setMtomEnabled(boolean mtomEnabled) {
430 this.mtomEnabled = mtomEnabled;
431 }
432
433 public boolean isMtomEnabled() {
434 return mtomEnabled;
435 }
436
437 public boolean isSchemaValidationEnabled() {
438 return schemaValidationEnabled;
439 }
440
441 public void setSchemaValidationEnabled(boolean schemaValidationEnabled) {
442 this.schemaValidationEnabled = schemaValidationEnabled;
443 }
444
445 /**
446 * Specifies if the endpoint expects messages that are encased in the JBI
447 * wrapper used for SOAP messages.
448 *
449 * @param mtomEnabled
450 * a <code>boolean</code>
451 * @org.apache.xbean.Property description="Specifies if the endpoint expects
452 * to receive the JBI wrapper in the message
453 * received from the NMR. The default is
454 * <code>true</code>."
455 */
456 public void setUseJBIWrapper(boolean useJBIWrapper) {
457 this.useJBIWrapper = useJBIWrapper;
458 }
459
460 public boolean isUseJBIWrapper() {
461 return useJBIWrapper;
462 }
463
464 /**
465 * Specifies if the endpoint expects soap messages when useJBIWrapper is
466 * false, if useJBIWrapper is true then ignore useSOAPEnvelope
467 *
468 * @org.apache.xbean.Property description="Specifies if the endpoint expects
469 * soap messages when useJBIWrapper is false, if
470 * useJBIWrapper is true then ignore
471 * useSOAPEnvelope. The default is
472 * <code>true</code>.
473 */
474 public void setUseSOAPEnvelope(boolean useSOAPEnvelope) {
475 this.useSOAPEnvelope = useSOAPEnvelope;
476 }
477
478 public boolean isUseSOAPEnvelope() {
479 return useSOAPEnvelope;
480 }
481
482 }