View Javadoc
1   /*
2    * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
3    * The software in this package is published under the terms of the CPAL v1.0
4    * license, a copy of which has been included with this distribution in the
5    * LICENSE.txt file.
6    */
7   package org.mule.transport.soap.axis;
8   
9   import org.mule.RequestContext;
10  import org.mule.api.ExceptionPayload;
11  import org.mule.api.MuleEvent;
12  import org.mule.api.MuleException;
13  import org.mule.api.MuleMessage;
14  import org.mule.api.component.JavaComponent;
15  import org.mule.api.config.MuleProperties;
16  import org.mule.api.lifecycle.Callable;
17  import org.mule.api.lifecycle.Disposable;
18  import org.mule.api.lifecycle.Initialisable;
19  import org.mule.api.service.Service;
20  import org.mule.api.transport.PropertyScope;
21  import org.mule.config.ExceptionHelper;
22  import org.mule.module.cxf.SoapConstants;
23  import org.mule.transport.AbstractMessageReceiver;
24  import org.mule.transport.soap.axis.extras.AxisCleanAndAddProperties;
25  import org.mule.util.ClassUtils;
26  
27  import java.lang.reflect.InvocationHandler;
28  import java.lang.reflect.Method;
29  import java.lang.reflect.Proxy;
30  import java.util.ArrayList;
31  import java.util.Arrays;
32  import java.util.List;
33  import java.util.Map;
34  
35  /**
36   * <code>ServiceProxy</code> is a proxy that wraps a soap endpointUri to look like
37   * a Web service. Also provides helper methods for building and describing web
38   * service interfaces in Mule.
39   */
40  
41  public class AxisServiceProxy
42  {
43  
44      private static ThreadLocal properties = new ThreadLocal();
45  
46      public static Object createProxy(AbstractMessageReceiver receiver, boolean synchronous, Class[] classes)
47      {
48          final ClassLoader cl = Thread.currentThread().getContextClassLoader();
49          return Proxy.newProxyInstance(cl, classes, createServiceHandler(receiver, synchronous));
50      }
51  
52      public static InvocationHandler createServiceHandler(AbstractMessageReceiver receiver, boolean synchronous)
53      {
54          return new AxisServiceHandler(receiver, synchronous);
55      }
56  
57      private static class AxisServiceHandler implements InvocationHandler
58      {
59          private AbstractMessageReceiver receiver;
60          private boolean synchronous = true;
61  
62          public AxisServiceHandler(AbstractMessageReceiver receiver, boolean synchronous)
63          {
64              this.receiver = receiver;
65              this.synchronous = synchronous;
66          }
67  
68          public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
69          {
70              MuleMessage messageToRoute = receiver.createMuleMessage(args,
71                  receiver.getEndpoint().getEncoding());
72              messageToRoute.setProperty(MuleProperties.MULE_METHOD_PROPERTY, method, PropertyScope.INVOCATION);
73              
74              // add all custom headers, filter out all mule headers (such as
75              // MULE_SESSION) except
76              // for MULE_USER header. Filter out other headers like "soapMethods" and
77              // MuleProperties.MULE_METHOD_PROPERTY and "soapAction"
78              // and also filter out any http related header
79              messageToRoute.addProperties(AxisCleanAndAddProperties.cleanAndAdd(RequestContext.getEventContext()));
80  
81              MuleEvent event = receiver.routeMessage(messageToRoute);
82              MuleMessage message = event == null ? null : event.getMessage();
83  
84              if (message != null)
85              {
86                  ExceptionPayload wsException = message.getExceptionPayload();
87  
88                  if (wsException != null)
89                  {
90                      MuleException exception = ExceptionHelper.getRootMuleException(wsException.getException());
91                      // if the exception has a cause, then throw only the cause
92                      if (exception.getCause() != null)
93                      {
94                          throw exception.getCause();
95                      }
96                      else
97                      {
98                          throw exception;
99                      }
100                 }
101 
102                 return message.getPayload();
103             }
104             else
105             {
106                 return null;
107             }
108         }
109     }
110 
111     /*
112        This is a horrible hack, which is axis-specific (no general classes are affected).  It was
113        added to allow service interface to be configured on endpoints.  The reason it needs to be
114        via a global thread local is that:
115        - the routine getInterfacesForComponent is called from "callback" objects, of which at least
116          one is set in the axis connector.  So the endpoint properties are unavailable when set.
117        - the information passed to the callback is sufficient to identify the component, but not
118          the endpoint, and we would like this configuration to be endpoint specific for two
119          reasons: (i) it is more flexible and (ii) we want to avoid transport specific config
120          on the component (setting it on the connector is way too constraining)
121        - the only other solution (which also uses thread local globals) would be to use the
122          request context, but this is called, amongst other places, from the create() method
123          of the axis message receiver, so no request context is currently in scope.
124        I apologise for this poor code, but after discussing it with rest of the 2.x team we
125        decided that if it worked, it was probably sufficient, since axis 1 support is largely
126        legacy-based.  AC.
127      */
128     public static void setProperties(Map properties)
129     {
130         AxisServiceProxy.properties.set(properties);
131     }
132 
133     public static Class[] getInterfacesForComponent(Service service)
134         throws MuleException, ClassNotFoundException
135     {
136         Class[] interfaces;
137         List ifaces = null;
138 
139         Map localProperties = (Map) properties.get();
140         if (null != localProperties)
141         {
142             ifaces = (List) localProperties.get(SoapConstants.SERVICE_INTERFACES);
143         }
144         if (ifaces == null || ifaces.size() == 0)
145         {
146             final Class implementationClass;
147 
148             if (service.getComponent() instanceof JavaComponent)
149             {
150                 try
151                 {
152                     implementationClass = ((JavaComponent) service.getComponent()).getObjectType();
153                 }
154                 catch (Exception e)
155                 {
156                     throw new ClassNotFoundException("Unable to retrieve class from service factory", e);
157                 }
158             }
159             else
160             {
161                 throw new ClassNotFoundException("Unable to retrieve class from service factory");
162             }
163             
164 
165             // get all implemented interfaces from superclasses as well
166             final List intfList = ClassUtils.getAllInterfaces(implementationClass);
167             interfaces = (Class[])intfList.toArray(new Class[intfList.size()]);
168         }
169         else
170         {
171             interfaces = new Class[ifaces.size()];
172             for (int i = 0; i < ifaces.size(); i++)
173             {
174                 String iface = (String)ifaces.get(i);
175                 interfaces[i] = ClassUtils.loadClass(iface, AxisServiceProxy.class);
176             }
177         }
178 
179         interfaces = removeInterface(interfaces, Callable.class);
180         interfaces = removeInterface(interfaces, Disposable.class);
181         interfaces = removeInterface(interfaces, Initialisable.class);
182         return interfaces;
183     }
184 
185     public static Class[] removeInterface(Class[] interfaces, Class iface)
186     {
187         if (interfaces == null)
188         {
189             return null;
190         }
191         List results = new ArrayList();
192         for (int i = 0; i < interfaces.length; i++)
193         {
194             Class anInterface = interfaces[i];
195             if (!anInterface.equals(iface))
196             {
197                 results.add(anInterface);
198             }
199         }
200         Class[] arResults = new Class[results.size()];
201         if (arResults.length == 0)
202         {
203             return arResults;
204         }
205         else
206         {
207             results.toArray(arResults);
208             return arResults;
209         }
210     }
211 
212     public static Method[] getMethods(Class[] interfaces)
213     {
214         List methodNames = new ArrayList();
215         for (int i = 0; i < interfaces.length; i++)
216         {
217             methodNames.addAll(Arrays.asList(interfaces[i].getMethods()));
218         }
219         Method[] results = new Method[methodNames.size()];
220         return (Method[])methodNames.toArray(results);
221 
222     }
223 
224     public static String[] getMethodNames(Class[] interfaces)
225     {
226         Method[] methods = getMethods(interfaces);
227 
228         String[] results = new String[methods.length];
229         for (int i = 0; i < results.length; i++)
230         {
231             results[i] = methods[i].getName();
232         }
233         return results;
234     }
235 
236 }