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