View Javadoc

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