View Javadoc

1   /*
2    * $Id: ClientWrapper.java 12248 2008-07-07 22:08:28Z 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.MuleEvent;
14  import org.mule.api.config.MuleProperties;
15  import org.mule.api.endpoint.EndpointURI;
16  import org.mule.api.endpoint.ImmutableEndpoint;
17  import org.mule.api.lifecycle.CreateException;
18  import org.mule.api.transport.DispatchException;
19  import org.mule.transport.cxf.i18n.CxfMessages;
20  import org.mule.transport.cxf.support.MuleHeadersInInterceptor;
21  import org.mule.transport.cxf.support.MuleHeadersOutInterceptor;
22  import org.mule.transport.cxf.support.MuleProtocolHeadersOutInterceptor;
23  import org.mule.transport.cxf.support.OutputPayloadInterceptor;
24  import org.mule.transport.cxf.support.ProxyService;
25  import org.mule.transport.soap.i18n.SoapMessages;
26  
27  import java.lang.reflect.Constructor;
28  import java.lang.reflect.Method;
29  import java.net.URL;
30  import java.util.HashMap;
31  import java.util.List;
32  
33  import javax.xml.namespace.QName;
34  import javax.xml.ws.BindingProvider;
35  import javax.xml.ws.Service;
36  import javax.xml.ws.WebEndpoint;
37  import javax.xml.ws.WebServiceClient;
38  
39  import org.apache.commons.lang.BooleanUtils;
40  import org.apache.cxf.Bus;
41  import org.apache.cxf.binding.Binding;
42  import org.apache.cxf.common.classloader.ClassLoaderUtils;
43  import org.apache.cxf.databinding.stax.StaxDataBinding;
44  import org.apache.cxf.databinding.stax.StaxDataBindingFeature;
45  import org.apache.cxf.endpoint.Client;
46  import org.apache.cxf.endpoint.ClientImpl;
47  import org.apache.cxf.endpoint.Endpoint;
48  import org.apache.cxf.endpoint.EndpointImpl;
49  import org.apache.cxf.feature.AbstractFeature;
50  import org.apache.cxf.frontend.ClientProxy;
51  import org.apache.cxf.frontend.ClientProxyFactoryBean;
52  import org.apache.cxf.frontend.MethodDispatcher;
53  import org.apache.cxf.interceptor.Interceptor;
54  import org.apache.cxf.interceptor.WrappedOutInterceptor;
55  import org.apache.cxf.message.Message;
56  import org.apache.cxf.phase.PhaseInterceptor;
57  import org.apache.cxf.resource.ResourceManager;
58  import org.apache.cxf.resource.URIResolver;
59  import org.apache.cxf.service.model.BindingOperationInfo;
60  import org.apache.cxf.service.model.EndpointInfo;
61  import org.apache.cxf.transport.ChainInitiationObserver;
62  import org.apache.cxf.transport.Destination;
63  import org.apache.cxf.transport.DestinationFactory;
64  import org.apache.cxf.transport.DestinationFactoryManager;
65  import org.apache.cxf.transport.MessageObserver;
66  
67  public class ClientWrapper
68  {
69  
70      protected ImmutableEndpoint endpoint;
71      protected Bus bus;
72      protected Client client;
73      protected String defaultMethodName;
74  
75      // If we have a proxy we're going to invoke it directly
76      // Since the JAX-WS proxy does extra special things for us.
77      protected BindingProvider clientProxy;
78      protected Method defaultMethod;
79  
80      protected boolean proxy;
81      
82      public Client getClient()
83      {
84          return client;
85      }
86  
87      public BindingProvider getClientProxy()
88      {
89          return clientProxy;
90      }
91  
92      @SuppressWarnings("unchecked")
93      public void initialize() throws Exception
94      {
95          String clientClass = (String) endpoint.getProperty(CxfConstants.CLIENT_CLASS);
96          proxy = BooleanUtils.toBoolean((String) endpoint.getProperty(CxfConstants.PROXY));
97          
98          if (clientClass != null)
99          {
100             createClientFromClass(bus, clientClass);
101         }
102         else if (proxy)
103         {
104             createClientProxy(bus);
105         }
106         else
107         {
108             createClientFromLocalServer(bus);
109         }
110         
111         addInterceptors(client.getInInterceptors(), (List<Interceptor>) endpoint.getProperty(CxfConstants.IN_INTERCEPTORS));
112         addInterceptors(client.getInFaultInterceptors(), (List<Interceptor>) endpoint.getProperty(CxfConstants.IN_FAULT_INTERCEPTORS));
113         addInterceptors(client.getOutInterceptors(), (List<Interceptor>) endpoint.getProperty(CxfConstants.OUT_INTERCEPTORS));
114         addInterceptors(client.getOutFaultInterceptors(), (List<Interceptor>) endpoint.getProperty(CxfConstants.OUT_FAULT_INTERCEPTORS));
115 
116         if (proxy)
117         {
118             client.getOutInterceptors().add(new OutputPayloadInterceptor());
119         }
120         
121         List<AbstractFeature> features = (List<AbstractFeature>) endpoint.getProperty(CxfConstants.OUT_FAULT_INTERCEPTORS);
122         
123         if (features != null)
124         {
125             for (AbstractFeature f : features)
126             {
127                 f.initialize(client, bus);
128             }
129         }
130 
131         EndpointImpl ep = (EndpointImpl) client.getEndpoint();
132         
133         Object mtomEnabled = endpoint.getProperty(CxfConstants.MTOM_ENABLED);
134         if (mtomEnabled != null) 
135         {
136             HashMap<String, Object> props = new HashMap<String, Object>();
137             props.put(Message.MTOM_ENABLED, mtomEnabled);
138             ep.setProperties(props);
139         }
140         
141         addMuleInterceptors();
142     }
143 
144     @SuppressWarnings("unchecked")
145     private void addInterceptors(List<Interceptor> col, List<Interceptor> supplied)
146     {
147         if (supplied != null) 
148         {
149             col.addAll(supplied);
150         }
151     }
152 
153     protected Method findMethod(Class<?> clientCls) throws Exception
154     {
155         if (defaultMethod == null)
156         {
157             String op = (String) endpoint.getProperties().get(CxfConstants.OPERATION);
158             if (op == null)
159             {
160                 op = (String) endpoint.getProperties().get(CxfConstants.OPERATION);
161             }
162 
163             if (op != null)
164             {
165                 return getMethodFromOperation(op);
166             }
167         }
168 
169         return null;
170     }
171 
172     protected BindingOperationInfo getOperation(String opName) throws Exception
173     {
174         // Normally its not this hard to invoke the CXF Client, but we're
175         // sending along some exchange properties, so we need to use a more advanced
176         // method
177         Endpoint ep = client.getEndpoint();
178         QName q = new QName(ep.getService().getName().getNamespaceURI(), opName);
179         BindingOperationInfo bop = ep.getBinding().getBindingInfo().getOperation(q);
180         if (bop == null)
181         {
182             throw new Exception("No such operation: " + defaultMethod);
183         }
184 
185         if (bop.isUnwrappedCapable())
186         {
187             bop = bop.getUnwrappedOperation();
188         }
189         return bop;
190     }
191 
192     private Method getMethodFromOperation(String op) throws Exception
193     {
194         BindingOperationInfo bop = getOperation(op);
195         MethodDispatcher md = (MethodDispatcher) client.getEndpoint().getService().get(
196             MethodDispatcher.class.getName());
197         return md.getMethod(bop);
198     }
199 
200     protected void createClientProxy(Bus bus) throws Exception
201     {
202         // TODO: Specify WSDL
203         String wsdlLocation = (String) endpoint.getProperty(CxfConstants.WSDL_LOCATION);
204         
205         ClientProxyFactoryBean cpf = new ClientProxyFactoryBean();
206         cpf.setServiceClass(ProxyService.class);
207         cpf.setDataBinding(new StaxDataBinding());
208         cpf.getFeatures().add(new StaxDataBindingFeature());
209         cpf.setAddress(endpoint.getEndpointURI().getAddress());
210 
211         if (wsdlLocation != null) 
212         {
213             cpf.setWsdlLocation(wsdlLocation);
214         }
215         
216         this.client = ClientProxy.getClient(cpf.create());
217         
218         Binding binding = this.client.getEndpoint().getBinding();
219         
220         removeInterceptor(binding.getOutInterceptors(), WrappedOutInterceptor.class.getName());
221         
222         proxy = true;
223     }
224 
225     @SuppressWarnings("unchecked")
226     private void removeInterceptor(List<Interceptor> inInterceptors, String name) {
227 
228         for (Interceptor<?> i : inInterceptors) {
229             if (i instanceof PhaseInterceptor) {
230                 PhaseInterceptor<Message> p = (PhaseInterceptor<Message>)i;
231 
232                 if (p.getId().equals(name)) {
233                     inInterceptors.remove(p);
234                     return;
235                 }
236             }
237         }
238     }
239 
240     protected void createClientFromClass(Bus bus, String clientClassName) throws Exception
241     {
242         // TODO: Specify WSDL
243         String wsdlLocation = (String) endpoint.getProperty(CxfConstants.WSDL_LOCATION);
244         Class<?> clientCls = ClassLoaderUtils.loadClass(clientClassName, getClass());
245 
246         Service s = null;
247         if (wsdlLocation != null)
248         {
249             Constructor<?> cons = clientCls.getConstructor(URL.class, QName.class);
250             ResourceManager rr = bus.getExtension(ResourceManager.class);
251             URL url = rr.resolveResource(wsdlLocation, URL.class);
252 
253             if (url == null)
254             {
255                 URIResolver res = new URIResolver(wsdlLocation);
256 
257                 if (!res.isResolved())
258                 {
259                     throw new CreateException(CxfMessages.wsdlNotFound(wsdlLocation), this);
260                 }
261                 url = res.getURL();
262             }
263 
264             WebServiceClient clientAnn = clientCls.getAnnotation(WebServiceClient.class);
265             QName svcName = new QName(clientAnn.targetNamespace(), clientAnn.name());
266 
267             s = (Service) cons.newInstance(url, svcName);
268         }
269         else
270         {
271             s = (Service) clientCls.newInstance();
272         }
273         String port = (String) endpoint.getProperty(CxfConstants.CLIENT_PORT);
274 
275         if (port == null)
276         {
277             throw new CreateException(CxfMessages.mustSpecifyPort(), this);
278         }
279 
280         clientProxy = null;
281         if (port != null)
282         {
283             for (Method m : clientCls.getMethods())
284             {
285                 WebEndpoint we = m.getAnnotation(WebEndpoint.class);
286 
287                 if (we != null && we.name().equals(port))
288                 {
289                     clientProxy = (BindingProvider) m.invoke(s, new Object[0]);
290                     break;
291                 }
292             }
293         }
294 
295         if (clientProxy == null)
296         {
297             throw new CreateException(CxfMessages.portNotFound(port), this);
298         }
299 
300         EndpointURI uri = endpoint.getEndpointURI();
301         if (uri.getUser() != null)
302         {
303             clientProxy.getRequestContext().put(BindingProvider.USERNAME_PROPERTY, uri.getUser());
304         }
305 
306         if (uri.getPassword() != null)
307         {
308             clientProxy.getRequestContext().put(BindingProvider.PASSWORD_PROPERTY, uri.getPassword());
309         }
310 
311         clientProxy.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, uri.getAddress());
312 
313         client = ClientProxy.getClient(clientProxy);
314 
315         defaultMethod = findMethod(clientCls);
316         defaultMethodName = getDefaultMethodName();
317     }
318 
319     private String getDefaultMethodName()
320     {
321         EndpointURI endpointUri = endpoint.getEndpointURI();
322         String m = (String) endpointUri.getParams().get(MuleProperties.MULE_METHOD_PROPERTY);
323 
324         if (m == null)
325         {
326             m = (String) endpoint.getProperties().get(MuleProperties.MULE_METHOD_PROPERTY);
327         }
328 
329         return m;
330     }
331 
332     protected void createClientFromLocalServer(final Bus bus) throws Exception
333     {
334         String uri = endpoint.getEndpointURI().toString();
335         int idx = uri.indexOf('?');
336         if (idx != -1)
337         {
338             uri = uri.substring(0, idx);
339         }
340 
341         EndpointInfo ei = new EndpointInfo();
342         ei.setAddress(uri);
343 
344         DestinationFactoryManager dfm = bus.getExtension(DestinationFactoryManager.class);
345         DestinationFactory df = dfm.getDestinationFactoryForUri(uri);
346         if (df == null)
347         {
348             throw new Exception("Could not find a destination factory for uri " + uri);
349         }
350 
351         Destination dest = df.getDestination(ei);
352         MessageObserver mo = dest.getMessageObserver();
353         if (mo instanceof ChainInitiationObserver)
354         {
355             ChainInitiationObserver cMo = (ChainInitiationObserver) mo;
356             Endpoint cxfEP = cMo.getEndpoint();
357 
358             client = new ClientImpl(bus, cxfEP);
359         }
360         else
361         {
362             throw new Exception("Could not create client! No Server was found directly on the endpoint: "
363                                 + uri);
364         }
365     }
366 
367     protected void addMuleInterceptors()
368     {
369         client.getInInterceptors().add(new MuleHeadersInInterceptor());
370         client.getInFaultInterceptors().add(new MuleHeadersInInterceptor());
371         client.getOutInterceptors().add(new MuleHeadersOutInterceptor());
372         client.getOutFaultInterceptors().add(new MuleHeadersOutInterceptor());
373         client.getOutInterceptors().add(new MuleProtocolHeadersOutInterceptor());
374         client.getOutFaultInterceptors().add(new MuleProtocolHeadersOutInterceptor());
375     }
376 
377     protected String getMethodOrOperationName(MuleEvent event) throws DispatchException
378     {
379         // @TODO: Which of these *really* matter?
380         String method = (String) event.getMessage().getProperty(MuleProperties.MULE_METHOD_PROPERTY);
381 
382         if (method == null)
383         {
384             method = (String) event.getMessage().getProperty(CxfConstants.OPERATION);
385         }
386 
387         if (method == null)
388         {
389             method = defaultMethodName;
390         }
391         
392         if (method == null && proxy)
393         {
394             return "invoke";
395         }
396 
397         if (method == null)
398         {
399             throw new DispatchException(SoapMessages.cannotInvokeCallWithoutOperation(), event.getMessage(),
400                 event.getEndpoint());
401         }
402 
403         return method;
404     }
405 
406     public void setEndpoint(ImmutableEndpoint endpoint)
407     {
408         this.endpoint = endpoint;
409     }
410 
411     public void setBus(Bus bus)
412     {
413         this.bus = bus;
414     }
415 
416     public boolean isClientProxyAvailable()
417     {
418         return clientProxy != null;
419     }
420 
421     public BindingOperationInfo getOperation(MuleEvent event) throws Exception
422     {
423         String opName = getMethodOrOperationName(event);
424 
425         if (opName == null)
426         {
427             opName = defaultMethodName;
428         }
429 
430         return getOperation(opName);
431     }
432 
433     public Method getMethod(MuleEvent event) throws Exception
434     {
435         Method method = defaultMethod;
436         if (method == null)
437         {
438             String opName = (String) event.getMessage().getProperty(CxfConstants.OPERATION);
439             if (opName != null) 
440             {
441                 method = getMethodFromOperation(opName);
442             }
443 
444             if (method == null)
445             {
446                 opName = (String) endpoint.getProperty(CxfConstants.OPERATION);
447                 if (opName != null) 
448                 {
449                     method = getMethodFromOperation(opName);
450                 }
451             }
452             
453             if (method == null)
454             {
455                 opName = defaultMethodName;
456                 if (opName != null) 
457                 {
458                     method = getMethodFromOperation(opName);
459                 }
460             }
461         }
462 
463         if (method == null)
464         {
465             throw new DispatchException(CxfMessages.noOperationWasFoundOrSpecified(), event.getMessage(),
466                 endpoint);
467         }
468         return method;
469     }
470 }