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