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