View Javadoc

1   /*
2    * $Id: MessagePropertiesTransformer.java 20010 2010-10-27 10:51:39Z 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  
11  package org.mule.transformer.simple;
12  
13  import org.mule.DefaultMuleMessage;
14  import org.mule.api.MuleMessage;
15  import org.mule.api.transport.PropertyScope;
16  import org.mule.routing.filters.WildcardFilter;
17  import org.mule.transformer.AbstractMessageTransformer;
18  import org.mule.transformer.types.DataTypeFactory;
19  import org.mule.transport.NullPayload;
20  
21  import java.text.MessageFormat;
22  import java.util.ArrayList;
23  import java.util.Arrays;
24  import java.util.HashMap;
25  import java.util.HashSet;
26  import java.util.List;
27  import java.util.Map;
28  import java.util.Set;
29  
30  /**
31   * <p>
32   * A configurable message transformer that allows users to add, overwrite, rename and delete
33   * properties on the current message. Users can set a {@link List} of 'deleteProperties' regular 
34   * expressions to remove the matching properties from the message and can also set a {@link Map} 
35   * of 'addProperties' that will be added to the message and possibly overwrite existing properties
36   * with the same name.
37   * <p/> 
38   * <p>
39   * If {@link #overwrite} is set to <code>false</code>, and a property 
40   * exists on the message (even if the value is <code>null</code>, it will be left intact. The 
41   * transformer then acts as a more gentle 'enricher'. The default setting is <code>true</code>.
42   * </p>
43   */
44  public class MessagePropertiesTransformer extends AbstractMessageTransformer
45  {
46      private List<String> deleteProperties = null;
47      private Map<String, Object> addProperties = null;
48      /** the properties map containing rename mappings for message properties */
49      private Map<String, String> renameProperties;
50      private String getProperty;
51      private boolean overwrite = true;
52      // outbound is the default scope
53      private PropertyScope scope = PropertyScope.OUTBOUND;
54  
55      public MessagePropertiesTransformer()
56      {
57          registerSourceType(DataTypeFactory.OBJECT);
58          setReturnDataType(DataTypeFactory.OBJECT);
59      }
60  
61      @Override
62      public Object clone() throws CloneNotSupportedException
63      {
64          MessagePropertiesTransformer clone = (MessagePropertiesTransformer) super.clone();
65  
66          if (deleteProperties != null)
67          {
68              clone.setDeleteProperties(new ArrayList<String>(deleteProperties));
69          }
70  
71          if (addProperties != null)
72          {
73              clone.setAddProperties(new HashMap<String, Object>(addProperties));
74          }
75  
76          if (renameProperties != null)
77          {
78              clone.setRenameProperties(new HashMap<String, String>(renameProperties));
79          }
80          return clone;
81      }
82  
83      @Override
84      public Object transformMessage(MuleMessage message, String outputEncoding)
85      {
86          if (deleteProperties != null && deleteProperties.size() > 0)
87          {
88              deleteProperties(message);
89          }
90  
91          if (addProperties != null && addProperties.size() > 0)
92          {
93              addProperties(message);
94          }
95  
96          /* perform renaming transformation */
97          if (this.renameProperties != null && this.renameProperties.size() > 0)
98          {
99              renameProperties(message);
100         }
101         
102         if (getProperty != null)
103         {
104             Object prop = message.getProperty(getProperty, scope);
105             if (prop != null)
106             {
107                 message = new DefaultMuleMessage(prop, muleContext);
108             }
109             else
110             {
111                 message = new DefaultMuleMessage(NullPayload.getInstance(), muleContext);
112             }
113         }
114 
115         return message;
116     }
117 
118     protected void deleteProperties(MuleMessage message)
119     {
120         final Set<String> propertyNames = new HashSet<String>(message.getPropertyNames(scope));
121         
122         for (String expression : deleteProperties)
123         {
124             for (String key : propertyNames)
125             {
126                 if (key.matches(expression))
127                 {
128                     if (logger.isDebugEnabled())
129                     {
130                         logger.debug(String.format("Removing property: '%s' from scope: '%s'", key, scope.getScopeName()));
131                     }
132                     message.removeProperty(key, scope);
133                 }
134                 else
135                 {
136                     // fallback to the plain wildcard for simplicity
137                     WildcardFilter filter = new WildcardFilter(expression);
138                     if (filter.accept(key))
139                     {
140                         message.removeProperty(key, scope);
141                     }
142                 }
143             }
144         }
145     }
146 
147     protected void addProperties(MuleMessage message)
148     {
149         for (Map.Entry<String, Object> entry : addProperties.entrySet())
150         {
151             if (entry.getKey() == null)
152             {
153                 logger.error("Setting Null property keys is not supported, this entry is being ignored");
154             }
155             else
156             {
157                 final String key = entry.getKey();
158 
159                 Object value= entry.getValue();
160                 Object realValue = value;
161 
162                 //Enable expression support for property values
163                 if (muleContext.getExpressionManager().isExpression(value.toString()))
164                 {
165                     realValue = muleContext.getExpressionManager().evaluate(value.toString(), message);
166                 }
167 
168                 if (realValue != null)
169                 {
170                     if (message.getProperty(key, scope) != null)
171                     {
172                         if (overwrite)
173                         {
174                             logger.debug("Overwriting message property " + key);
175                             message.setProperty(key, realValue, scope);
176                         }
177                         else if(logger.isDebugEnabled())
178                         {
179                             logger.debug(MessageFormat.format(
180                                 "Message already contains the property and overwrite is false, skipping: key={0}, value={1}, scope={2}",
181                                 key, realValue, scope));
182                         }
183                     }
184                     //If value is null an exception will not be thrown if the key was marked as optional (with a '?'). If not
185                     //optional the expression evaluator will throw an exception
186                     else
187                     {
188                         message.setProperty(key, realValue, scope);
189                     }
190                 }
191                 else if (logger.isInfoEnabled())
192                 {
193                     logger.info(MessageFormat.format(
194                         "Property with key '{0}', not found on message using '{1}'. Since the value was marked optional, nothing was set on the message for this property",
195                         key, value));
196                 }
197             }
198         }
199     }
200 
201     protected void renameProperties(MuleMessage message)
202     {
203         for (Map.Entry<String, String> entry : this.renameProperties.entrySet())
204         {
205             if (entry.getKey() == null)
206             {
207                 logger.error("Setting Null property keys is not supported, this entry is being ignored");
208             }
209             else
210             {
211                 final String key = entry.getKey();
212                 String value = entry.getValue();
213 
214                 if (value == null)
215                 {
216                     logger.error("Setting Null property values for renameProperties is not supported, this entry is being ignored");
217                 }
218                 else
219                 {
220                     //Enable expression support for property values
221                     if (muleContext.getExpressionManager().isValidExpression(value))
222                     {
223                         Object temp = muleContext.getExpressionManager().evaluate(value, message);
224                         if (temp != null)
225                         {
226                             value = temp.toString();
227                         }
228                     }
229 
230                     /* log transformation */
231                     if (logger.isDebugEnabled() && message.getProperty(key, scope) == null)
232                     {
233                         logger.debug(String.format("renaming message property '%s' to '%s'", key, value));
234                     }
235 
236                     renameInScope(key, value, scope, message);
237                 }
238             }
239         }
240     }
241 
242     protected void renameInScope(String oldKey, String newKey, PropertyScope propertyScope, MuleMessage message)
243     {
244         Object propValue = message.getProperty(oldKey, propertyScope);
245         message.removeProperty(oldKey, propertyScope);
246         message.setProperty(newKey, propValue, propertyScope);
247     }
248 
249     public List<String> getDeleteProperties()
250     {
251         return deleteProperties;
252     }
253 
254     /**
255      * @see #setDeleteProperties(String...)
256      */
257     public void setDeleteProperties(List<String> deleteProperties)
258     {
259         this.deleteProperties = deleteProperties;
260     }
261 
262     public void setDeleteProperties(String... deleteProperties)
263     {
264         this.deleteProperties = Arrays.asList(deleteProperties);
265     }
266 
267     public Map<String, Object> getAddProperties()
268     {
269         return addProperties;
270     }
271 
272     public void setAddProperties(Map<String, Object> addProperties)
273     {
274         this.addProperties = addProperties;
275     }
276 
277     /**
278      * @return the renameProperties
279      */
280     public Map<String, String> getRenameProperties()
281     {
282         return this.renameProperties;
283     }
284 
285     /**
286      * @param renameProperties the renameProperties to set
287      */
288     public void setRenameProperties(Map<String, String> renameProperties)
289     {
290         this.renameProperties = renameProperties;
291     }
292 
293     public String getGetProperty()
294     {
295         return getProperty;
296     }
297 
298     public void setGetProperty(String getProperty)
299     {
300         this.getProperty = getProperty;
301     }
302 
303     public boolean isOverwrite()
304     {
305         return overwrite;
306     }
307 
308     public void setOverwrite(final boolean overwrite)
309     {
310         this.overwrite = overwrite;
311     }
312 
313     public PropertyScope getScope()
314     {
315         return scope;
316     }
317 
318     public void setScope(PropertyScope scope)
319     {
320         this.scope = scope;
321     }
322     
323     /**
324      * For XML-based config
325      *
326      * @return The string value name for a {@link org.mule.api.transport.PropertyScope}
327      */
328     public String getScopeName()
329     {
330         return scope != null ? scope.getScopeName() : null;
331     }
332 
333     /**
334      * For XML-based config
335      * @param scopeName The string value name for a {@link org.mule.api.transport.PropertyScope}
336      */
337     public void setScopeName(String scopeName)
338     {
339         this.scope = PropertyScope.get(scopeName);
340     }
341 }