View Javadoc

1   /*
2    * $Id: RemoteDispatcherComponent.java 11517 2008-03-31 21:34:19Z dirk.olmes $
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.module.client.remoting;
12  
13  import org.mule.DefaultMuleEvent;
14  import org.mule.DefaultMuleMessage;
15  import org.mule.DefaultMuleSession;
16  import org.mule.MuleServer;
17  import org.mule.RequestContext;
18  import org.mule.api.DefaultMuleException;
19  import org.mule.api.MuleContext;
20  import org.mule.api.MuleEvent;
21  import org.mule.api.MuleEventContext;
22  import org.mule.api.MuleException;
23  import org.mule.api.MuleMessage;
24  import org.mule.api.MuleSession;
25  import org.mule.api.config.MuleProperties;
26  import org.mule.api.endpoint.EndpointBuilder;
27  import org.mule.api.endpoint.EndpointFactory;
28  import org.mule.api.endpoint.ImmutableEndpoint;
29  import org.mule.api.endpoint.InboundEndpoint;
30  import org.mule.api.endpoint.OutboundEndpoint;
31  import org.mule.api.lifecycle.Callable;
32  import org.mule.api.lifecycle.Initialisable;
33  import org.mule.api.lifecycle.InitialisationException;
34  import org.mule.api.service.Service;
35  import org.mule.api.transformer.TransformerException;
36  import org.mule.api.transformer.wire.WireFormat;
37  import org.mule.component.SimpleCallableJavaComponent;
38  import org.mule.config.i18n.CoreMessages;
39  import org.mule.endpoint.EndpointURIEndpointBuilder;
40  import org.mule.message.DefaultExceptionPayload;
41  import org.mule.model.seda.SedaService;
42  import org.mule.module.client.remoting.notification.RemoteDispatcherNotification;
43  import org.mule.object.PrototypeObjectFactory;
44  import org.mule.transport.AbstractConnector;
45  import org.mule.transport.NullPayload;
46  import org.mule.util.MapUtils;
47  
48  import java.io.ByteArrayInputStream;
49  import java.util.HashMap;
50  import java.util.LinkedList;
51  import java.util.List;
52  import java.util.Map;
53  
54  import org.apache.commons.io.output.ByteArrayOutputStream;
55  import org.apache.commons.logging.Log;
56  import org.apache.commons.logging.LogFactory;
57  
58  /**
59   * <code>RemoteDispatcherComponent</code> is a MuleManager interal server component
60   * responsible for receiving remote requests and dispatching them locally. This
61   * allows developer to tunnel requests through http ssl to a Mule instance behind a
62   * firewall
63   */
64  
65  public class RemoteDispatcherComponent implements Callable, Initialisable
66  {
67      /**
68       * logger used by this class
69       */
70      protected static final Log logger = LogFactory.getLog(RemoteDispatcherComponent.class);
71  
72      public static final String MANAGER_COMPONENT_NAME = "_muleManagerComponent";
73  
74      /**
75       * Use Serialization by default
76       */
77      protected WireFormat wireFormat;
78  
79      protected String encoding;
80  
81      protected int synchronousEventTimeout = 5000;
82  
83      public void initialise() throws InitialisationException
84      {
85          if (wireFormat == null)
86          {
87              throw new InitialisationException(CoreMessages.objectIsNull("wireFormat"), this);
88          }
89      }
90  
91      public Object onCall(MuleEventContext context) throws Exception
92      {
93          if(context.transformMessageToString().equals(ServerHandshake.SERVER_HANDSHAKE_PROPERTY))
94          {
95              return doHandshake(context);
96          }
97  
98          Object result;
99          logger.debug("Message received by RemoteDispatcherComponent");
100         ByteArrayInputStream in = new ByteArrayInputStream(context.transformMessageToBytes());
101         RemoteDispatcherNotification action = (RemoteDispatcherNotification) ((MuleMessage)wireFormat.read(in)).getPayload();
102 
103         if (RemoteDispatcherNotification.ACTION_INVOKE == action.getAction())
104         {
105             result = invokeAction(action, context);
106         }
107         else if (RemoteDispatcherNotification.ACTION_SEND == action.getAction() ||
108                  RemoteDispatcherNotification.ACTION_DISPATCH == action.getAction())
109         {
110             result = sendAction(action, context);
111         }
112         else if (RemoteDispatcherNotification.ACTION_RECEIVE == action.getAction())
113         {
114             result = receiveAction(action, context);
115         }
116         else
117         {
118             result = handleException(null, new DefaultMuleException(
119                 CoreMessages.eventTypeNotRecognised("RemoteDispatcherNotification:" + action.getAction())));
120         }
121         return result;
122     }
123 
124     protected ServerHandshake doHandshake(MuleEventContext context) throws TransformerException
125     {
126         ServerHandshake handshake  = new ServerHandshake();
127         handshake.setWireFormatClass(wireFormat.getClass().getName());
128         return handshake;
129     }
130 
131     protected Object invokeAction(RemoteDispatcherNotification action, MuleEventContext context) throws MuleException
132     {
133         String destComponent;
134         MuleMessage result = null;
135         String endpoint = action.getResourceIdentifier();
136         if (action.getResourceIdentifier().startsWith("mule:"))
137         {
138             destComponent = endpoint.substring(endpoint.lastIndexOf("/") + 1);
139         }
140         else
141         {
142             destComponent = endpoint;
143         }
144 
145         if (destComponent != null)
146         {
147             MuleSession session = 
148                 new DefaultMuleSession(context.getMuleContext().getRegistry().lookupService(destComponent), context.getMuleContext());
149             // Need to do this otherise when the event is invoked the
150             // transformer associated with the Mule Admin queue will be invoked, but
151             // the message will not be of expected type
152             MuleContext managementContext = MuleServer.getMuleContext();
153             EndpointBuilder builder = new EndpointURIEndpointBuilder(RequestContext.getEvent().getEndpoint(), managementContext);
154             // TODO - is this correct? it stops any other transformer from being set
155             builder.setTransformers(new LinkedList());
156             ImmutableEndpoint ep = managementContext.getRegistry().lookupEndpointFactory().getInboundEndpoint(builder);
157             MuleEvent event = new DefaultMuleEvent(action.getMessage(), ep, context.getSession(), context.isSynchronous());
158             event = RequestContext.setEvent(event);
159 
160             if (context.isSynchronous())
161             {
162                 result = session.getService().sendEvent(event);
163                 ByteArrayOutputStream out = new ByteArrayOutputStream();
164                 wireFormat.write(out, result, getEncoding());
165                 return out.toByteArray();
166             }
167             else
168             {
169                 session.getService().dispatchEvent(event);
170                 return null;
171             }
172         }
173         else
174         {
175             return handleException(result, new DefaultMuleException(
176                 CoreMessages.couldNotDetermineDestinationComponentFromEndpoint(endpoint)));
177         }
178     }
179 
180     protected Object sendAction(RemoteDispatcherNotification action, MuleEventContext context) throws MuleException
181     {
182         MuleMessage result = null;
183         OutboundEndpoint endpoint = null;
184         MuleContext managementContext = context.getMuleContext();
185         try
186         {
187             if (RemoteDispatcherNotification.ACTION_DISPATCH == action.getAction())
188             {
189                 endpoint = managementContext.getRegistry().lookupEndpointFactory().getOutboundEndpoint(
190                     action.getResourceIdentifier());
191                 context.dispatchEvent(action.getMessage(), endpoint);
192                 return null;
193             }
194             else
195             {
196                 EndpointFactory endpointFactory = managementContext.getRegistry().lookupEndpointFactory();
197                 EndpointBuilder endpointBuilder = endpointFactory.getEndpointBuilder(action.getResourceIdentifier());
198                 endpointBuilder.setRemoteSync(true);
199                 endpoint = managementContext.getRegistry().lookupEndpointFactory().getOutboundEndpoint(endpointBuilder);
200                 result = context.sendEvent(action.getMessage(), endpoint);
201                 if (result == null)
202                 {
203                     return null;
204                 }
205                 else
206                 {
207                     ByteArrayOutputStream out = new ByteArrayOutputStream();
208                     wireFormat.write(out, result, getEncoding());
209                     return out.toByteArray();
210                 }
211             }
212         }
213         catch (Exception e)
214         {
215             return handleException(result, e);
216         }
217     }
218 
219     protected Object receiveAction(RemoteDispatcherNotification action, MuleEventContext context) throws MuleException
220     {
221         MuleMessage result = null;
222         try
223         {
224             ImmutableEndpoint endpoint = context.getMuleContext()
225                 .getRegistry()
226                 .lookupEndpointFactory()
227                 .getOutboundEndpoint(action.getResourceIdentifier());
228 
229             long timeout = MapUtils.getLongValue(action.getProperties(),
230                 MuleProperties.MULE_EVENT_TIMEOUT_PROPERTY, getSynchronousEventTimeout());
231 
232             result = endpoint.getConnector().request(action.getResourceIdentifier(), timeout);
233             if (result != null)
234             {
235                 // See if there is a default transformer on the connector
236                 List transformers = ((AbstractConnector) endpoint.getConnector()).getDefaultInboundTransformers();
237                 if (transformers != null)
238                 {
239                     result.applyTransformers(transformers);
240                 }
241                 ByteArrayOutputStream out = new ByteArrayOutputStream();
242                 wireFormat.write(out, result, getEncoding());
243                 return out.toByteArray();
244             }
245             else
246             {
247                 return null;
248             }
249         }
250         catch (Exception e)
251         {
252             return handleException(result, e);
253         }
254 
255     }
256 
257 
258     public static final Service getSerivce(InboundEndpoint endpoint,
259                                                     WireFormat wireFormat,
260                                                     String encoding,
261                                                     int eventTimeout,
262                                                     MuleContext managementContext) throws MuleException
263     {
264         try
265         {
266             Service service = new SedaService();
267             service.setName(MANAGER_COMPONENT_NAME);
268             service.setModel(managementContext.getRegistry().lookupSystemModel());
269 
270             Map props = new HashMap();
271             props.put("wireFormat", wireFormat);
272             props.put("encoding", encoding);
273             props.put("synchronousEventTimeout", new Integer(eventTimeout));
274             service.setComponent(new SimpleCallableJavaComponent(new PrototypeObjectFactory(RemoteDispatcherComponent.class, props)));
275 
276 
277             service.setMuleContext(managementContext);
278             service.getInboundRouter().addEndpoint(endpoint);
279 
280             return service;
281         }
282         catch (Exception e)
283         {
284             throw new InitialisationException(e, null);
285         }
286     }
287 
288     /**
289      * Wraps an exception into a MuleMessage with an Exception payload and returns
290      * the Xml representation of it
291      * 
292      * @param result the result of the invocation or null if the exception occurred
293      *            before or during the invocation
294      * @param e the Exception thrown
295      * @return an Xml String message result
296      */
297     protected Object handleException(MuleMessage result, Throwable e)
298     {
299         logger.error("Failed to process admin request: " + e.getMessage(), e);
300         if (result == null)
301         {
302             result = new DefaultMuleMessage(NullPayload.getInstance(), (Map) null);
303         }
304         result.setExceptionPayload(new DefaultExceptionPayload(e));
305         try
306         {
307             ByteArrayOutputStream out = new ByteArrayOutputStream();
308             wireFormat.write(out, result, getEncoding());
309             return out.toByteArray();
310         }
311         catch (Exception e1)
312         {
313             // TODO MULE-863: Is this sufficient?
314             // log the inner exception here since the earlier exception was logged earlier
315             logger.error("Failed to format message, using direct string (details at debug level): " + e1.getMessage());
316             logger.debug(e1.toString(), e1);
317             return e.getMessage();
318         }
319     }
320 
321     public WireFormat getWireFormat()
322     {
323         return wireFormat;
324     }
325 
326     public void setWireFormat(WireFormat wireFormat)
327     {
328         this.wireFormat = wireFormat;
329     }
330 
331     public String getEncoding()
332     {
333         return encoding;
334     }
335 
336     public void setEncoding(String encoding)
337     {
338         this.encoding = encoding;
339     }
340 
341     public int getSynchronousEventTimeout()
342     {
343         return synchronousEventTimeout;
344     }
345 
346     public void setSynchronousEventTimeout(int synchronousEventTimeout)
347     {
348         this.synchronousEventTimeout = synchronousEventTimeout;
349     }
350 }