View Javadoc

1   /*
2    * $Id: JaxenFilter.java 19191 2010-08-25 21:05:23Z tcarlson $
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.module.xml.filters;
12  
13  import static org.mule.util.ClassUtils.equal;
14  import static org.mule.util.ClassUtils.hash;
15  
16  import org.mule.api.MuleContext;
17  import org.mule.api.MuleMessage;
18  import org.mule.api.context.MuleContextAware;
19  import org.mule.api.expression.ExpressionRuntimeException;
20  import org.mule.api.registry.RegistrationException;
21  import org.mule.api.routing.filter.Filter;
22  import org.mule.config.i18n.CoreMessages;
23  import org.mule.module.xml.util.NamespaceManager;
24  import org.mule.module.xml.util.XMLUtils;
25  
26  import java.io.InputStream;
27  import java.util.HashMap;
28  import java.util.Iterator;
29  import java.util.Map;
30  
31  import javax.xml.transform.dom.DOMSource;
32  
33  import org.apache.commons.jxpath.AbstractFactory;
34  import org.apache.commons.logging.Log;
35  import org.apache.commons.logging.LogFactory;
36  import org.dom4j.Document;
37  import org.jaxen.BaseXPath;
38  import org.jaxen.JaxenException;
39  import org.jaxen.dom.DOMXPath;
40  import org.jaxen.dom4j.Dom4jXPath;
41  import org.jaxen.javabean.JavaBeanXPath;
42  import org.mule.transformer.types.DataTypeFactory;
43  
44  /**
45   * <code>JaxenFilter</code> evaluates an XPath expression against an XML document
46   * using Jaxen.
47   */
48  public class JaxenFilter implements Filter, MuleContextAware
49  {
50      protected transient Log logger = LogFactory.getLog(getClass());
51  
52      private String pattern;
53      private String expectedValue;
54      private Map namespaces = null;
55      private Map contextProperties = null;
56      private AbstractFactory factory;
57  
58      private MuleContext muleContext;
59      private NamespaceManager namespaceManager;
60  
61      public JaxenFilter()
62      {
63          super();
64      }
65  
66      public JaxenFilter(String pattern)
67      {
68          this.pattern = pattern;
69      }
70  
71      public JaxenFilter(String pattern, String expectedValue)
72      {
73          this.pattern = pattern;
74          this.expectedValue = expectedValue;
75      }
76  
77  
78      public void setMuleContext(MuleContext context)
79      {
80          this.muleContext = context;
81          try
82          {
83              namespaceManager = muleContext.getRegistry().lookupObject(NamespaceManager.class);
84          }
85          catch (RegistrationException e)
86          {
87              throw new ExpressionRuntimeException(CoreMessages.failedToLoad("NamespaceManager"), e);
88          }
89          if(namespaceManager!=null)
90          {
91              if(namespaces == null)
92              {
93                  namespaces = new HashMap(namespaceManager.getNamespaces());
94              }
95              else
96              {
97                  namespaces.putAll(namespaceManager.getNamespaces());
98              }
99          }
100     }
101 
102     public boolean accept(MuleMessage obj)
103     {
104         Object payload = obj.getPayload();
105         
106         try 
107         {
108             // Ensure that we have an object we can run an XPath on
109             if (payload instanceof DOMSource)
110             {
111                 accept(((DOMSource) payload).getNode());
112             }
113             else if (payload instanceof byte[] 
114                      || payload instanceof InputStream 
115                      || payload instanceof String)
116             {
117                 try
118                 {
119                     return accept(obj.getPayload(DataTypeFactory.create(org.w3c.dom.Document.class)));
120                 }
121                 catch (Exception e)
122                 {
123                     logger.warn("JaxenPath filter rejected message because it could not convert from " 
124                             + payload.getClass() 
125                             + " to Source: "+ e.getMessage(), e);
126                     return false;
127                 }
128             }
129         
130             return accept(payload);
131         }
132         catch (JaxenException e) 
133         {
134             logger.warn("JaxenPath filter rejected message because it could not build/evaluate the XPath expression.", e);
135             return false;
136         }
137     }
138 
139     private boolean accept(Object obj) throws JaxenException
140     {
141         if (obj == null)
142         {
143             logger.warn("Applying JaxenFilter to null object.");
144             return false;
145         }
146         if (pattern == null)
147         {
148             logger.warn("Expression for JaxenFilter is not set.");
149             return false;
150         }
151         if (expectedValue == null)
152         {
153             // Handle the special case where the expected value really is null.
154             if (pattern.endsWith("= null") || pattern.endsWith("=null"))
155             {
156                 expectedValue = "null";
157                 pattern = pattern.substring(0, pattern.lastIndexOf("="));
158             }
159             else
160             {
161                 if (logger.isInfoEnabled())
162                 {
163                     logger.info("Expected value for JaxenFilter is not set, using 'true' by default");
164                 }
165                 expectedValue = Boolean.TRUE.toString();
166             }
167         }
168 
169         Object xpathResult = null;
170         boolean accept = false;
171 
172         Document dom4jDoc;
173         try
174         {
175             dom4jDoc = XMLUtils.toDocument(obj, muleContext);
176         }
177         catch (Exception e)
178         {
179             throw new JaxenException(e);
180         }
181         
182         // Payload is a DOM Document
183         if (dom4jDoc != null)
184         {
185             xpathResult = getDom4jXPath().stringValueOf(dom4jDoc);
186         }
187         // Payload is a W3C Document
188         else if (obj instanceof DOMSource)
189         {
190             xpathResult = getDOMXPath().stringValueOf(obj);
191         }
192         // Payload is a W3C Document
193         else if (obj instanceof org.w3c.dom.Document)
194         {
195             xpathResult = getDOMXPath().stringValueOf(obj);
196         }
197         // Payload is a Java object
198         else
199         {
200             if (logger.isDebugEnabled())
201             {
202                 logger.debug("Passing object of type " + obj.getClass().getName() + " to JaxenContext");
203             }
204             xpathResult = getJavaBeanXPath().stringValueOf(obj);
205         }
206 
207         if (logger.isDebugEnabled())
208         {
209             logger.debug("JaxenFilter Expression result = '" + xpathResult + "' -  Expected value = '"
210                     + expectedValue + "'");
211         }
212         // Compare the XPath result with the expected result.
213         if (xpathResult != null)
214         {
215             accept = xpathResult.toString().equals(expectedValue);
216         }
217         else
218         {
219             // A null result was actually expected.
220             if (expectedValue.equals("null"))
221             {
222                 accept = true;
223             }
224             // A null result was not expected, something probably went wrong.
225             else
226             {
227                 logger.warn("JaxenFilter expression evaluates to null: " + pattern);
228             }
229         }
230 
231         if (logger.isDebugEnabled())
232         {
233             logger.debug("JaxenFilter accept object  : " + accept);
234         }
235 
236         return accept;
237     }
238 
239     protected DOMXPath getDOMXPath() throws JaxenException
240     {
241         DOMXPath xpath = new DOMXPath(pattern);
242         setupNamespaces(xpath);
243         return xpath;
244     }
245 
246     protected Dom4jXPath getDom4jXPath() throws JaxenException
247     {
248         Dom4jXPath xpath = new Dom4jXPath(pattern);
249         setupNamespaces(xpath);
250         return xpath;
251     }
252     
253     protected JavaBeanXPath getJavaBeanXPath() throws JaxenException
254     {
255         JavaBeanXPath xpath = new JavaBeanXPath(pattern);
256         setupNamespaces(xpath);
257         return xpath;
258     }
259 
260     private void setupNamespaces(BaseXPath xpath) throws JaxenException
261     {
262         if (namespaces != null) 
263         {
264             for (Iterator itr = namespaces.entrySet().iterator(); itr.hasNext();)
265             {
266                 Map.Entry entry = (Map.Entry) itr.next();
267                 
268                 xpath.addNamespace((String) entry.getKey(), (String) entry.getValue());
269             }
270         }
271     }
272 
273     /** @return XPath expression */
274     public String getPattern()
275     {
276         return pattern;
277     }
278 
279     /** @param pattern The XPath expression */
280     public void setPattern(String pattern)
281     {
282         this.pattern = pattern;
283     }
284 
285     /** @return The expected result value of the XPath expression */
286     public String getExpectedValue()
287     {
288         return expectedValue;
289     }
290 
291     /** Sets the expected result value of the XPath expression */
292     public void setExpectedValue(String expectedValue)
293     {
294         this.expectedValue = expectedValue;
295     }
296 
297     public Map getNamespaces()
298     {
299         return namespaces;
300     }
301 
302     public void setNamespaces(Map namespaces)
303     {
304         this.namespaces = namespaces;
305     }
306 
307     public Map getContextProperties()
308     {
309         return contextProperties;
310     }
311 
312     public void setContextProperties(Map contextProperties)
313     {
314         this.contextProperties = contextProperties;
315     }
316 
317     public AbstractFactory getFactory()
318     {
319         return factory;
320     }
321 
322     public void setFactory(AbstractFactory factory)
323     {
324         this.factory = factory;
325     }
326     public boolean equals(Object obj)
327     {
328         if (this == obj) return true;
329         if (obj == null || getClass() != obj.getClass()) return false;
330 
331         final JaxenFilter other = (JaxenFilter) obj;
332         return equal(expectedValue, other.expectedValue)
333             && equal(contextProperties, other.contextProperties)
334             && equal(namespaces, other.namespaces)
335             && equal(pattern, other.pattern);
336     }
337 
338     public int hashCode()
339     {
340         return hash(new Object[]{this.getClass(), expectedValue, contextProperties, namespaces, pattern});
341     }
342 }