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.aegis.databinding.AegisDatabinding;
040 import org.apache.cxf.endpoint.Server;
041 import org.apache.cxf.frontend.ServerFactoryBean;
042 import org.apache.cxf.interceptor.Fault;
043 import org.apache.cxf.interceptor.Interceptor;
044 import org.apache.cxf.interceptor.InterceptorProvider;
045 import org.apache.cxf.jaxws.EndpointImpl;
046 import org.apache.cxf.jaxws.JaxWsServerFactoryBean;
047 import org.apache.cxf.jaxws.ServiceImpl;
048 import org.apache.cxf.jaxws.support.JaxWsImplementorInfo;
049 import org.apache.cxf.jaxws.support.JaxWsServiceFactoryBean;
050 import org.apache.cxf.service.model.EndpointInfo;
051 import org.apache.cxf.transport.ConduitInitiatorManager;
052 import org.apache.cxf.transport.jbi.JBIDestination;
053 import org.apache.cxf.transport.jbi.JBIDispatcherUtil;
054 import org.apache.cxf.transport.jbi.JBITransportFactory;
055 import org.apache.cxf.wsdl11.ServiceWSDLBuilder;
056 import org.apache.servicemix.common.endpoints.ProviderEndpoint;
057 import org.apache.servicemix.cxfse.interceptors.AttachmentInInterceptor;
058 import org.apache.servicemix.cxfse.interceptors.AttachmentOutInterceptor;
059 import org.apache.servicemix.cxfse.support.ReflectionUtils;
060 import org.apache.servicemix.id.IdGenerator;
061 import org.springframework.util.ReflectionUtils.FieldCallback;
062
063 /**
064 *
065 * @author gnodet
066 * @org.apache.xbean.XBean element="endpoint" description="an endpoint using
067 * CXF's JAX-WS frontend"
068 */
069 public class CxfSeEndpoint extends ProviderEndpoint implements
070 InterceptorProvider {
071
072 private static final IdGenerator ID_GENERATOR = new IdGenerator();
073
074 private Object pojo;
075
076 private EndpointImpl endpoint;
077
078 private String address;
079
080 private ServerFactoryBean sf;
081
082 private List<Interceptor> in = new CopyOnWriteArrayList<Interceptor>();
083
084 private List<Interceptor> out = new CopyOnWriteArrayList<Interceptor>();
085
086 private List<Interceptor> outFault = new CopyOnWriteArrayList<Interceptor>();
087
088 private List<Interceptor> inFault = new CopyOnWriteArrayList<Interceptor>();
089
090 private Map properties;
091
092 private Server server;
093
094 private boolean mtomEnabled;
095
096 private boolean schemaValidationEnabled;
097
098 private boolean useJBIWrapper = true;
099
100 private boolean useSOAPEnvelope = true;
101
102 private boolean useAegis;
103
104 /**
105 * Returns the object implementing the endpoint's functionality. It is
106 * returned as a generic Java <code>Object</code> that can be cast to the
107 * proper type.
108 *
109 * @return the pojo
110 */
111 public Object getPojo() {
112 return pojo;
113 }
114
115 /**
116 * Specifies the object implementing the endpoint's functionality. This
117 * object should use the JAX-WS annotations.
118 *
119 * @param pojo
120 * a JAX-WS annotated object
121 *
122 * @org.apache.xbean.Property description="a bean configuring the JAX-WS
123 * annotated implementation for the endpoint"
124 */
125 public void setPojo(Object pojo) {
126 this.pojo = pojo;
127 }
128
129 /**
130 * Returns the list of interceptors used to process fault messages being
131 * sent back to the consumer.
132 *
133 * @return a list of <code>Interceptor</code> objects
134 */
135 public List<Interceptor> getOutFaultInterceptors() {
136 return outFault;
137 }
138
139 /**
140 * Returns the list of interceptors used to process fault messages being
141 * recieved by the endpoint.
142 *
143 * @return a list of <code>Interceptor</code> objects
144 */
145 public List<Interceptor> getInFaultInterceptors() {
146 return inFault;
147 }
148
149 /**
150 * Returns the list of interceptors used to process messages being recieved
151 * by the endpoint.
152 *
153 * @return a list of <code>Interceptor</code> objects
154 */
155 public List<Interceptor> getInInterceptors() {
156 return in;
157 }
158
159 /**
160 * Returns the list of interceptors used to process responses being sent
161 * back to the consumer.
162 *
163 * @return a list of <code>Interceptor</code> objects
164 */
165 public List<Interceptor> getOutInterceptors() {
166 return out;
167 }
168
169 /**
170 * Specifies a list of interceptors used to process requests recieved by the
171 * endpoint.
172 *
173 * @param interceptors
174 * a list of <code>Interceptor</code> objects
175 * @org.apache.xbean.Property description="a list of beans configuring
176 * interceptors that process incoming requests"
177 */
178 public void setInInterceptors(List<Interceptor> interceptors) {
179 in = interceptors;
180 }
181
182 /**
183 * Specifies a list of interceptors used to process faults recieved by the
184 * endpoint.
185 *
186 * @param interceptors
187 * a list of <code>Interceptor</code> objects
188 * @org.apache.xbean.Property description="a list of beans configuring
189 * interceptors that process incoming faults"
190 */
191 public void setInFaultInterceptors(List<Interceptor> interceptors) {
192 inFault = interceptors;
193 }
194
195 /**
196 * Specifies a list of interceptors used to process responses sent by the
197 * endpoint.
198 *
199 * @param interceptors
200 * a list of <code>Interceptor</code> objects
201 * @org.apache.xbean.Property description="a list of beans configuring
202 * interceptors that process response messages"
203 */
204 public void setOutInterceptors(List<Interceptor> interceptors) {
205 out = interceptors;
206 }
207
208 /**
209 * Specifies a list of interceptors used to process faults sent by the
210 * endpoint.
211 *
212 * @param interceptors
213 * a list of <code>Interceptor</code> objects
214 * @org.apache.xbean.Property description="a list of beans configuring
215 * interceptors that process fault messages being
216 * returned to the consumer"
217 */
218 public void setOutFaultInterceptors(List<Interceptor> interceptors) {
219 outFault = interceptors;
220 }
221
222 public Map getProperties() {
223 return properties;
224 }
225
226 public void setProperties(Map properties) {
227 this.properties = properties;
228 }
229
230 /*
231 * (non-Javadoc)
232 *
233 * @see org.apache.servicemix.common.Endpoint#validate()
234 */
235 @Override
236 public void validate() throws DeploymentException {
237 if (getPojo() == null) {
238 throw new DeploymentException("pojo must be set");
239 }
240 if (isUseAegis()) {
241 sf = new ServerFactoryBean();
242 sf.setServiceBean(getPojo());
243 sf.setAddress("jbi://" + ID_GENERATOR.generateSanitizedId());
244 sf.getServiceFactory().setDataBinding(new AegisDatabinding());
245 sf.getServiceFactory().setPopulateFromClass(true);
246 sf.setStart(false);
247 if (isUseJBIWrapper()) {
248 sf.setBindingId(org.apache.cxf.binding.jbi.JBIConstants.NS_JBI_BINDING);
249 }
250 server = sf.create();
251 server.getEndpoint().getInInterceptors().addAll(getInInterceptors());
252 server.getEndpoint().getInFaultInterceptors().addAll(getInFaultInterceptors());
253 server.getEndpoint().getOutInterceptors().addAll(getOutInterceptors());
254 server.getEndpoint().getOutFaultInterceptors().addAll(getOutFaultInterceptors());
255 if (isMtomEnabled()) {
256 server.getEndpoint().getInInterceptors().add(new AttachmentInInterceptor());
257 server.getEndpoint().getOutInterceptors().add(new AttachmentOutInterceptor());
258 }
259
260 if (sf.getServiceFactory().getServiceQName() != null) {
261 setService(sf.getServiceFactory().getServiceQName());
262 }
263 if (sf.getServiceFactory().getEndpointInfo().getName() != null) {
264 setEndpoint(sf.getServiceFactory().getEndpointInfo().getName().getLocalPart());
265 }
266 if (sf.getServiceFactory().getInterfaceName() != null) {
267 setInterfaceName(sf.getServiceFactory().getInterfaceName());
268 }
269 } else {
270 JaxWsServiceFactoryBean serviceFactory = new JaxWsServiceFactoryBean();
271 serviceFactory.setPopulateFromClass(true);
272 endpoint = new EndpointImpl(getBus(), getPojo(),
273 new JaxWsServerFactoryBean(serviceFactory));
274 if (isUseJBIWrapper()) {
275 endpoint
276 .setBindingUri(org.apache.cxf.binding.jbi.JBIConstants.NS_JBI_BINDING);
277 }
278 endpoint.setInInterceptors(getInInterceptors());
279 endpoint.setInFaultInterceptors(getInFaultInterceptors());
280 endpoint.setOutInterceptors(getOutInterceptors());
281 endpoint.setOutFaultInterceptors(getOutFaultInterceptors());
282 if (isMtomEnabled()) {
283 endpoint.getInInterceptors().add(new AttachmentInInterceptor());
284 endpoint.getOutInterceptors().add(new AttachmentOutInterceptor());
285 }
286 if (isSchemaValidationEnabled()) {
287 if (endpoint.getProperties() == null) {
288 endpoint.setProperties(new HashMap<String, Object>());
289 }
290 endpoint.getProperties().put(org.apache.cxf.message.Message.SCHEMA_VALIDATION_ENABLED, true);
291 }
292
293 JaxWsImplementorInfo implInfo = new JaxWsImplementorInfo(getPojo()
294 .getClass());
295 setService(implInfo.getServiceName());
296
297 setInterfaceName(implInfo.getInterfaceName());
298 setEndpoint(implInfo.getEndpointName().getLocalPart());
299
300 }
301 super.validate();
302 }
303
304 private void removeInterceptor(List<Interceptor> interceptors,
305 String whichInterceptor) {
306 for (Interceptor interceptor : interceptors) {
307 if (interceptor.getClass().getName().endsWith(whichInterceptor)) {
308 interceptors.remove(interceptor);
309 }
310 }
311 }
312
313 /*
314 * (non-Javadoc)
315 *
316 * @see org.apache.servicemix.common.endpoints.ProviderEndpoint#process(javax.jbi.messaging.MessageExchange)
317 */
318 @Override
319 public void process(MessageExchange exchange) throws Exception {
320
321 QName opeName = exchange.getOperation();
322 EndpointInfo ei = server.getEndpoint().getEndpointInfo();
323
324 if (opeName == null) {
325 // if interface only have one operation, may not specify the opeName
326 // in MessageExchange
327 if (ei.getBinding().getOperations().size() == 1) {
328 opeName = ei.getBinding().getOperations().iterator().next()
329 .getName();
330 exchange.setOperation(opeName);
331 } else {
332 throw new Fault(new Exception(
333 "Operation not bound on this MessageExchange"));
334
335 }
336 }
337 JBITransportFactory jbiTransportFactory = (JBITransportFactory) getBus()
338 .getExtension(ConduitInitiatorManager.class)
339 .getConduitInitiator(CxfSeComponent.JBI_TRANSPORT_ID);
340
341 QName serviceName = exchange.getService();
342 if (serviceName == null) {
343 serviceName = getService();
344 exchange.setService(serviceName);
345 }
346 QName interfaceName = exchange.getInterfaceName();
347 if (interfaceName == null) {
348 interfaceName = getInterfaceName();
349 exchange.setInterfaceName(interfaceName);
350 }
351 JBIDestination jbiDestination = jbiTransportFactory
352 .getDestination(serviceName.toString()
353 + interfaceName.toString());
354 DeliveryChannel dc = getContext().getDeliveryChannel();
355 jbiTransportFactory.setDeliveryChannel(dc);
356
357 jbiDestination.setDeliveryChannel(dc);
358 if (exchange.getStatus() == ExchangeStatus.ACTIVE) {
359 jbiDestination.getJBIDispatcherUtil().dispatch(exchange);
360 }
361
362 }
363
364 /*
365 * (non-Javadoc)
366 *
367 * @see org.apache.servicemix.common.endpoints.ProviderEndpoint#start()
368 */
369 @Override
370 public void start() throws Exception {
371 super.start();
372 address = "jbi://" + ID_GENERATOR.generateSanitizedId();
373 try {
374 if (isUseAegis()) {
375 server.start();
376 } else {
377 endpoint.publish(address);
378 server = endpoint.getServer();
379 }
380 } catch (Exception e) {
381 e.printStackTrace();
382 }
383
384 setService(server.getEndpoint().getService().getName());
385 setEndpoint(server.getEndpoint().getEndpointInfo()
386 .getName().getLocalPart());
387 if (!isUseJBIWrapper() && !isUseSOAPEnvelope()) {
388 removeInterceptor(server.getEndpoint().getBinding()
389 .getInInterceptors(), "ReadHeadersInterceptor");
390 removeInterceptor(server.getEndpoint().getBinding()
391 .getInFaultInterceptors(), "ReadHeadersInterceptor");
392 removeInterceptor(server.getEndpoint().getBinding()
393 .getOutInterceptors(), "SoapOutInterceptor");
394 removeInterceptor(server.getEndpoint().getBinding()
395 .getOutFaultInterceptors(), "SoapOutInterceptor");
396 removeInterceptor(server.getEndpoint().getBinding()
397 .getOutInterceptors(), "StaxOutInterceptor");
398 }
399
400 try {
401 definition = new ServiceWSDLBuilder(getBus(), server
402 .getEndpoint().getService().getServiceInfos().iterator()
403 .next()).build();
404 description = WSDLFactory.newInstance().newWSDLWriter()
405 .getDocument(definition);
406 } catch (WSDLException e) {
407 throw new DeploymentException(e);
408 }
409 ReflectionUtils.doWithFields(getPojo().getClass(), new FieldCallback() {
410 public void doWith(Field field) throws IllegalArgumentException,
411 IllegalAccessException {
412 if (field.getAnnotation(WebServiceRef.class) != null) {
413 ServiceImpl s = new ServiceImpl(getBus(), null, null, field
414 .getType());
415 s.addPort(new QName("port"),
416 JBITransportFactory.TRANSPORT_ID, "jbi://"
417 + ID_GENERATOR.generateSanitizedId());
418 Object o = s.getPort(new QName("port"), field.getType());
419 field.setAccessible(true);
420 field.set(getPojo(), o);
421 }
422 }
423 });
424 ReflectionUtils.callLifecycleMethod(getPojo(), PostConstruct.class);
425 injectPojo();
426 }
427
428 /*
429 * (non-Javadoc)
430 *
431 * @see org.apache.servicemix.common.endpoints.ProviderEndpoint#stop()
432 */
433 @Override
434 public void stop() throws Exception {
435 if (isUseAegis()) {
436 server.stop();
437 } else {
438 endpoint.stop();
439 }
440 ReflectionUtils.callLifecycleMethod(getPojo(), PreDestroy.class);
441 JBIDispatcherUtil.clean();
442 JBITransportFactory jbiTransportFactory = (JBITransportFactory) getBus()
443 .getExtension(ConduitInitiatorManager.class)
444 .getConduitInitiator(CxfSeComponent.JBI_TRANSPORT_ID);
445 jbiTransportFactory.setDeliveryChannel(null);
446 jbiTransportFactory.removeDestination(getService().toString()
447 + getInterfaceName().toString());
448 super.stop();
449 }
450
451 protected Bus getBus() {
452 return ((CxfSeComponent) getServiceUnit().getComponent()).getBus();
453 }
454
455 @PostConstruct
456 protected void injectPojo() {
457 try {
458 ComponentContext context = getContext();
459 Method mth = pojo.getClass().getMethod("setContext",
460 new Class[] {ComponentContext.class});
461 if (mth != null) {
462 mth.invoke(pojo, new Object[] {context});
463 }
464 } catch (Exception e) {
465 logger
466 .debug("Unable to inject ComponentContext: "
467 + e.getMessage());
468 }
469
470 }
471
472 /**
473 * Specifies if the endpoint can process messages with binary data.
474 *
475 * @param mtomEnabled
476 * a <code>boolean</code>
477 * @org.apache.xbean.Property description="Specifies if the service can
478 * consume MTOM formatted binary data. The
479 * default is <code>false</code>."
480 */
481 public void setMtomEnabled(boolean mtomEnabled) {
482 this.mtomEnabled = mtomEnabled;
483 }
484
485 public boolean isMtomEnabled() {
486 return mtomEnabled;
487 }
488
489 public boolean isSchemaValidationEnabled() {
490 return schemaValidationEnabled;
491 }
492
493 public void setSchemaValidationEnabled(boolean schemaValidationEnabled) {
494 this.schemaValidationEnabled = schemaValidationEnabled;
495 }
496
497 /**
498 * Specifies if the endpoint expects messages that are encased in the JBI
499 * wrapper used for SOAP messages.
500 *
501 * @param mtomEnabled
502 * a <code>boolean</code>
503 * @org.apache.xbean.Property description="Specifies if the endpoint expects
504 * to receive the JBI wrapper in the message
505 * received from the NMR. The default is
506 * <code>true</code>."
507 */
508 public void setUseJBIWrapper(boolean useJBIWrapper) {
509 this.useJBIWrapper = useJBIWrapper;
510 }
511
512 public boolean isUseJBIWrapper() {
513 return useJBIWrapper;
514 }
515
516 /**
517 * Specifies if the endpoint expects soap messages when useJBIWrapper is
518 * false, if useJBIWrapper is true then ignore useSOAPEnvelope
519 *
520 * @org.apache.xbean.Property description="Specifies if the endpoint expects
521 * soap messages when useJBIWrapper is false, if
522 * useJBIWrapper is true then ignore
523 * useSOAPEnvelope. The default is
524 * <code>true</code>.
525 */
526 public void setUseSOAPEnvelope(boolean useSOAPEnvelope) {
527 this.useSOAPEnvelope = useSOAPEnvelope;
528 }
529
530 public boolean isUseSOAPEnvelope() {
531 return useSOAPEnvelope;
532 }
533
534 /**
535 * Specifies if the endpoint use aegis databinding to marshell/unmarshell message
536 *
537 * @org.apache.xbean.Property description="Specifies if the endpoint use aegis databinding to marshell/unmarshell message.
538 * The default is <code>false</code>.
539 */
540 public void setUseAegis(boolean useAegis) {
541 this.useAegis = useAegis;
542 }
543
544
545 public boolean isUseAegis() {
546 return useAegis;
547 }
548
549 }