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.camel.component.cxf;
018
019 import java.lang.reflect.Proxy;
020 import java.util.Collection;
021 import java.util.List;
022 import java.util.concurrent.atomic.AtomicBoolean;
023
024 import javax.xml.ws.WebServiceProvider;
025
026 import org.w3c.dom.Element;
027
028 import org.apache.camel.CamelContext;
029 import org.apache.camel.CamelException;
030 import org.apache.camel.Consumer;
031 import org.apache.camel.Processor;
032 import org.apache.camel.Producer;
033 import org.apache.camel.Service;
034 import org.apache.camel.component.cxf.feature.MessageDataFormatFeature;
035 import org.apache.camel.component.cxf.feature.PayLoadDataFormatFeature;
036 import org.apache.camel.component.cxf.util.CxfEndpointUtils;
037 import org.apache.camel.impl.DefaultEndpoint;
038 import org.apache.camel.spi.HeaderFilterStrategy;
039 import org.apache.camel.spi.HeaderFilterStrategyAware;
040 import org.apache.camel.spring.SpringCamelContext;
041 import org.apache.camel.util.ObjectHelper;
042 import org.apache.commons.logging.Log;
043 import org.apache.commons.logging.LogFactory;
044 import org.apache.cxf.Bus;
045 import org.apache.cxf.BusFactory;
046 import org.apache.cxf.common.classloader.ClassLoaderUtils;
047 import org.apache.cxf.common.util.ClassHelper;
048 import org.apache.cxf.endpoint.Client;
049 import org.apache.cxf.endpoint.ClientImpl;
050 import org.apache.cxf.endpoint.Endpoint;
051 import org.apache.cxf.feature.LoggingFeature;
052 import org.apache.cxf.frontend.ClientFactoryBean;
053 import org.apache.cxf.frontend.ClientProxy;
054 import org.apache.cxf.frontend.ClientProxyFactoryBean;
055 import org.apache.cxf.frontend.ServerFactoryBean;
056 import org.apache.cxf.headers.Header;
057 import org.apache.cxf.jaxws.JaxWsClientFactoryBean;
058 import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
059 import org.apache.cxf.jaxws.JaxWsServerFactoryBean;
060 import org.apache.cxf.message.Attachment;
061 import org.apache.cxf.message.Message;
062 import org.apache.cxf.message.MessageContentsList;
063 import org.apache.cxf.service.model.BindingOperationInfo;
064 import org.apache.cxf.service.model.MessagePartInfo;
065 import org.springframework.context.ApplicationContext;
066
067 /**
068 * Defines the <a href="http://camel.apache.org/cxf.html">CXF Endpoint</a>.
069 * It contains a list of properties for CXF endpoint including {@link DataFormat},
070 * {@link CxfBinding}, and {@link HeaderFilterStrategy}. The default DataFormat
071 * mode is {@link DataFormat#POJO}.
072 *
073 * @version $Revision: 20459 $
074 */
075 public class CxfEndpoint extends DefaultEndpoint implements HeaderFilterStrategyAware, Service {
076
077 private static final Log LOG = LogFactory.getLog(CxfEndpoint.class);
078
079 private String wsdlURL;
080 private String serviceClass;
081 private String portName;
082 private String serviceName;
083 private String defaultOperationName;
084 private String defaultOperationNamespace;
085 private DataFormat dataFormat = DataFormat.POJO;
086 // This is for invoking the CXFClient with wrapped parameters of unwrapped parameters
087 private boolean isWrapped;
088 // This is for marshal or unmarshal message with the document-literal wrapped or unwrapped style
089 private Boolean wrappedStyle;
090 private boolean inOut = true;
091 private Bus bus;
092 private CxfBinding cxfBinding;
093 private HeaderFilterStrategy headerFilterStrategy;
094 private AtomicBoolean getBusHasBeenCalled = new AtomicBoolean(false);
095 private boolean isSetDefaultBus;
096 private boolean loggingFeatureEnabled;
097
098 public CxfEndpoint(String remaining, CxfComponent cxfComponent) {
099 super(remaining, cxfComponent);
100 }
101
102 public CxfEndpoint(String remaining, CamelContext context) {
103 super(remaining, context);
104 }
105
106 public Producer createProducer() throws Exception {
107 return new CxfProducer(this);
108 }
109
110 public Consumer createConsumer(Processor processor) throws Exception {
111 return new CxfConsumer(this, processor);
112 }
113
114 public boolean isSingleton() {
115 return true;
116 }
117
118 /**
119 * Populate server factory bean
120 */
121 protected void setupServerFactoryBean(ServerFactoryBean sfb, Class<?> cls) {
122
123 // address
124 sfb.setAddress(getEndpointUri());
125
126 // service class
127 sfb.setServiceClass(cls);
128
129 // wsdl url
130 if (getWsdlURL() != null) {
131 sfb.setWsdlURL(getWsdlURL());
132 }
133
134 // service name qname
135 if (getServiceName() != null) {
136 sfb.setServiceName(CxfEndpointUtils.getQName(getServiceName()));
137 }
138
139 // port qname
140 if (getPortName() != null) {
141 sfb.setEndpointName(CxfEndpointUtils.getQName(getPortName()));
142 }
143
144 // apply feature here
145 if (!CxfEndpointUtils.hasAnnotation(cls, WebServiceProvider.class)) {
146 if (getDataFormat() == DataFormat.PAYLOAD) {
147 sfb.getFeatures().add(new PayLoadDataFormatFeature());
148 } else if (getDataFormat() == DataFormat.MESSAGE) {
149 sfb.getFeatures().add(new MessageDataFormatFeature());
150 }
151 } else {
152 if (LOG.isDebugEnabled()) {
153 LOG.debug("Ignore DataFormat mode " + getDataFormat()
154 + " since SEI class is annotated with WebServiceProvider");
155 }
156 }
157
158 if (loggingFeatureEnabled) {
159 sfb.getFeatures().add(new LoggingFeature());
160 }
161
162 if (getDataFormat() == DataFormat.PAYLOAD) {
163 sfb.setDataBinding(new HybridSourceDataBinding());
164 }
165
166 // set the document-literal wrapped style
167 if (getWrappedStyle() != null) {
168 sfb.getServiceFactory().setWrapped(getWrappedStyle());
169 }
170
171 sfb.setBus(getBus());
172 sfb.setStart(false);
173 }
174
175 /**
176 *
177 * Create a client factory bean object. Notice that the serviceClass <b>must</b> be
178 * an interface.
179 */
180 protected ClientProxyFactoryBean createClientFactoryBean(Class<?> cls) throws CamelException {
181 if (CxfEndpointUtils.hasWebServiceAnnotation(cls)) {
182 return new JaxWsProxyFactoryBean(new JaxWsClientFactoryBean() {
183 @Override
184 protected Client createClient(Endpoint ep) {
185 return new CamelCxfClientImpl(getBus(), ep);
186 }
187 });
188
189 } else {
190 return new ClientProxyFactoryBean(new ClientFactoryBean() {
191 @Override
192 protected Client createClient(Endpoint ep) {
193 return new CamelCxfClientImpl(getBus(), ep);
194 }
195 });
196 }
197 }
198
199 /**
200 *
201 * Create a client factory bean object without serviceClass interface.
202 */
203 protected ClientFactoryBean createClientFactoryBean() {
204 return new ClientFactoryBean(new WSDLServiceFactoryBean()) {
205
206 @Override
207 protected Client createClient(Endpoint ep) {
208 return new CamelCxfClientImpl(getBus(), ep);
209 }
210
211 @Override
212 protected void initializeAnnotationInterceptors(Endpoint ep, Class<?> cls) {
213 // Do nothing here
214 }
215
216 };
217 }
218
219 protected Bus doGetBus() {
220 BusFactory busFactory = BusFactory.newInstance();
221 // need to check if the camelContext is SpringCamelContext and
222 // update the bus configuration with the applicationContext
223 // which SpringCamelContext holds
224 if (getCamelContext() instanceof SpringCamelContext) {
225 SpringCamelContext springCamelContext = (SpringCamelContext)getCamelContext();
226 ApplicationContext applicationContext = springCamelContext.getApplicationContext();
227 busFactory = new org.apache.cxf.bus.spring.SpringBusFactory(applicationContext);
228 }
229 return busFactory.createBus();
230
231 }
232
233 /**
234 *
235 * Populate a client factory bean
236 */
237 protected void setupClientFactoryBean(ClientProxyFactoryBean factoryBean, Class<?> cls) {
238 // service class
239 factoryBean.setServiceClass(cls);
240
241 // address
242 factoryBean.setAddress(getEndpointUri());
243
244 // wsdl url
245 if (getWsdlURL() != null) {
246 factoryBean.setWsdlURL(getWsdlURL());
247 }
248
249 // service name qname
250 if (getServiceName() != null) {
251 factoryBean.setServiceName(CxfEndpointUtils.getQName(getServiceName()));
252 }
253
254 // port name qname
255 if (getPortName() != null) {
256 factoryBean.setEndpointName(CxfEndpointUtils.getQName(getPortName()));
257 }
258
259 // apply feature here
260 if (getDataFormat() == DataFormat.MESSAGE) {
261 factoryBean.getFeatures().add(new MessageDataFormatFeature());
262 } else if (getDataFormat() == DataFormat.PAYLOAD) {
263 factoryBean.getFeatures().add(new PayLoadDataFormatFeature());
264 factoryBean.setDataBinding(new HybridSourceDataBinding());
265 }
266
267 if (loggingFeatureEnabled) {
268 factoryBean.getFeatures().add(new LoggingFeature());
269 }
270
271 // set the document-literal wrapped style
272 if (getWrappedStyle() != null) {
273 factoryBean.getServiceFactory().setWrapped(getWrappedStyle());
274 }
275
276 factoryBean.setBus(getBus());
277
278 }
279
280 protected void setupClientFactoryBean(ClientFactoryBean factoryBean) {
281 // address
282 factoryBean.setAddress(getEndpointUri());
283
284 // wsdl url
285 if (getWsdlURL() != null) {
286 factoryBean.setWsdlURL(getWsdlURL());
287 }
288
289 // service name qname
290 if (getServiceName() != null) {
291 factoryBean.setServiceName(CxfEndpointUtils.getQName(getServiceName()));
292 }
293
294 // port name qname
295 if (getPortName() != null) {
296 factoryBean.setEndpointName(CxfEndpointUtils.getQName(getPortName()));
297 }
298
299 // apply feature here
300 if (getDataFormat() == DataFormat.MESSAGE) {
301 factoryBean.getFeatures().add(new MessageDataFormatFeature());
302 } else if (getDataFormat() == DataFormat.PAYLOAD) {
303 factoryBean.getFeatures().add(new PayLoadDataFormatFeature());
304 factoryBean.setDataBinding(new HybridSourceDataBinding());
305 }
306
307 if (loggingFeatureEnabled) {
308 factoryBean.getFeatures().add(new LoggingFeature());
309 }
310
311 // set the document-literal wrapped style
312 if (getWrappedStyle() != null) {
313 factoryBean.getServiceFactory().setWrapped(getWrappedStyle());
314 }
315
316 factoryBean.setBus(getBus());
317 }
318
319 // Package private methods
320 // -------------------------------------------------------------------------
321
322 /**
323 * Create a CXF client object
324 */
325 Client createClient() throws Exception {
326
327 // get service class
328 if (getDataFormat().equals(DataFormat.POJO)) {
329 ObjectHelper.notEmpty(getServiceClass(), CxfConstants.SERVICE_CLASS);
330 }
331
332 Class<?> cls = null;
333 if (getServiceClass() != null) {
334 cls = ClassLoaderUtils.loadClass(getServiceClass(), getClass());
335 // create client factory bean
336 ClientProxyFactoryBean factoryBean = createClientFactoryBean(cls);
337 // setup client factory bean
338 setupClientFactoryBean(factoryBean, cls);
339 return ((ClientProxy)Proxy.getInvocationHandler(factoryBean.create())).getClient();
340 } else {
341 checkName(portName, "endpoint/port name");
342 checkName(serviceName, "service name");
343 ClientFactoryBean factoryBean = createClientFactoryBean();
344 // setup client factory bean
345 setupClientFactoryBean(factoryBean);
346 return factoryBean.create();
347 }
348
349 }
350
351 void checkName(String value, String name) {
352 if (ObjectHelper.isEmpty(value)) {
353 LOG.warn("The " + name + "is empty, cxf will try to load the first one in wsdl for you");
354 }
355 }
356
357 /**
358 * Create a CXF server factory bean
359 */
360 ServerFactoryBean createServerFactoryBean() throws Exception {
361
362 Class<?> cls = null;
363 if (getDataFormat() == DataFormat.POJO || getServiceClass() != null) {
364 // get service class
365 ObjectHelper.notEmpty(getServiceClass(), CxfConstants.SERVICE_CLASS);
366 cls = ClassLoaderUtils.loadClass(getServiceClass(), getClass());
367 }
368
369 // create server factory bean
370 // Shouldn't use CxfEndpointUtils.getServerFactoryBean(cls) as it is for
371 // CxfSoapComponent
372 ServerFactoryBean answer = null;
373
374 if (cls == null) {
375 ObjectHelper.notNull(portName, "Please provide endpoint/port name");
376 ObjectHelper.notNull(serviceName, "Please provide service name");
377 answer = new ServerFactoryBean(new WSDLServiceFactoryBean());
378 } else if (CxfEndpointUtils.hasWebServiceAnnotation(cls)) {
379 answer = new JaxWsServerFactoryBean();
380 } else {
381 answer = new ServerFactoryBean();
382 }
383
384 // setup server factory bean
385 setupServerFactoryBean(answer, cls);
386 return answer;
387 }
388
389 // Properties
390 // -------------------------------------------------------------------------
391
392 public DataFormat getDataFormat() {
393 return dataFormat;
394 }
395
396 public void setDataFormat(DataFormat format) {
397 dataFormat = format;
398 }
399
400 public String getWsdlURL() {
401 return wsdlURL;
402 }
403
404 public void setWsdlURL(String url) {
405 wsdlURL = url;
406 }
407
408 public String getServiceClass() {
409 return serviceClass;
410 }
411
412 public void setServiceClass(String className) {
413 serviceClass = className;
414 }
415
416 public void setServiceClass(Object instance) {
417 serviceClass = ClassHelper.getRealClass(instance).getName();
418 }
419
420 public void setServiceName(String service) {
421 serviceName = service;
422 }
423
424 public String getServiceName() {
425 return serviceName;
426 }
427
428 public String getPortName() {
429 return portName;
430 }
431
432 public void setPortName(String port) {
433 portName = port;
434 }
435
436 public String getDefaultOperationName() {
437 return defaultOperationName;
438 }
439
440 public void setDefaultOperationName(String name) {
441 defaultOperationName = name;
442 }
443
444 public String getDefaultOperationNamespace() {
445 return defaultOperationNamespace;
446 }
447
448 public void setDefaultOperationNamespace(String namespace) {
449 defaultOperationNamespace = namespace;
450 }
451
452 public boolean isInOut() {
453 return inOut;
454 }
455
456 public void setInOut(boolean inOut) {
457 this.inOut = inOut;
458 }
459
460 public boolean isWrapped() {
461 return isWrapped;
462 }
463
464 public void setWrapped(boolean wrapped) {
465 isWrapped = wrapped;
466 }
467
468 public Boolean getWrappedStyle() {
469 return wrappedStyle;
470 }
471
472 public void setWrappedStyle(Boolean wrapped) {
473 wrappedStyle = wrapped;
474 }
475
476 public void setCxfBinding(CxfBinding cxfBinding) {
477 this.cxfBinding = cxfBinding;
478 }
479
480 public CxfBinding getCxfBinding() {
481 return cxfBinding;
482 }
483
484 public void setHeaderFilterStrategy(HeaderFilterStrategy headerFilterStrategy) {
485 this.headerFilterStrategy = headerFilterStrategy;
486 if (cxfBinding instanceof HeaderFilterStrategyAware) {
487 ((HeaderFilterStrategyAware)cxfBinding)
488 .setHeaderFilterStrategy(headerFilterStrategy);
489 }
490 }
491
492 public HeaderFilterStrategy getHeaderFilterStrategy() {
493 return headerFilterStrategy;
494 }
495
496 public void setBus(Bus bus) {
497 this.bus = bus;
498 }
499
500 public Bus getBus() {
501 if (bus == null) {
502 bus = doGetBus();
503 if (LOG.isDebugEnabled()) {
504 LOG.debug("Using DefaultBus " + bus);
505 }
506 }
507
508 if (!getBusHasBeenCalled.getAndSet(true) && isSetDefaultBus) {
509 BusFactory.setDefaultBus(bus);
510 if (LOG.isDebugEnabled()) {
511 LOG.debug("Set bus " + bus + " as thread default bus");
512 }
513 }
514 return bus;
515 }
516
517 public void setSetDefaultBus(boolean isSetDefaultBus) {
518 this.isSetDefaultBus = isSetDefaultBus;
519 }
520
521 public boolean isSetDefaultBus() {
522 return isSetDefaultBus;
523 }
524
525 public void setLoggingFeatureEnabled(boolean loggingFeatureEnabled) {
526 this.loggingFeatureEnabled = loggingFeatureEnabled;
527 }
528
529 public boolean isLoggingFeatureEnabled() {
530 return loggingFeatureEnabled;
531 }
532
533 public void start() throws Exception {
534 if (headerFilterStrategy == null) {
535 headerFilterStrategy = new CxfHeaderFilterStrategy();
536 }
537 if (cxfBinding == null) {
538 cxfBinding = new DefaultCxfBinding();
539 }
540 if (cxfBinding instanceof HeaderFilterStrategyAware) {
541 ((HeaderFilterStrategyAware)cxfBinding).setHeaderFilterStrategy(getHeaderFilterStrategy());
542 }
543 }
544
545 public void stop() throws Exception {
546 // noop
547 }
548
549 /**
550 * We need to override the {@link ClientImpl#setParameters} method
551 * to insert parameters into CXF Message for {@link DataFormat#PAYLOAD}
552 * mode.
553 */
554 class CamelCxfClientImpl extends ClientImpl {
555
556 public CamelCxfClientImpl(Bus bus, Endpoint ep) {
557 super(bus, ep);
558 }
559
560 public Bus getBus() {
561 return bus;
562 }
563
564 @SuppressWarnings("unchecked")
565 @Override
566 protected void setParameters(Object[] params, Message message) {
567
568 Object attachements = message.get(CxfConstants.CAMEL_CXF_ATTACHMENTS);
569 if (attachements != null) {
570 message.setAttachments((Collection<Attachment>)attachements);
571 message.remove(CxfConstants.CAMEL_CXF_ATTACHMENTS);
572 }
573
574 if (DataFormat.PAYLOAD == message.get(DataFormat.class)) {
575
576 CxfPayload<?> payload = (CxfPayload<?>)params[0];
577 List<Element> elements = payload.getBody();
578
579 BindingOperationInfo boi = message.get(BindingOperationInfo.class);
580 MessageContentsList content = new MessageContentsList();
581 int i = 0;
582
583 for (MessagePartInfo partInfo : boi.getOperationInfo().getInput().getMessageParts()) {
584 if (elements.size() > i && partInfo.getConcreteName().getLocalPart()
585 .equals(elements.get(i).getLocalName())) {
586 content.put(partInfo, elements.get(i++));
587 }
588 }
589
590 message.setContent(List.class, content);
591 message.put(Header.HEADER_LIST, payload.getHeaders());
592 } else {
593 super.setParameters(params, message);
594 }
595
596 message.remove(DataFormat.class);
597 }
598 }
599 }