View Javadoc

1   /*
2    * $Id: CxfMessageReceiver.java 12283 2008-07-10 18:16:53Z dandiep $
3    * --------------------------------------------------------------------------------------
4    * Copyright (c) MuleSource, Inc.  All rights reserved.  http://www.mulesource.com
5    *
6    * The software in this package is published under the terms of the CPAL v1.0
7    * license, a copy of which has been included with this distribution in the
8    * LICENSE.txt file.
9    */
10  
11  package org.mule.transport.cxf;
12  
13  import org.mule.api.MuleException;
14  import org.mule.api.component.Component;
15  import org.mule.api.component.JavaComponent;
16  import org.mule.api.endpoint.InboundEndpoint;
17  import org.mule.api.lifecycle.Callable;
18  import org.mule.api.lifecycle.CreateException;
19  import org.mule.api.lifecycle.Disposable;
20  import org.mule.api.lifecycle.Initialisable;
21  import org.mule.api.lifecycle.InitialisationException;
22  import org.mule.api.service.Service;
23  import org.mule.api.service.ServiceAware;
24  import org.mule.api.transport.Connector;
25  import org.mule.transport.AbstractMessageReceiver;
26  import org.mule.transport.cxf.i18n.CxfMessages;
27  import org.mule.transport.cxf.support.MuleHeadersInInterceptor;
28  import org.mule.transport.cxf.support.MuleProtocolHeadersOutInterceptor;
29  import org.mule.transport.cxf.support.OutputPayloadInterceptor;
30  import org.mule.transport.cxf.support.ProxyService;
31  import org.mule.util.ClassUtils;
32  import org.mule.util.StringUtils;
33  
34  import java.util.ArrayList;
35  import java.util.HashMap;
36  import java.util.List;
37  import java.util.Map;
38  
39  import javax.xml.namespace.QName;
40  
41  import org.apache.commons.lang.BooleanUtils;
42  import org.apache.cxf.Bus;
43  import org.apache.cxf.aegis.databinding.AegisDatabinding;
44  import org.apache.cxf.configuration.Configurer;
45  import org.apache.cxf.databinding.DataBinding;
46  import org.apache.cxf.databinding.stax.StaxDataBinding;
47  import org.apache.cxf.databinding.stax.StaxDataBindingFeature;
48  import org.apache.cxf.endpoint.Server;
49  import org.apache.cxf.feature.AbstractFeature;
50  import org.apache.cxf.frontend.ServerFactoryBean;
51  import org.apache.cxf.interceptor.Interceptor;
52  import org.apache.cxf.jaxws.JaxWsServerFactoryBean;
53  import org.apache.cxf.service.factory.AbstractServiceConfiguration;
54  import org.apache.cxf.service.factory.ReflectionServiceFactoryBean;
55  
56  /**
57   * Create a CXF service. All messages for the service will be sent to the Mule bus a
58   * la the MuleInvoker.
59   */
60  public class CxfMessageReceiver extends AbstractMessageReceiver
61  {
62  
63      protected CxfConnector connector;
64      private Server server;
65      private boolean proxy;
66      private boolean applySecurityToProtocol;
67      private boolean applyTransformersToProtocol;
68      private boolean applyFiltersToProtocol;
69      
70      public CxfMessageReceiver(Connector connector, Service service, InboundEndpoint Endpoint)
71          throws CreateException
72      {
73          super(connector, service, Endpoint);
74          this.connector = (CxfConnector) connector;
75      }
76  
77      @SuppressWarnings("unchecked")
78      @Override
79      protected void doInitialise() throws InitialisationException
80      {
81          try
82          {
83              Map endpointProps = getEndpoint().getProperties();
84              String wsdlUrl = (String) endpointProps.get(CxfConstants.WSDL_LOCATION);
85              String bindingId = (String) endpointProps.get(CxfConstants.BINDING_ID);
86              String frontend = (String) endpointProps.get(CxfConstants.FRONTEND);
87              String serviceClassName = (String) endpointProps.get(CxfConstants.SERVICE_CLASS);
88              String mtomEnabled = (String) endpointProps.get(CxfConstants.MTOM_ENABLED);
89              List<DataBinding> databinding = (List<DataBinding>) endpointProps.get(CxfConstants.DATA_BINDING);
90              List<AbstractFeature> features = (List<AbstractFeature>) endpointProps.get(CxfConstants.FEATURES);
91              String proxyStr = (String) endpointProps.get(CxfConstants.PROXY);
92  
93              applyFiltersToProtocol = BooleanUtils.toBoolean((String) endpointProps.get(CxfConstants.APPLY_FILTERS_TO_PROTOCOL));
94              applySecurityToProtocol = BooleanUtils.toBoolean((String) endpointProps.get(CxfConstants.APPLY_SECURITY_TO_PROTOCOL));
95              applyTransformersToProtocol = BooleanUtils.toBoolean((String) endpointProps.get(CxfConstants.APPLY_TRANSFORMERS_TO_PROTOCOL));
96              
97              Class<?> svcCls = null;
98              Class<?> targetCls;
99              
100             proxy = BooleanUtils.toBoolean(proxyStr);
101             
102             if (proxy)
103             {
104                 svcCls = ProxyService.class;
105                 targetCls = svcCls;
106                 frontend = "simple";
107             }
108             else 
109             {
110                 if (StringUtils.isEmpty(frontend))
111                 {
112                     frontend = connector.getDefaultFrontend();
113                 }
114                 
115                 if (!StringUtils.isEmpty(serviceClassName)) 
116                 {
117                     svcCls = ClassUtils.loadClass(serviceClassName, getClass());
118                 } 
119                 
120                 targetCls = getTargetClass(svcCls);
121                 
122                 if (svcCls == null)
123                 {
124                     svcCls = targetCls;
125                 }
126             }
127 
128             ServerFactoryBean sfb = null;
129             if (CxfConstants.SIMPLE_FRONTEND.equals(frontend))
130             {
131                 sfb = new ServerFactoryBean();
132                 sfb.setDataBinding(new AegisDatabinding());
133             }
134             else if (CxfConstants.JAX_WS_FRONTEND.equals(frontend))
135             {
136                 sfb = new JaxWsServerFactoryBean();
137             }
138             else
139             {
140                 throw new CreateException(CxfMessages.invalidFrontend(frontend), this);
141             }
142 
143             if (!proxy)
144             {
145                 if (databinding != null && databinding.size() > 0)
146                 {
147                     // TODO: find a way to make this not a list
148                     sfb.setDataBinding(databinding.get(0));
149                 }
150                 
151                 if (service.getComponent() instanceof JavaComponent)
152                 {
153                     sfb.setServiceBean(((JavaComponent) service.getComponent()).getObjectFactory().getInstance());
154                 }
155             }
156             else
157             {
158                 sfb.setDataBinding(new StaxDataBinding());
159                 sfb.getFeatures().add(new StaxDataBindingFeature());
160             }
161            
162             // The binding - i.e. SOAP, XML, HTTP Binding, etc
163             if (bindingId != null)
164             {
165                 sfb.setBindingId(bindingId);
166             }
167             
168             if (features != null) 
169             {
170                 sfb.getFeatures().addAll(features);
171             }
172             
173             if (mtomEnabled != null)
174             {
175                 Map<String, Object> properties = sfb.getProperties();
176                 if (properties == null)
177                 {
178                     properties = new HashMap<String, Object>();
179                     sfb.setProperties(properties);
180                 }
181                 properties.put("mtom-enabled", mtomEnabled);
182             }
183             
184             sfb.setInInterceptors((List<Interceptor>) endpointProps.get("inInterceptors"));
185             sfb.setInFaultInterceptors((List<Interceptor>) endpointProps.get("inFaultInterceptors"));
186             sfb.setOutInterceptors((List<Interceptor>) endpointProps.get("outInterceptors"));
187             sfb.setOutFaultInterceptors((List<Interceptor>) endpointProps.get("outFaultInterceptors"));
188 
189             if (sfb.getInInterceptors() == null)
190             {
191                 sfb.setInInterceptors(new ArrayList<Interceptor>());
192             }
193             
194             sfb.getInInterceptors().add(new MuleHeadersInInterceptor());
195             
196             if (sfb.getOutInterceptors() == null)
197             {
198                 sfb.setOutInterceptors(new ArrayList<Interceptor>());
199             }
200             sfb.getOutInterceptors().add(new MuleProtocolHeadersOutInterceptor());
201             
202             if (sfb.getOutFaultInterceptors() == null)
203             {
204                 sfb.setOutFaultInterceptors(new ArrayList<Interceptor>());
205             }
206             sfb.getOutFaultInterceptors().add(new MuleProtocolHeadersOutInterceptor());
207             
208             if (proxy)
209             {
210                 sfb.getOutInterceptors().add(new OutputPayloadInterceptor());
211             }
212             
213             sfb.setServiceClass(svcCls);
214             sfb.setAddress(getAddressWithoutQuery());
215 
216             if (wsdlUrl != null)
217             {
218                 sfb.setWsdlURL(wsdlUrl);
219             }
220 
221             ReflectionServiceFactoryBean svcFac = sfb.getServiceFactory();
222 
223             addIgnoredMethods(svcFac, Callable.class.getName());
224             addIgnoredMethods(svcFac, Initialisable.class.getName());
225             addIgnoredMethods(svcFac, Disposable.class.getName());
226             addIgnoredMethods(svcFac, ServiceAware.class.getName());
227 
228             String name = (String) endpointProps.get(CxfConstants.NAME);
229             // check if there is the namespace property on the service
230             String namespace = (String) endpointProps.get(CxfConstants.NAMESPACE);
231 
232             // HACK because CXF expects a QName for the service
233             initServiceName(svcCls, name, namespace, svcFac);
234 
235             boolean sync = endpoint.isSynchronous();
236             // default to synchronous if using http
237             if (endpoint.getEndpointURI().getScheme().startsWith("http")
238                 || endpoint.getEndpointURI().getScheme().startsWith("servlet"))
239             {
240                 sync = true;
241             }
242 
243             sfb.setInvoker(new MuleInvoker(this, targetCls, sync));
244             sfb.setStart(false);
245 
246             Bus bus = connector.getCxfBus();
247             sfb.setBus(bus);
248 
249             initializeServerFactory(sfb);
250             
251             Configurer configurer = bus.getExtension(Configurer.class);
252             if (null != configurer)
253             {
254                 configurer.configureBean(sfb.getServiceFactory().getEndpointName().toString(), sfb);
255             }
256 
257             server = sfb.create();
258         }
259         catch (MuleException e)
260         {
261             throw new InitialisationException(e, this);
262         }
263         catch (ClassNotFoundException e)
264         {
265             // will be thrown in the case that the ClassUtils.loadClass() does
266             // not find the class to load
267             throw new InitialisationException(e, this);
268         }
269         catch (Exception e)
270         {
271             throw new InitialisationException(e, this);
272         }
273     }
274 
275     /**
276      * If any custom initialization logic needs to be done, it can
277      * be done by overriding this method.
278      * @param sfb
279      */
280     protected void initializeServerFactory(ServerFactoryBean sfb)
281     {
282     }
283 
284     private String getAddressWithoutQuery()
285     {
286         String a = getEndpointURI().getAddress();
287         int idx = a.lastIndexOf('?');
288         if (idx > -1) {
289             a = a.substring(0, idx);
290         }
291         return a;
292     }
293 
294     /**
295      * Gross hack to support getting the service namespace from CXF if one wasn't
296      * supplied.
297      */
298     private void initServiceName(Class<?> exposedInterface,
299                                  String name,
300                                  String namespace,
301                                  ReflectionServiceFactoryBean svcFac)
302     {
303         svcFac.setServiceClass(exposedInterface);
304         for (AbstractServiceConfiguration c : svcFac.getServiceConfigurations())
305         {
306             c.setServiceFactory(svcFac);
307         }
308 
309         if (name != null && namespace == null)
310         {
311             namespace = svcFac.getServiceQName().getNamespaceURI();
312         }
313         else if (name == null && namespace != null)
314         {
315             name = svcFac.getServiceQName().getLocalPart();
316         }
317 
318         if (name != null)
319         {
320             svcFac.setServiceName(new QName(namespace, name));
321         }
322     }
323 
324     public void addIgnoredMethods(ReflectionServiceFactoryBean svcFac, String className)
325     {
326         try
327         {
328             Class<?> c = ClassUtils.loadClass(className, getClass());
329             for (int i = 0; i < c.getMethods().length; i++)
330             {
331                 svcFac.getIgnoredMethods().add(c.getMethods()[i]);
332             }
333         }
334         catch (ClassNotFoundException e)
335         {
336             // can be ignored.
337         }
338     }
339 
340     private Class<?> getTargetClass(Class<?> svcCls) throws MuleException, ClassNotFoundException
341     {
342         Component component = service.getComponent();
343         if (!(component instanceof JavaComponent)) 
344         {
345             if (svcCls == null)
346             {
347                 throw new InitialisationException(CxfMessages.serviceClassRequiredWithPassThrough(), this);
348             }
349             else
350             {
351                 return svcCls;
352             }
353         }
354         
355         try
356         {
357             return ((JavaComponent) component).getObjectType();
358         }
359         catch (Exception e)
360         {
361             throw new CreateException(e, this);
362         }
363     }
364 
365     protected void doDispose()
366     {
367         // template method
368     }
369 
370     public void doConnect() throws Exception
371     {
372         // Start the CXF Server
373         server.start();
374         connector.registerReceiverWithMuleService(this, endpoint.getEndpointURI());
375     }
376 
377     public void doDisconnect() throws Exception
378     {
379         server.stop();
380     }
381 
382     public void doStart() throws MuleException
383     {
384         // nothing to do
385     }
386 
387     public void doStop() throws MuleException
388     {
389         // nothing to do
390     }
391 
392     public Server getServer()
393     {
394         return server;
395     }
396 
397     public boolean isProxy()
398     {
399         return proxy;
400     }
401 
402     public boolean isApplySecurityToProtocol()
403     {
404         return applySecurityToProtocol;
405     }
406 
407     public boolean isApplyTransformersToProtocol()
408     {
409         return applyTransformersToProtocol;
410     }
411 
412     public boolean isApplyFiltersToProtocol()
413     {
414         return applyFiltersToProtocol;
415     }
416 
417 }