1
2
3
4
5
6
7
8
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
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
255 if (xpathResult != null && !"".equals(xpathResult))
256 {
257 accept = xpathResult.toString().equals(expectedValue);
258 }
259 else
260 {
261
262 if ("null".equals(expectedValue))
263 {
264 accept = true;
265 }
266
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
287
288 public String getPattern()
289 {
290 return pattern;
291 }
292
293
294
295
296 public void setPattern(String pattern)
297 {
298 this.pattern = pattern;
299 }
300
301
302
303
304 public String getExpectedValue()
305 {
306 return expectedValue;
307 }
308
309
310
311
312
313
314 public void setExpectedValue(String expectedValue)
315 {
316 this.expectedValue = expectedValue;
317 }
318
319
320
321
322
323
324 public XPath getXpath()
325 {
326 return xpath;
327 }
328
329
330
331
332
333
334 public void setXpath(XPath xpath)
335 {
336 this.xpath = xpath;
337 }
338
339
340
341
342
343
344
345
346
347 public Map<String, String> getNamespaces()
348 {
349 return prefixToNamespaceMap;
350 }
351
352
353
354
355
356
357
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 }