View Javadoc

1   /*
2    * $Id: DefaultMuleEvent.java 12269 2008-07-10 04:19:03Z dfeist $
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;
12  
13  import org.mule.api.DefaultMuleException;
14  import org.mule.api.MuleContext;
15  import org.mule.api.MuleEvent;
16  import org.mule.api.MuleException;
17  import org.mule.api.MuleMessage;
18  import org.mule.api.MuleSession;
19  import org.mule.api.ThreadSafeAccess;
20  import org.mule.api.config.MuleProperties;
21  import org.mule.api.endpoint.ImmutableEndpoint;
22  import org.mule.api.security.Credentials;
23  import org.mule.api.service.Service;
24  import org.mule.api.transformer.TransformerException;
25  import org.mule.api.transport.PropertyScope;
26  import org.mule.config.i18n.CoreMessages;
27  import org.mule.endpoint.DefaultEndpointFactory;
28  import org.mule.security.MuleCredentials;
29  import org.mule.util.MapUtils;
30  import org.mule.util.UUID;
31  
32  import java.io.IOException;
33  import java.io.ObjectInputStream;
34  import java.io.ObjectOutputStream;
35  import java.io.OutputStream;
36  import java.io.UnsupportedEncodingException;
37  import java.util.EventObject;
38  import java.util.Iterator;
39  
40  import org.apache.commons.beanutils.PropertyUtils;
41  import org.apache.commons.logging.Log;
42  import org.apache.commons.logging.LogFactory;
43  
44  /**
45   * <code>DefaultMuleEvent</code> represents any data event occuring in the Mule
46   * environment. All data sent or received within the Mule environment will be passed
47   * between components as an MuleEvent. <p/> The MuleEvent holds some data and provides
48   * helper methods for obtaining the data in a format that the receiving Mule UMO
49   * understands. The event can also maintain any number of properties that can be set
50   * and retrieved by Mule UMO components.
51   */
52  
53  public class DefaultMuleEvent extends EventObject implements MuleEvent, ThreadSafeAccess
54  {
55      /**
56       * Serial version
57       */
58      private static final long serialVersionUID = 1L;
59      /**
60       * logger used by this class
61       */
62      protected Log logger = LogFactory.getLog(getClass());
63      /**
64       * The endpoint associated with the event
65       */
66      private transient ImmutableEndpoint endpoint = null;
67  
68      /**
69       * the Universally Unique ID for the event
70       */
71      private String id = null;
72  
73      /**
74       * The payload message used to read the payload of the event
75       */
76      private MuleMessage message = null;
77  
78      private MuleSession session;
79  
80      private boolean stopFurtherProcessing = false;
81  
82      private boolean synchronous = false;
83  
84      private int timeout = TIMEOUT_NOT_SET_VALUE;
85  
86      private transient ResponseOutputStream outputStream = null;
87  
88      private transient Object transformedMessage = null;
89  
90      private Credentials credentials = null;
91  
92      protected String[] ignoredPropertyOverrides = new String[]{MuleProperties.MULE_METHOD_PROPERTY};
93  
94      /**
95       * Properties cache that only reads properties once from the inbound message and
96       * merges them with any properties on the endpoint. The message properties take
97       * precedence over the endpoint properties
98       */
99      public DefaultMuleEvent(MuleMessage message,
100                      ImmutableEndpoint endpoint,
101                      Service service,
102                      MuleEvent previousEvent)
103     {
104         super(message.getPayload());
105         this.message = message;
106         this.id = generateEventId();
107         this.session = previousEvent.getSession();
108         ((DefaultMuleSession) session).setService(service);
109         this.endpoint = endpoint;
110         this.synchronous = previousEvent.isSynchronous();
111         this.timeout = previousEvent.getTimeout();
112         this.outputStream = (ResponseOutputStream) previousEvent.getOutputStream();
113         fillProperties(previousEvent);
114     }
115 
116     public DefaultMuleEvent(MuleMessage message,
117                      ImmutableEndpoint endpoint,
118                      MuleSession session,
119                      boolean synchronous)
120     {
121         this(message, endpoint, session, synchronous, null);
122     }
123 
124     /**
125      * Contructor.
126      *
127      * @param message the event payload
128      * @param endpoint the endpoint to associate with the event
129      * @param session the previous event if any
130      * @see org.mule.api.transport.MessageAdapter
131      */
132     public DefaultMuleEvent(MuleMessage message,
133                      ImmutableEndpoint endpoint,
134                      MuleSession session,
135                      boolean synchronous,
136                      ResponseOutputStream outputStream)
137     {
138         super(message.getPayload());
139         this.message = message;
140         this.endpoint = endpoint;
141         this.session = session;
142         this.id = generateEventId();
143         this.synchronous = synchronous;
144         this.outputStream = outputStream;
145         fillProperties(null);
146     }
147 
148     /**
149      * Contructor.
150      *
151      * @param message the event payload
152      * @param endpoint the endpoint to associate with the event
153      * @param session the previous event if any
154      * @see org.mule.api.transport.MessageAdapter
155      */
156     public DefaultMuleEvent(MuleMessage message,
157                      ImmutableEndpoint endpoint,
158                      MuleSession session,
159                      String eventId,
160                      boolean synchronous)
161     {
162         super(message.getPayload());
163         this.message = message;
164         this.endpoint = endpoint;
165         this.session = session;
166         this.id = eventId;
167         this.synchronous = synchronous;
168         fillProperties(null);
169     }
170 
171     /**
172      * A helper constructor used to rewrite an event payload
173      *
174      * @param message
175      * @param rewriteEvent
176      */
177     public DefaultMuleEvent(MuleMessage message, MuleEvent rewriteEvent)
178     {
179         super(message.getPayload());
180         this.message = message;
181         this.id = rewriteEvent.getId();
182         this.session = rewriteEvent.getSession();
183         ((DefaultMuleSession) session).setService(rewriteEvent.getService());
184         this.endpoint = rewriteEvent.getEndpoint();
185         this.synchronous = rewriteEvent.isSynchronous();
186         this.timeout = rewriteEvent.getTimeout();
187         this.outputStream = (ResponseOutputStream) rewriteEvent.getOutputStream();
188         if (rewriteEvent instanceof DefaultMuleEvent)
189         {
190             this.transformedMessage = ((DefaultMuleEvent) rewriteEvent).getCachedMessage();
191         }
192         fillProperties(rewriteEvent);
193     }
194 
195     protected void fillProperties(MuleEvent previousEvent)
196     {
197         if (previousEvent != null)
198         {
199             MuleMessage msg = previousEvent.getMessage();
200             synchronized (msg)
201             {
202                 for (Iterator iterator = msg.getPropertyNames().iterator(); iterator.hasNext();)
203                 {
204                     String prop = (String) iterator.next();
205                     Object value = msg.getProperty(prop);
206                     // don't overwrite property on the message
207                     if (!ignoreProperty(prop))
208                     {
209                         message.setProperty(prop, value);
210                     }
211                 }
212             }
213         }
214 
215         if (endpoint != null && endpoint.getProperties() != null)
216         {
217             for (Iterator iterator = endpoint.getProperties().keySet().iterator(); iterator.hasNext();)
218             {
219                 String prop = (String) iterator.next();
220                 Object value = endpoint.getProperties().get(prop);
221                 // don't overwrite property on the message
222                 if (!ignoreProperty(prop))
223                 {
224                     message.setProperty(prop, value, PropertyScope.INVOCATION);
225                 }
226             }
227         }
228 
229         setCredentials();
230     }
231 
232     /**
233      * This method is used to determine if a property on the previous event should be
234      * ignorred for the next event. This method is here because we don't have proper
235      * scoped handlng of meta data yet The rules are
236      * <ol>
237      * <li>If a property is already set on the currect event don't overwrite with the previous event value 
238      * <li>If the propery name appears in the ignorredPropertyOverrides list, then we always set it on the new event
239      * </ol>
240      *
241      * @param key
242      * @return
243      */
244     protected boolean ignoreProperty(String key)
245     {
246         if (key == null)
247         {
248             return true;
249         }
250 
251         for (int i = 0; i < ignoredPropertyOverrides.length; i++)
252         {
253             if (key.equals(ignoredPropertyOverrides[i]))
254             {
255                 return false;
256             }
257         }
258 
259         return null != message.getProperty(key);
260     }
261 
262     protected void setCredentials()
263     {
264         if (null != endpoint && null != endpoint.getEndpointURI() && null != endpoint.getEndpointURI().getUserInfo())
265         {
266             final String userName = endpoint.getEndpointURI().getUser();
267             final String password = endpoint.getEndpointURI().getPassword();
268             if (password != null && userName != null)
269             {
270                 credentials = new MuleCredentials(userName, password.toCharArray());
271             }
272         }
273     }
274 
275     public Credentials getCredentials()
276     {
277         return credentials;
278     }
279 
280     Object getCachedMessage()
281     {
282         return transformedMessage;
283     }
284 
285     public MuleMessage getMessage()
286     {
287         return message;
288     }
289 
290     public byte[] getMessageAsBytes() throws DefaultMuleException
291     {
292         try
293         {
294             return message.getPayloadAsBytes();
295         }
296         catch (Exception e)
297         {
298             throw new DefaultMuleException(
299                 CoreMessages.cannotReadPayloadAsBytes(message.getPayload().getClass().getName()), e);
300         }
301     }
302 
303     public Object transformMessage() throws TransformerException
304     {
305         return transformMessage(null);
306     }
307 
308     public Object transformMessage(Class outputType) throws TransformerException
309     {
310         message.applyTransformers(endpoint.getTransformers());
311         if(outputType==null)
312         {
313             return message.getPayload();
314         }
315         else
316         {
317             return message.getPayload(outputType);
318         }
319     }
320     /**
321      * This method will attempt to convert the transformed message into an array of
322      * bytes It will first check if the result of the transformation is a byte array
323      * and return that. Otherwise if the the result is a string it will serialized
324      * the CONTENTS of the string not the String object. finally it will check if the
325      * result is a Serializable object and convert that to an array of bytes.
326      *
327      * @return a byte[] representation of the message
328      * @throws TransformerException if an unsupported encoding is being used or if
329      *             the result message is not a String byte[] or Seializable object
330      */
331     public byte[] transformMessageToBytes() throws TransformerException
332     {
333         return (byte[]) transformMessage(byte[].class);
334     }
335 
336     /**
337      * Returns the message transformed into it's recognised or expected format and
338      * then into a String. The transformer used is the one configured on the endpoint
339      * through which this event was received.
340      *
341      * @return the message transformed into it's recognised or expected format as a
342      *         Strings.
343      * @throws org.mule.api.transformer.TransformerException if a failure occurs in
344      *             the transformer
345      * @see org.mule.api.transformer.Transformer
346      */
347     public String transformMessageToString() throws TransformerException
348     {
349         try
350         {
351             return new String(transformMessageToBytes(), getEncoding());
352         }
353         catch (UnsupportedEncodingException e)
354         {
355             throw new TransformerException(endpoint.getTransformers(), e);
356         }
357         
358         /* TODO MULE-2691 Note that the above code actually transforms the message 
359          * to byte[] instead of String.  The following code would transform the 
360          * message to a String but breaks some tests in transports/http:
361          * 
362         transformMessageToBytes();
363         
364         ByteArrayToObject t = new ByteArrayToObject();
365         t.setEncoding(getEncoding());
366         List list = new ArrayList();
367         list.add(t);
368         message.applyTransformers(list);
369 
370         return (String) message.getPayload();
371         */
372     }
373 
374     public String getMessageAsString() throws MuleException
375     {
376         return getMessageAsString(getEncoding());
377     }
378 
379     /**
380      * Returns the message contents as a string
381      *
382      * @param encoding the encoding to use when converting the message to string
383      * @return the message contents as a string
384      * @throws org.mule.api.MuleException if the message cannot be converted into a
385      *             string
386      */
387     public String getMessageAsString(String encoding) throws MuleException
388     {
389         try
390         {
391             return message.getPayloadAsString(encoding);
392         }
393         catch (Exception e)
394         {
395             throw new DefaultMuleException(
396                 CoreMessages.cannotReadPayloadAsString(message.getClass().getName()), e);
397         }
398     }
399 
400     /*
401      * (non-Javadoc)
402      *
403      * @see org.mule.api.MuleEvent#getId()
404      */
405     public String getId()
406     {
407         return id;
408     }
409 
410     /**
411      * @see org.mule.api.MuleEvent#getProperty(java.lang.String, boolean)
412      */
413     public Object getProperty(String name, boolean exhaustiveSearch)
414     {
415         return getProperty(name, /* defaultValue */null, exhaustiveSearch);
416     }
417 
418     /*
419      * (non-Javadoc)
420      *
421      * @see org.mule.api.MuleEvent#getProperty(java.lang.String, java.lang.Object,
422      *      boolean)
423      */
424     public Object getProperty(String name, Object defaultValue, boolean exhaustiveSearch)
425     {
426         Object property = message.getProperty(name);
427 
428         if (exhaustiveSearch)
429         {
430             // Search the endpoint
431             if (property == null)
432             {
433                 property = MapUtils.getObject(getEndpoint().getEndpointURI().getParams(), name, null);
434             }
435 
436             // Search the connector
437             if (property == null)
438             {
439                 try
440                 {
441                     property = PropertyUtils.getProperty(getEndpoint().getConnector(), name);
442                 }
443                 catch (Exception e)
444                 {
445                     // Ignore this exception, it just means that the connector has no
446                     // such property.
447                 }
448             }
449         }
450         return (property == null ? defaultValue : property);
451     }
452 
453     /*
454      * (non-Javadoc)
455      *
456      * @see org.mule.api.MuleEvent#getEndpoint()
457      */
458     public ImmutableEndpoint getEndpoint()
459     {
460         return endpoint;
461     }
462 
463     /*
464      * (non-Javadoc)
465      *
466      * @see java.lang.Object#toString()
467      */
468     public String toString()
469     {
470         StringBuffer buf = new StringBuffer(64);
471         buf.append("MuleEvent: ").append(getId());
472         buf.append(", sync=").append(isSynchronous());
473         buf.append(", stop processing=").append(isStopFurtherProcessing());
474         buf.append(", ").append(endpoint);
475 
476         return buf.toString();
477     }
478 
479     protected String generateEventId()
480     {
481         return UUID.getUUID();
482     }
483 
484     public MuleSession getSession()
485     {
486         return session;
487     }
488 
489     void setSession(MuleSession session)
490     {
491         this.session = session;
492     }
493 
494     /**
495      * Gets the recipient service of this event
496      */
497     public Service getService()
498     {
499         return session.getService();
500     }
501 
502     /**
503      * Determines whether the default processing for this event will be executed
504      *
505      * @return Returns the stopFurtherProcessing.
506      */
507     public boolean isStopFurtherProcessing()
508     {
509         return stopFurtherProcessing;
510     }
511 
512     /**
513      * Setting this parameter will stop the Mule framework from processing this event
514      * in the standard way. This allow for client code to override default behaviour.
515      * The common reasons for doing this are - 1. The UMO has more than one send
516      * endpoint configured; the service must dispatch to other prviders
517      * programatically by using the service on the current event 2. The UMO doesn't
518      * send the current event out through a endpoint. i.e. the processing of the
519      * event stops in the uMO.
520      *
521      * @param stopFurtherProcessing The stopFurtherProcessing to set.
522      */
523     public void setStopFurtherProcessing(boolean stopFurtherProcessing)
524     {
525         this.stopFurtherProcessing = stopFurtherProcessing;
526     }
527 
528     public boolean equals(Object o)
529     {
530         if (this == o)
531         {
532             return true;
533         }
534         if (!(o instanceof DefaultMuleEvent))
535         {
536             return false;
537         }
538 
539         final DefaultMuleEvent event = (DefaultMuleEvent) o;
540 
541         if (message != null ? !message.equals(event.message) : event.message != null)
542         {
543             return false;
544         }
545         return id.equals(event.id);
546     }
547 
548     public int hashCode()
549     {
550         return 29 * id.hashCode() + (message != null ? message.hashCode() : 0);
551     }
552 
553     public boolean isSynchronous()
554     {
555         return synchronous;
556     }
557 
558     public void setSynchronous(boolean value)
559     {
560         synchronous = value;
561     }
562 
563     public int getTimeout()
564     {
565         if (timeout == TIMEOUT_NOT_SET_VALUE)
566         {
567             // If this is not set it will use the default timeout value
568             timeout = endpoint.getRemoteSyncTimeout();
569         }
570         return timeout;
571     }
572 
573     public void setTimeout(int timeout)
574     {
575         this.timeout = timeout;
576     }
577 
578     /**
579      * An outputstream can optionally be used to write response data to an incoming
580      * message.
581      *
582      * @return an output strem if one has been made available by the message receiver
583      *         that received the message
584      */
585     public OutputStream getOutputStream()
586     {
587         return outputStream;
588     }
589 
590     private void writeObject(ObjectOutputStream out) throws IOException
591     {
592         out.defaultWriteObject();
593         out.writeInt(endpoint.hashCode());
594     }
595 
596     private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException
597     {
598         in.defaultReadObject();
599         int endpointHashCode = in.readInt();
600         endpoint = (ImmutableEndpoint) RegistryContext.getRegistry().lookupObject(
601             DefaultEndpointFactory.ENDPOINT_REGISTRY_PREFIX + endpointHashCode);
602     }
603 
604     /**
605      * Gets the encoding for this message. First it looks to see if encoding has been
606      * set on the endpoint, if not it will check the message itself and finally it
607      * will fall back to the Mule global configuration for encoding which cannot be
608      * null.
609      *
610      * @return the encoding for the event
611      */
612     public String getEncoding()
613     {
614         String encoding = message.getEncoding();
615         if (encoding == null)
616         {
617             encoding = endpoint.getEncoding();
618         }
619 
620         return encoding;
621     }
622 
623     public MuleContext getMuleContext()
624     {
625         return endpoint.getMuleContext();
626     }
627 
628     public ThreadSafeAccess newThreadCopy()
629     {
630         if (message instanceof ThreadSafeAccess)
631         {
632             DefaultMuleEvent copy = new DefaultMuleEvent((MuleMessage) ((ThreadSafeAccess) message).newThreadCopy(), this);
633             copy.resetAccessControl();
634             return copy;
635         }
636         else
637         {
638             return this;
639         }
640     }
641 
642     public void resetAccessControl()
643     {
644         if (message instanceof ThreadSafeAccess)
645         {
646             ((ThreadSafeAccess) message).resetAccessControl();
647         }
648     }
649 
650     public void assertAccess(boolean write)
651     {
652         if (message instanceof ThreadSafeAccess)
653         {
654             ((ThreadSafeAccess) message).assertAccess(write);
655         }
656     }
657 
658 }