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.addAll(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.addAll(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.addAll(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.addAll(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 if (exchange.getStatus() != ExchangeStatus.ACTIVE) {
324 return;
325 }
326 QName opeName = exchange.getOperation();
327 EndpointInfo ei = server.getEndpoint().getEndpointInfo();
328
329 if (opeName == null) {
330 // if interface only have one operation, may not specify the opeName
331 // in MessageExchange
332 if (ei.getBinding().getOperations().size() == 1) {
333 opeName = ei.getBinding().getOperations().iterator().next()
334 .getName();
335 exchange.setOperation(opeName);
336 } else {
337 throw new Fault(new Exception(
338 "Operation not bound on this MessageExchange"));
339
340 }
341 }
342 JBITransportFactory jbiTransportFactory = (JBITransportFactory) getBus()
343 .getExtension(ConduitInitiatorManager.class)
344 .getConduitInitiator(CxfSeComponent.JBI_TRANSPORT_ID);
345
346 QName serviceName = exchange.getService();
347 if (serviceName == null) {
348 serviceName = getService();
349 exchange.setService(serviceName);
350 }
351 QName interfaceName = exchange.getInterfaceName();
352 if (interfaceName == null) {
353 interfaceName = getInterfaceName();
354 exchange.setInterfaceName(interfaceName);
355 }
356 JBIDestination jbiDestination = jbiTransportFactory
357 .getDestination(serviceName.toString()
358 + interfaceName.toString());
359 DeliveryChannel dc = getContext().getDeliveryChannel();
360 jbiTransportFactory.setDeliveryChannel(dc);
361
362 jbiDestination.setDeliveryChannel(dc);
363 if (exchange.getStatus() == ExchangeStatus.ACTIVE) {
364 jbiDestination.getJBIDispatcherUtil().dispatch(exchange);
365 }
366 if (exchange instanceof InOnly || exchange instanceof RobustInOnly) {
367 exchange.setStatus(ExchangeStatus.DONE);
368 dc.send(exchange);
369 }
370 }
371
372 /*
373 * (non-Javadoc)
374 *
375 * @see org.apache.servicemix.common.endpoints.ProviderEndpoint#start()
376 */
377 @Override
378 public void start() throws Exception {
379 super.start();
380 address = "jbi://" + ID_GENERATOR.generateSanitizedId();
381 try {
382 if (isUseAegis()) {
383 server.start();
384 } else {
385 endpoint.publish(address);
386 server = endpoint.getServer();
387 }
388 } catch (Exception e) {
389 e.printStackTrace();
390 }
391
392 setService(server.getEndpoint().getService().getName());
393 setEndpoint(server.getEndpoint().getEndpointInfo()
394 .getName().getLocalPart());
395 if (!isUseJBIWrapper() && !isUseSOAPEnvelope()) {
396 removeInterceptor(server.getEndpoint().getBinding()
397 .getInInterceptors(), "ReadHeadersInterceptor");
398 removeInterceptor(server.getEndpoint().getBinding()
399 .getInFaultInterceptors(), "ReadHeadersInterceptor");
400 removeInterceptor(server.getEndpoint().getBinding()
401 .getOutInterceptors(), "SoapOutInterceptor");
402 removeInterceptor(server.getEndpoint().getBinding()
403 .getOutFaultInterceptors(), "SoapOutInterceptor");
404 removeInterceptor(server.getEndpoint().getBinding()
405 .getOutInterceptors(), "StaxOutInterceptor");
406 }
407
408 try {
409 definition = new ServiceWSDLBuilder(getBus(), server
410 .getEndpoint().getService().getServiceInfos().iterator()
411 .next()).build();
412 description = WSDLFactory.newInstance().newWSDLWriter()
413 .getDocument(definition);
414 } catch (WSDLException e) {
415 throw new DeploymentException(e);
416 }
417 ReflectionUtils.doWithFields(getPojo().getClass(), new FieldCallback() {
418 public void doWith(Field field) throws IllegalArgumentException,
419 IllegalAccessException {
420 if (field.getAnnotation(WebServiceRef.class) != null) {
421 ServiceImpl s = new ServiceImpl(getBus(), null, null, field
422 .getType());
423 s.addPort(new QName("port"),
424 JBITransportFactory.TRANSPORT_ID, "jbi://"
425 + ID_GENERATOR.generateSanitizedId());
426 Object o = s.getPort(new QName("port"), field.getType());
427 field.setAccessible(true);
428 field.set(getPojo(), o);
429 }
430 }
431 });
432 ReflectionUtils.callLifecycleMethod(getPojo(), PostConstruct.class);
433 injectPojo();
434 }
435
436 /*
437 * (non-Javadoc)
438 *
439 * @see org.apache.servicemix.common.endpoints.ProviderEndpoint#stop()
440 */
441 @Override
442 public void stop() throws Exception {
443 if (isUseAegis()) {
444 server.stop();
445 } else {
446 endpoint.stop();
447 }
448 ReflectionUtils.callLifecycleMethod(getPojo(), PreDestroy.class);
449 JBIDispatcherUtil.clean();
450 JBITransportFactory jbiTransportFactory = (JBITransportFactory) getBus()
451 .getExtension(ConduitInitiatorManager.class)
452 .getConduitInitiator(CxfSeComponent.JBI_TRANSPORT_ID);
453 jbiTransportFactory.setDeliveryChannel(null);
454 jbiTransportFactory.removeDestination(getService().toString()
455 + getInterfaceName().toString());
456 super.stop();
457 }
458
459 protected Bus getBus() {
460 return ((CxfSeComponent) getServiceUnit().getComponent()).getBus();
461 }
462
463 @PostConstruct
464 protected void injectPojo() {
465 try {
466 ComponentContext context = getContext();
467 Method mth = pojo.getClass().getMethod("setContext",
468 new Class[] {ComponentContext.class});
469 if (mth != null) {
470 mth.invoke(pojo, new Object[] {context});
471 }
472 } catch (Exception e) {
473 logger
474 .debug("Unable to inject ComponentContext: "
475 + e.getMessage());
476 }
477
478 }
479
480 /**
481 * Specifies if the endpoint can process messages with binary data.
482 *
483 * @param mtomEnabled
484 * a <code>boolean</code>
485 * @org.apache.xbean.Property description="Specifies if the service can
486 * consume MTOM formatted binary data. The
487 * default is <code>false</code>."
488 */
489 public void setMtomEnabled(boolean mtomEnabled) {
490 this.mtomEnabled = mtomEnabled;
491 }
492
493 public boolean isMtomEnabled() {
494 return mtomEnabled;
495 }
496
497 public boolean isSchemaValidationEnabled() {
498 return schemaValidationEnabled;
499 }
500
501 public void setSchemaValidationEnabled(boolean schemaValidationEnabled) {
502 this.schemaValidationEnabled = schemaValidationEnabled;
503 }
504
505 /**
506 * Specifies if the endpoint expects messages that are encased in the JBI
507 * wrapper used for SOAP messages.
508 *
509 * @param mtomEnabled
510 * a <code>boolean</code>
511 * @org.apache.xbean.Property description="Specifies if the endpoint expects
512 * to receive the JBI wrapper in the message
513 * received from the NMR. The default is
514 * <code>true</code>."
515 */
516 public void setUseJBIWrapper(boolean useJBIWrapper) {
517 this.useJBIWrapper = useJBIWrapper;
518 }
519
520 public boolean isUseJBIWrapper() {
521 return useJBIWrapper;
522 }
523
524 /**
525 * Specifies if the endpoint expects soap messages when useJBIWrapper is
526 * false, if useJBIWrapper is true then ignore useSOAPEnvelope
527 *
528 * @org.apache.xbean.Property description="Specifies if the endpoint expects
529 * soap messages when useJBIWrapper is false, if
530 * useJBIWrapper is true then ignore
531 * useSOAPEnvelope. The default is
532 * <code>true</code>.
533 */
534 public void setUseSOAPEnvelope(boolean useSOAPEnvelope) {
535 this.useSOAPEnvelope = useSOAPEnvelope;
536 }
537
538 public boolean isUseSOAPEnvelope() {
539 return useSOAPEnvelope;
540 }
541
542 /**
543 * Specifies if the endpoint use aegis databinding to marshell/unmarshell message
544 *
545 * @org.apache.xbean.Property description="Specifies if the endpoint use aegis databinding to marshell/unmarshell message.
546 * The default is <code>false</code>.
547 */
548 public void setUseAegis(boolean useAegis) {
549 this.useAegis = useAegis;
550 }
551
552
553 public boolean isUseAegis() {
554 return useAegis;
555 }
556
557 }