View Javadoc

1   /*
2    * $Id: XPathFilter.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.lifecycle.Initialisable;
21  import org.mule.api.lifecycle.InitialisationException;
22  import org.mule.api.registry.RegistrationException;
23  import org.mule.api.routing.filter.Filter;
24  import org.mule.config.i18n.CoreMessages;
25  import org.mule.config.i18n.MessageFactory;
26  import org.mule.module.xml.util.NamespaceManager;
27  import org.mule.util.ClassUtils;
28  
29  import java.text.MessageFormat;
30  import java.util.Arrays;
31  import java.util.Collections;
32  import java.util.HashMap;
33  import java.util.Iterator;
34  import java.util.Map;
35  
36  import javax.xml.namespace.NamespaceContext;
37  import javax.xml.xpath.XPath;
38  import javax.xml.xpath.XPathConstants;
39  import javax.xml.xpath.XPathFactory;
40  
41  import org.apache.commons.logging.Log;
42  import org.apache.commons.logging.LogFactory;
43  import org.w3c.dom.Node;
44  
45  /**
46   */
47  public class XPathFilter extends AbstractJaxpFilter 
48      implements Filter, Initialisable, MuleContextAware
49  {
50  
51      protected transient Log logger = LogFactory.getLog(getClass());
52  
53      private String pattern;
54      private String expectedValue;
55      private XPath xpath;
56      private Map<String, String> prefixToNamespaceMap = null;
57  
58      private NamespaceManager namespaceManager;
59  
60      private MuleContext muleContext;
61  
62      public XPathFilter()
63      {
64      }
65  
66      public XPathFilter(String pattern)
67      {
68          this.pattern = pattern;
69      }
70  
71      public XPathFilter(String pattern, String expectedValue)
72      {
73          this.pattern = pattern;
74          this.expectedValue = expectedValue;
75      }
76  
77      public void setMuleContext(MuleContext context)
78      {
79          this.muleContext = context;
80      }
81  
82      public void initialise() throws InitialisationException
83      {
84          super.initialise();
85          
86          if (getXpath() == null)
87          {
88              setXpath(XPathFactory.newInstance().newXPath());
89          }
90  
91  
92          if (pattern == null)
93          {
94              throw new InitialisationException(
95                  MessageFactory.createStaticMessage("A pattern must be supplied to the " +
96                                                     ClassUtils.getSimpleName(getClass())),
97                  this);
98          }
99  
100         if (muleContext != null)
101         {
102             try
103             {
104                 namespaceManager = muleContext.getRegistry().lookupObject(NamespaceManager.class);
105             }
106             catch (RegistrationException e)
107             {
108                 throw new ExpressionRuntimeException(CoreMessages.failedToLoad("NamespaceManager"), e);
109             }
110 
111             if (namespaceManager != null)
112             {
113                 if (prefixToNamespaceMap == null)
114                 {
115                     prefixToNamespaceMap = new HashMap(namespaceManager.getNamespaces());
116                 }
117                 else
118                 {
119                     prefixToNamespaceMap.putAll(namespaceManager.getNamespaces());
120                 }
121             }
122         }
123         
124         final Map<String, String> prefixToNamespaceMap = this.prefixToNamespaceMap;
125         if (prefixToNamespaceMap != null)
126         {
127             getXpath().setNamespaceContext(new NamespaceContext()
128             {
129                 public String getNamespaceURI(String prefix)
130                 {
131                     return prefixToNamespaceMap.get(prefix);
132                 }
133 
134                 public String getPrefix(String namespaceURI)
135                 {
136 
137                     for (Map.Entry<String, String> entry : prefixToNamespaceMap.entrySet())
138                     {
139                         if (namespaceURI.equals(entry.getValue()))
140                         {
141                             return entry.getKey();
142                         }
143                     }
144 
145                     return null;
146                 }
147 
148                 public Iterator getPrefixes(String namespaceURI)
149                 {
150                     String prefix = getPrefix(namespaceURI);
151                     if (prefix == null)
152                     {
153                         return Collections.emptyList().iterator();
154                     }
155                     else
156                     {
157                         return Arrays.asList(prefix).iterator();
158                     }
159                 }
160             });
161         }
162 
163         if (logger.isInfoEnabled())
164         {
165             logger.info("XPath implementation: " + getXpath());
166             logger.info("DocumentBuilderFactory implementation: " + getDocumentBuilderFactory());
167         }
168     }
169 
170     public boolean accept(MuleMessage message)
171     {
172         Object payload = message.getPayload();
173         if (payload == null)
174         {
175             if (logger.isWarnEnabled())
176             {
177                 logger.warn("Applying " + ClassUtils.getSimpleName(getClass()) + " to null object.");
178             }
179             return false;
180         }
181         if (pattern == null)
182         {
183             if (logger.isWarnEnabled())
184             {
185                 logger.warn("Expression for " + ClassUtils.getSimpleName(getClass()) + " is not set.");
186             }
187             return false;
188         }
189         if (expectedValue == null)
190         {
191             // Handle the special case where the expected value really is null.
192             if (pattern.endsWith("= null") || pattern.endsWith("=null"))
193             {
194                 expectedValue = "null";
195                 pattern = pattern.substring(0, pattern.lastIndexOf("="));
196             }
197             else
198             {
199                 if (logger.isInfoEnabled())
200                 {
201                     logger.info("''expectedValue'' attribute for " + ClassUtils.getSimpleName(getClass()) +
202                                 " is not set, using 'true' by default");
203                 }
204                 expectedValue = Boolean.TRUE.toString();
205             }
206         }
207 
208         Node node;
209         try
210         {
211             node = toDOMNode(payload);
212         }
213         catch (Exception e)
214         {
215             if (logger.isWarnEnabled())
216             {
217                 logger.warn(ClassUtils.getSimpleName(getClass()) + " filter rejected message because of an error while parsing XML: "
218                             + e.getMessage(), e);
219             }
220             return false;
221         }
222 
223         message.setPayload(node);
224 
225         return accept(node);
226     }
227 
228     protected boolean accept(Node node)
229     {
230         Object xpathResult;
231         boolean accept = false;
232 
233         try
234         {
235             xpathResult = getXpath().evaluate(pattern, node, XPathConstants.STRING);
236         }
237         catch (Exception e)
238         {
239             if (logger.isWarnEnabled())
240             {
241                 logger.warn(
242                         ClassUtils.getSimpleName(getClass()) + " filter rejected message because of an error while evaluating the expression: "
243                         + e.getMessage(), e);
244             }
245             return false;
246         }
247 
248         if (logger.isDebugEnabled())
249         {
250             logger.debug(MessageFormat.format("{0} Expression result = ''{1}'' -  Expected value = ''{2}''",
251                                               ClassUtils.getSimpleName(getClass()), xpathResult, expectedValue));
252         }
253 
254         // Compare the XPath result with the expected result.
255         if (xpathResult != null && !"".equals(xpathResult))
256         {
257             accept = xpathResult.toString().equals(expectedValue);
258         }
259         else
260         {
261             // A null result was actually expected.
262             if ("null".equals(expectedValue))
263             {
264                 accept = true;
265             }
266             // A null result was not expected, something probably went wrong.
267             else
268             {
269                 if (logger.isDebugEnabled())
270                 {
271                     logger.debug(MessageFormat.format("{0} expression evaluates to null: {1}",
272                                                       ClassUtils.getSimpleName(getClass()), pattern));
273                 }
274             }
275         }
276 
277         if (logger.isDebugEnabled())
278         {
279             logger.debug(MessageFormat.format("{0} accept object  : {1}", ClassUtils.getSimpleName(getClass()), accept));
280         }
281 
282         return accept;
283     }
284 
285     /**
286      * @return XPath expression
287      */
288     public String getPattern()
289     {
290         return pattern;
291     }
292 
293     /**
294      * @param pattern The XPath expression
295      */
296     public void setPattern(String pattern)
297     {
298         this.pattern = pattern;
299     }
300 
301     /**
302      * @return The expected result value of the XPath expression
303      */
304     public String getExpectedValue()
305     {
306         return expectedValue;
307     }
308 
309     /**
310      * Sets the expected result value of the XPath expression
311      * 
312      * @param expectedValue The expected value.
313      */
314     public void setExpectedValue(String expectedValue)
315     {
316         this.expectedValue = expectedValue;
317     }
318 
319     /**
320      * The xpath object to use to evaluate the expression.
321      * 
322      * @return The xpath object to use to evaluate the expression.
323      */
324     public XPath getXpath()
325     {
326         return xpath;
327     }
328 
329     /**
330      * The xpath object to use to evaluate the expression.
331      * 
332      * @param xpath The xpath object to use to evaluate the expression.
333      */
334     public void setXpath(XPath xpath)
335     {
336         this.xpath = xpath;
337     }
338 
339 
340     /**
341      * The prefix-to-namespace map for the namespace context to be applied to the
342      * XPath evaluation.
343      * 
344      * @return The prefix-to-namespace map for the namespace context to be applied to
345      *         the XPath evaluation.
346      */
347     public Map<String, String> getNamespaces()
348     {
349         return prefixToNamespaceMap;
350     }
351 
352     /**
353      * The prefix-to-namespace map for the namespace context to be applied to the
354      * XPath evaluation.
355      * 
356      * @param prefixToNamespaceMap The prefix-to-namespace map for the namespace
357      *            context to be applied to the XPath evaluation.
358      */
359     public void setNamespaces(Map<String, String> prefixToNamespaceMap)
360     {
361         this.prefixToNamespaceMap = prefixToNamespaceMap;
362     }
363     
364     public boolean equals(Object obj)
365     {
366         if (this == obj) return true;
367         if (obj == null || getClass() != obj.getClass()) return false;
368 
369         final XPathFilter other = (XPathFilter) obj;
370         return equal(expectedValue, other.expectedValue)
371             && equal(prefixToNamespaceMap, other.prefixToNamespaceMap)
372             && equal(pattern, other.pattern);
373     }
374 
375     public int hashCode()
376     {
377         return hash(new Object[]{this.getClass(), expectedValue, prefixToNamespaceMap, pattern});
378     }
379 }