View Javadoc

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