View Javadoc

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