View Javadoc

1   /*
2    * $Id: JXPathFilter.java 12273 2008-07-10 13:25:32Z tcarlson $
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.module.xml.filters;
12  
13  import org.mule.api.MuleMessage;
14  import org.mule.api.routing.filter.Filter;
15  import org.mule.module.xml.util.XMLUtils;
16  
17  import java.util.Iterator;
18  import java.util.Map;
19  
20  import org.apache.commons.jxpath.AbstractFactory;
21  import org.apache.commons.jxpath.JXPathContext;
22  import org.apache.commons.logging.Log;
23  import org.apache.commons.logging.LogFactory;
24  import org.dom4j.Document;
25  import org.dom4j.DocumentHelper;
26  import org.dom4j.XPath;
27  
28  /**
29   * <code>JXPathFilter</code> evaluates an XPath expression against a W3C Document,
30   * XML string, or Java bean and returns true if the result is as expected.
31   */
32  public class JXPathFilter implements Filter
33  {
34  
35      protected transient Log logger = LogFactory.getLog(getClass());
36  
37      private String pattern;
38      private String expectedValue;
39      private Map namespaces = null;
40      private Map contextProperties = null;
41      private AbstractFactory factory;
42      private boolean lenient = true;
43  
44      public JXPathFilter()
45      {
46          super();
47      }
48  
49      public JXPathFilter(String pattern)
50      {
51          this.pattern = pattern;
52      }
53  
54      public JXPathFilter(String pattern, String expectedValue)
55      {
56          this.pattern = pattern;
57          this.expectedValue = expectedValue;
58      }
59  
60      public boolean accept(MuleMessage obj)
61      {
62          if (obj.getPayload() instanceof byte[])
63          {
64              try
65              {
66                  return accept(obj.getPayloadAsString());
67              }
68              catch (Exception e)
69              {
70                  logger.warn("JxPath filter rejected message because it could not convert from byte[] to String" + e.getMessage(), e);
71                  return false;
72              }
73          }
74          return accept(obj.getPayload());
75      }
76  
77      private boolean accept(Object obj)
78      {
79          if (obj == null)
80          {
81              logger.warn("Applying JXPathFilter to null object.");
82              return false;
83          }
84          if (pattern == null)
85          {
86              logger.warn("Expression for JXPathFilter is not set.");
87              return false;
88          }
89          if (expectedValue == null)
90          {
91              // Handle the special case where the expected value really is null.
92              if (pattern.endsWith("= null") || pattern.endsWith("=null"))
93              {
94                  expectedValue = "null";
95                  pattern = pattern.substring(0, pattern.lastIndexOf("="));
96              }
97              else
98              {
99                  if (logger.isInfoEnabled())
100                 {
101                     logger.info("Expected value for JXPathFilter is not set, using 'true' by default");
102                 }
103                 expectedValue = Boolean.TRUE.toString();
104             }
105         }
106 
107         Object xpathResult = null;
108         boolean accept = false;
109 
110         Document dom4jDoc;
111         try
112         {
113             dom4jDoc = XMLUtils.toDocument(obj);
114         }
115         catch (Exception e)
116         {
117             logger.warn("JxPath filter rejected message because of an error while parsing XML: " + e.getMessage(), e);
118             return false;
119         }
120         
121         // Payload is XML
122         if (dom4jDoc != null)
123         {
124             if (namespaces == null)
125             {
126                 // no namespace defined, let's perform a direct evaluation
127                 xpathResult = dom4jDoc.valueOf(pattern);
128             }
129             else
130             {
131                 // create an xpath expression with namespaces and evaluate it
132                 XPath xpath = DocumentHelper.createXPath(pattern);
133                 xpath.setNamespaceURIs(namespaces);
134                 xpathResult = xpath.valueOf(dom4jDoc);
135             }
136         }
137         // Payload is a Java object
138         else
139         {
140             if (logger.isDebugEnabled())
141             {
142                 logger.debug("Passing object of type " + obj.getClass().getName() + " to JXPathContext");
143             }
144             JXPathContext context = JXPathContext.newContext(obj);
145             initialise(context);
146             xpathResult = context.getValue(pattern);
147         }
148 
149         if (logger.isDebugEnabled())
150         {
151             logger.debug("JXPathFilter Expression result = '" + xpathResult + "' -  Expected value = '"
152                     + expectedValue + "'");
153         }
154         // Compare the XPath result with the expected result.
155         if (xpathResult != null)
156         {
157             accept = xpathResult.toString().equals(expectedValue);
158         }
159         else
160         {
161             // A null result was actually expected.
162             if (expectedValue.equals("null"))
163             {
164                 accept = true;
165             }
166             // A null result was not expected, something probably went wrong.
167             else
168             {
169                 logger.warn("JXPathFilter expression evaluates to null: " + pattern);
170             }
171         }
172 
173         if (logger.isDebugEnabled())
174         {
175             logger.debug("JXPathFilter accept object  : " + accept);
176         }
177 
178         return accept;
179     }
180 
181     /**
182      * Initializes the JXPathContext based on any relevant properties set for the
183      * filter.
184      *
185      * @param context the JXPathContext to initialize
186      */
187     protected void initialise(JXPathContext context)
188     {
189         Map.Entry entry;
190         if (namespaces != null)
191         {
192             if (logger.isDebugEnabled())
193             {
194                 logger.debug("Initializing JXPathContext with namespaces: " + namespaces);
195             }
196 
197             for (Iterator iterator = namespaces.entrySet().iterator(); iterator.hasNext();)
198             {
199                 entry = (Map.Entry) iterator.next();
200                 context.registerNamespace(entry.getKey().toString(), entry.getValue().toString());
201             }
202         }
203 
204         if (contextProperties != null)
205         {
206             if (logger.isDebugEnabled())
207             {
208                 logger.debug("Initializing JXPathContext with properties: " + contextProperties);
209             }
210 
211             for (Iterator iterator = contextProperties.entrySet().iterator(); iterator.hasNext();)
212             {
213                 entry = (Map.Entry) iterator.next();
214                 context.setValue(entry.getKey().toString(), entry.getValue());
215             }
216         }
217 
218         if (factory != null)
219         {
220             context.setFactory(factory);
221         }
222 
223         context.setLenient(lenient);
224     }
225 
226     /** @return XPath expression */
227     public String getPattern()
228     {
229         return pattern;
230     }
231 
232     /** @param pattern The XPath expression */
233     public void setPattern(String pattern)
234     {
235         this.pattern = pattern;
236     }
237 
238     /** @return The expected result value of the XPath expression */
239     public String getExpectedValue()
240     {
241         return expectedValue;
242     }
243 
244     /** Sets the expected result value of the XPath expression */
245     public void setExpectedValue(String expectedValue)
246     {
247         this.expectedValue = expectedValue;
248     }
249 
250     /**
251      * @return The expected result value of the XPath expression
252      * @deprecated Use <code>getExpectedValue()</code>.
253      */
254     public String getValue()
255     {
256         return getExpectedValue();
257     }
258 
259     /**
260      * Sets the expected result value of the XPath expression
261      *
262      * @deprecated Use <code>setExpectedValue(String expectedValue)</code>.
263      */
264     public void setValue(String value)
265     {
266         setExpectedValue(value);
267     }
268 
269     public Map getNamespaces()
270     {
271         return namespaces;
272     }
273 
274     public void setNamespaces(Map namespaces)
275     {
276         this.namespaces = namespaces;
277     }
278 
279     public Map getContextProperties()
280     {
281         return contextProperties;
282     }
283 
284     public void setContextProperties(Map contextProperties)
285     {
286         this.contextProperties = contextProperties;
287     }
288 
289     public AbstractFactory getFactory()
290     {
291         return factory;
292     }
293 
294     public void setFactory(AbstractFactory factory)
295     {
296         this.factory = factory;
297     }
298 
299     public boolean isLenient()
300     {
301         return lenient;
302     }
303 
304     public void setLenient(boolean lenient)
305     {
306         this.lenient = lenient;
307     }
308 }