View Javadoc

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