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