View Javadoc

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