View Javadoc

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