View Javadoc
1   /*
2    * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
3    * The software in this package is published under the terms of the CPAL v1.0
4    * license, a copy of which has been included with this distribution in the
5    * LICENSE.txt file.
6    */
7   package org.mule;
8   
9   import org.mule.api.MuleEvent;
10  import org.mule.api.MuleSession;
11  import org.mule.api.transport.PropertyScope;
12  import org.mule.util.CaseInsensitiveHashMap;
13  import org.mule.util.MapUtils;
14  import org.mule.util.ObjectUtils;
15  
16  import java.io.Serializable;
17  import java.util.Collections;
18  import java.util.HashMap;
19  import java.util.HashSet;
20  import java.util.Map;
21  import java.util.Set;
22  import java.util.TreeMap;
23  import java.util.TreeSet;
24  
25  import org.apache.commons.logging.Log;
26  import org.apache.commons.logging.LogFactory;
27  
28  /**
29   * This object maintains a scoped map of properties.  This means that certain properties will only be visible under some
30   * scopes. The scopes supported by Mule are:
31   * <ol>
32   * <li> {@link org.mule.api.transport.PropertyScope#INBOUND} Contains properties that were on the message when it was
33   * received by Mule.  This scope is read-only.</li>
34   * <li>{@link org.mule.api.transport.PropertyScope#INVOCATION} Any properties set on the invocation scope will be
35   * available to the current service but will not be attached to any outbound messages.</li>
36   * <li>{@link org.mule.api.transport.PropertyScope#OUTBOUND} Any properties set in this scope will be attached to any
37   * outbound messages resulting from this message.  This is the default scope.</li>
38   * <li>{@link org.mule.api.transport.PropertyScope#SESSION} Any properties set on this scope will be added to the session.
39   * Note that this is a convenience scope in that you cannot directly access session properties from this scope.  Session
40   * properties can be accessed from the {@link MuleEvent}</li>
41   * </ol>
42   */
43  public class MessagePropertiesContext implements Serializable
44  {
45      private static final long serialVersionUID = -5230693402768953742L;
46      private static final PropertyScope DEFAULT_SCOPE = PropertyScope.OUTBOUND;
47  
48      private static Log logger = LogFactory.getLog(MessagePropertiesContext.class);
49  
50      /**
51       * Map of maps containing the scoped properties, each scope has its own Map.
52       */
53      protected Map<PropertyScope, Map<String, Object>> scopedMap;
54  
55      /**
56       * The union of all property names from all scopes.
57       */
58      protected Set<String> keySet;
59  
60  
61      @SuppressWarnings("unchecked")
62      public MessagePropertiesContext()
63      {
64          keySet = new TreeSet<String>();
65          scopedMap = new TreeMap<PropertyScope, Map<String, Object>>(new PropertyScope.ScopeComparator());
66  
67          scopedMap.put(PropertyScope.INVOCATION, new CaseInsensitiveHashMap/*<String, Object>*/(6));
68          scopedMap.put(PropertyScope.INBOUND, new CaseInsensitiveHashMap/*<String, Object>*/(6));
69          scopedMap.put(PropertyScope.OUTBOUND, new CaseInsensitiveHashMap/*<String, Object>*/(6));
70      }
71  
72      protected Map<String, Object> getScopedProperties(PropertyScope scope)
73      {
74          Map<String, Object> map = scopedMap.get(scope);
75          if (map == null)
76          {
77              throw new IllegalArgumentException("Scope not registered: " + scope);
78          }
79          return map;
80      }
81  
82      public PropertyScope getDefaultScope()
83      {
84          return DEFAULT_SCOPE;
85      }
86  
87      protected void addInboundProperties(Map<String, Object> properties)
88      {
89          if (properties != null)
90          {
91              Map<String, Object> props = new HashMap<String, Object>(properties.size());
92              for (String key : properties.keySet())
93              {
94                  props.put(key, properties.get(key));
95              }
96              getScopedProperties(PropertyScope.INBOUND).putAll(props);
97              keySet.addAll(props.keySet());
98          }
99      }
100 
101     /**
102      *
103      * @deprecated use the overloaded version with an explicit lookup scope. This method will
104      * now use only the outbound scope.
105      */
106     @Deprecated
107     public Object getProperty(String key)
108     {
109         return getProperty(key, PropertyScope.OUTBOUND);
110     }
111 
112     @SuppressWarnings("unchecked")
113     public <T> T getProperty(String key, PropertyScope scope)
114     {
115         if (scope == null)
116         {
117             scope = PropertyScope.OUTBOUND;
118         }
119 
120         T value = null;
121         if (PropertyScope.SESSION.equals(scope))
122         {
123             if (RequestContext.getEvent() != null)
124             {
125                 value = (T) RequestContext.getEvent().getSession().getProperty(key);
126             }
127         }
128         else
129         {
130             value = (T) scopedMap.get(scope).get(key);
131         }
132         return value;
133     }
134 
135     /**
136      * Removes all properties from all scopes except for SESSION and INBOUND (which is read-only).
137      * You may explicitly clear the session properties by calling clearProperties(PropertyScope.SESSION)
138      */
139     public void clearProperties()
140     {
141         Map<String, Object> props = getScopedProperties(PropertyScope.INVOCATION);
142         keySet.removeAll(props.keySet());
143         props.clear();
144         props = getScopedProperties(PropertyScope.OUTBOUND);
145         keySet.removeAll(props.keySet());
146         props.clear();
147     }
148 
149     public void clearProperties(PropertyScope scope)
150     {
151         if (scope == null)
152         {
153             clearProperties();
154             return;
155         }
156 
157         //checkScopeForWriteAccess(scope);
158         if (PropertyScope.SESSION.equals(scope))
159         {
160             if (RequestContext.getEvent() != null)
161             {
162                 MuleSession session = RequestContext.getEvent().getSession();
163                 for (Object key : session.getPropertyNamesAsSet())
164                 {
165                     session.removeProperty(key);
166                 }
167             }
168         }
169         else
170         {
171             Map<String, Object> props = getScopedProperties(scope);
172             keySet.removeAll(props.keySet());
173             props.clear();
174         }
175     }
176 
177     /**
178      * Removes a property from all scopes except for SESSION and INBOUND (which is read-only).
179      * You may explicitly remove a session property by calling removeProperty(key, PropertyScope.SESSION)
180      *
181      * @param key the property key to remove
182      * @return the removed property value or null if the property did not exist
183      */
184     public Object removeProperty(String key)
185     {
186         Object value = getScopedProperties(PropertyScope.OUTBOUND).remove(key);
187         Object inv = getScopedProperties(PropertyScope.INVOCATION).remove(key);
188 
189         keySet.remove(key);
190 
191         if (value == null)
192         {
193             value = inv;
194         }
195 
196         return value;
197     }
198 
199     /**
200      * Removes a property from the specified property scope.
201      *
202      * @param key the property key to remove
203      * @return the removed property value or null if the property did not exist
204      */
205     public Object removeProperty(String key, PropertyScope scope)
206     {
207         if (scope == null)
208         {
209             return removeProperty(key);
210         }
211 
212         Object value = null;
213         if (PropertyScope.SESSION.equals(scope))
214         {
215             if (RequestContext.getEvent() != null)
216             {
217                 value = RequestContext.getEvent().getSession().removeProperty(key);
218             }
219         }
220         else
221         {
222             value = getScopedProperties(scope).remove(key);
223         }
224 
225         // Only remove the property from the keySet if it does not exist in any other scope besides this one.
226         if (getProperty(key, PropertyScope.OUTBOUND) == null
227                 && getProperty(key, PropertyScope.INVOCATION) == null
228                 && getProperty(key, PropertyScope.INBOUND) == null)
229         {
230             keySet.remove(key);
231         }
232 
233         return value;
234     }
235 
236     /**
237      * Set a property on the message
238      *
239      * @param key   the key on which to associate the value
240      * @param value the property value
241      * @deprecated use {@link #setProperty(String, Object, org.mule.api.transport.PropertyScope)}
242      */
243     @Deprecated
244     public void setProperty(String key, Object value)
245     {
246         getScopedProperties(DEFAULT_SCOPE).put(key, value);
247         keySet.add(key);
248     }
249 
250     /**
251      * Set a property on the message
252      *
253      * @param key   the key on which to associate the value
254      * @param value the property value
255      * @param scope the scope to se the property on
256      * @see org.mule.api.transport.PropertyScope
257      */
258     public void setProperty(String key, Object value, PropertyScope scope)
259     {
260         //checkScopeForWriteAccess(scope);
261         if (PropertyScope.SESSION.equals(scope))
262         {
263             if (RequestContext.getEvent() != null)
264             {
265                 RequestContext.getEvent().getSession().setProperty(key, value);
266             }
267             else
268             {
269                 logger.warn(String.format("Detected an attempt to set a session property, " +
270                                           "but MuleEvent isn't available in this thread. Key/value: %s=%s %s",
271                                           key, value, Thread.currentThread()));
272             }
273         }
274         else
275         {
276             getScopedProperties(scope).put(key, value);
277             keySet.add(key);
278         }
279     }
280 
281     /**
282      * @deprecated use {@link #getPropertyNames(org.mule.api.transport.PropertyScope)}
283      */
284     @Deprecated
285     public Set<String> getPropertyNames()
286     {
287         Set<String> allProps = new HashSet<String>();
288         allProps.addAll(keySet);
289         if (RequestContext.getEvent() != null)
290         {
291             allProps.addAll(RequestContext.getEvent().getSession().getPropertyNamesAsSet());
292         }
293         return allProps;
294     }
295 
296     /**
297      * @return all property keys on this message for the given scope
298      */
299     public Set<String> getPropertyNames(PropertyScope scope)
300     {
301         if (PropertyScope.SESSION.equals(scope))
302         {
303             if (RequestContext.getEvent() != null)
304             {
305                 return RequestContext.getEvent().getSession().getPropertyNamesAsSet();
306             }
307             else
308             {
309                 return Collections.emptySet();
310             }
311         }
312         else
313         {
314             return Collections.unmodifiableSet(getScopedProperties(scope).keySet());
315         }
316     }
317 
318     protected void checkScopeForWriteAccess(PropertyScope scope)
319     {
320         if (scope == null || PropertyScope.INBOUND.equals(scope))
321         {
322             throw new IllegalArgumentException("Scope is invalid for writing properties: " + scope);
323         }
324     }
325 
326     public Object getProperty(String key, Object defaultValue)
327     {
328         Object value = getProperty(key);
329         if (value == null)
330         {
331             value = defaultValue;
332         }
333         return value;
334     }
335 
336     public byte getByteProperty(String name, byte defaultValue)
337     {
338         return ObjectUtils.getByte(getProperty(name), defaultValue);
339     }
340 
341     public short getShortProperty(String name, short defaultValue)
342     {
343         return ObjectUtils.getShort(getProperty(name), defaultValue);
344     }
345 
346     public int getIntProperty(String name, int defaultValue)
347     {
348         return ObjectUtils.getInt(getProperty(name), defaultValue);
349     }
350 
351     public long getLongProperty(String name, long defaultValue)
352     {
353         return ObjectUtils.getLong(getProperty(name), defaultValue);
354     }
355 
356     public float getFloatProperty(String name, float defaultValue)
357     {
358         return ObjectUtils.getFloat(getProperty(name), defaultValue);
359     }
360 
361     public double getDoubleProperty(String name, double defaultValue)
362     {
363         return ObjectUtils.getDouble(getProperty(name), defaultValue);
364     }
365 
366     public boolean getBooleanProperty(String name, boolean defaultValue)
367     {
368         return ObjectUtils.getBoolean(getProperty(name), defaultValue);
369     }
370 
371     @Deprecated
372     public String getStringProperty(String name, String defaultValue)
373     {
374         return getStringProperty(name, PropertyScope.OUTBOUND, defaultValue);
375     }
376 
377     public String getStringProperty(String name, PropertyScope scope, String defaultValue)
378     {
379         return ObjectUtils.getString(getProperty(name, scope), defaultValue);
380     }
381 
382     @Override
383     public String toString()
384     {
385         StringBuffer buf = new StringBuffer(128);
386         buf.append("Properties{");
387         for (Map.Entry<PropertyScope, Map<String, Object>> entry : scopedMap.entrySet())
388         {
389             buf.append(entry.getKey()).append(":");
390             buf.append(MapUtils.toString(entry.getValue(), false));
391             buf.append(", ");
392         }
393         buf.append("}");
394         return buf.toString();
395     }
396 }