KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > mule > routing > outbound > FilteringXmlMessageSplitter


1 /*
2  * $Id: FilteringXmlMessageSplitter.java 3937 2006-11-20 16:04:25Z lajos $
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 MuleSource MPL
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.outbound;
12
13 import org.apache.commons.lang.exception.ExceptionUtils;
14 import org.dom4j.Document;
15 import org.dom4j.DocumentHelper;
16 import org.dom4j.Element;
17 import org.dom4j.Node;
18 import org.dom4j.XPath;
19 import org.dom4j.io.SAXReader;
20 import org.mule.impl.MuleMessage;
21 import org.mule.umo.UMOMessage;
22 import org.mule.umo.endpoint.UMOEndpoint;
23 import org.mule.util.IOUtils;
24 import org.mule.util.StringUtils;
25 import org.xml.sax.SAXException JavaDoc;
26
27 import java.io.InputStream JavaDoc;
28 import java.io.StringReader JavaDoc;
29 import java.util.ArrayList JavaDoc;
30 import java.util.HashMap JavaDoc;
31 import java.util.Iterator JavaDoc;
32 import java.util.List JavaDoc;
33 import java.util.Map JavaDoc;
34
35 /**
36  * <code>FilteringXmlMessageSplitter</code> will split a DOM4J document into nodes
37  * based on the "splitExpression" property. <p/> Optionally, you can specify a
38  * <code>namespaces</code> property map that contain prefix/namespace mappings.
39  * Mind if you have a default namespace declared you should map it to some namespace,
40  * and reference it in the <code>splitExpression</code> property. <p/> The splitter
41  * can optionally validate against an XML schema. By default schema validation is
42  * turned off. <p/> You may reference an external schema from the classpath by using
43  * the <code>externalSchemaLocation</code> property. <p/> Note that each part
44  * returned is actually returned as a new Document.
45  */

46 public class FilteringXmlMessageSplitter extends AbstractMessageSplitter
47 {
48     // xml parser feature names for optional XSD validation
49
private static final String JavaDoc APACHE_XML_FEATURES_VALIDATION_SCHEMA = "http://apache.org/xml/features/validation/schema";
50     private static final String JavaDoc APACHE_XML_FEATURES_VALIDATION_SCHEMA_FULL_CHECKING = "http://apache.org/xml/features/validation/schema-full-checking";
51
52     // JAXP property for specifying external XSD location
53
private static final String JavaDoc JAXP_PROPERTIES_SCHEMA_SOURCE = "http://java.sun.com/xml/jaxp/properties/schemaSource";
54
55     // JAXP properties for specifying external XSD language (as required by newer
56
// JAXP implementation)
57
private static final String JavaDoc JAXP_PROPERTIES_SCHEMA_LANGUAGE = "http://java.sun.com/xml/jaxp/properties/schemaLanguage";
58     private static final String JavaDoc JAXP_PROPERTIES_SCHEMA_LANGUAGE_VALUE = "http://www.w3.org/2001/XMLSchema";
59
60     protected static ThreadLocal JavaDoc properties = new ThreadLocal JavaDoc();
61     protected static ThreadLocal JavaDoc nodes = new ThreadLocal JavaDoc();
62     protected String JavaDoc splitExpression = "";
63     protected Map JavaDoc namespaces = null;
64     protected boolean validateSchema = false;
65     protected String JavaDoc externalSchemaLocation = "";
66
67     public void setSplitExpression(String JavaDoc splitExpression)
68     {
69         this.splitExpression = StringUtils.trimToEmpty(splitExpression);
70     }
71
72     public void setNamespaces(Map JavaDoc namespaces)
73     {
74         this.namespaces = namespaces;
75     }
76
77     public String JavaDoc getSplitExpression()
78     {
79         return splitExpression;
80     }
81
82     public boolean isValidateSchema()
83     {
84         return validateSchema;
85     }
86
87     public void setValidateSchema(boolean validateSchema)
88     {
89         this.validateSchema = validateSchema;
90     }
91
92     public String JavaDoc getExternalSchemaLocation()
93     {
94         return externalSchemaLocation;
95     }
96
97     /**
98      * Set classpath location of the XSD to check against. If the resource cannot be
99      * found, an exception will be thrown at runtime.
100      *
101      * @param externalSchemaLocation location of XSD
102      */

103     public void setExternalSchemaLocation(String JavaDoc externalSchemaLocation)
104     {
105         this.externalSchemaLocation = externalSchemaLocation;
106     }
107
108     /**
109      * Template method can be used to split the message up before the getMessagePart
110      * method is called .
111      *
112      * @param message the message being routed
113      */

114     protected void initialise(UMOMessage message)
115     {
116         if (logger.isDebugEnabled())
117         {
118             if (splitExpression.length() == 0)
119             {
120                 logger.warn("splitExpression is not specified, no processing will take place");
121             }
122             else
123             {
124                 logger.debug("splitExpression is " + splitExpression);
125             }
126         }
127
128         Object JavaDoc src = message.getPayload();
129
130         try
131         {
132             if (src instanceof byte[])
133             {
134                 src = new String JavaDoc((byte[])src);
135             }
136
137             Document dom4jDoc;
138
139             if (src instanceof String JavaDoc)
140             {
141                 String JavaDoc xml = (String JavaDoc)src;
142                 SAXReader reader = new SAXReader();
143                 setDoSchemaValidation(reader, isValidateSchema());
144
145                 InputStream JavaDoc xsdAsStream = IOUtils.getResourceAsStream(getExternalSchemaLocation(), getClass());
146                 if (xsdAsStream == null)
147                 {
148                     throw new IllegalArgumentException JavaDoc("Couldn't find schema at "
149                                                        + getExternalSchemaLocation());
150                 }
151
152                 // Set schema language property (must be done before the schemaSource
153
// is set)
154
reader.setProperty(JAXP_PROPERTIES_SCHEMA_LANGUAGE, JAXP_PROPERTIES_SCHEMA_LANGUAGE_VALUE);
155
156                 // Need this one to map schemaLocation to a physical location
157
reader.setProperty(JAXP_PROPERTIES_SCHEMA_SOURCE, xsdAsStream);
158
159                 dom4jDoc = reader.read(new StringReader JavaDoc(xml));
160             }
161             else if (src instanceof org.dom4j.Document)
162             {
163                 dom4jDoc = (org.dom4j.Document)src;
164             }
165             else
166             {
167                 logger.error("Non-xml message payload: " + src.getClass().toString());
168                 return;
169             }
170
171             if (dom4jDoc != null)
172             {
173                 if (splitExpression.length() > 0)
174                 {
175                     XPath xpath = dom4jDoc.createXPath(splitExpression);
176                     if (namespaces != null)
177                     {
178                         xpath.setNamespaceURIs(namespaces);
179                     }
180                     List JavaDoc foundNodes = xpath.selectNodes(dom4jDoc);
181                     if (logger.isDebugEnabled())
182                     {
183                         logger.debug("Split into " + foundNodes.size());
184                     }
185                     List JavaDoc parts = new ArrayList JavaDoc();
186                     // Rather than reparsing these when individual messages are
187
// created, lets do it now
188
// We can also avoid parsing the Xml again altogether
189
for (Iterator JavaDoc iterator = foundNodes.iterator(); iterator.hasNext();)
190                     {
191                         Node node = (Node)iterator.next();
192                         if (node instanceof Element)
193                         {
194                             // Can't do detach here just in case the source object
195
// was a document.
196
node = (Node)node.clone();
197                             parts.add(DocumentHelper.createDocument((Element)node));
198                         }
199                         else
200                         {
201                             logger.warn("Dcoument node: " + node.asXML()
202                                         + " is not an element and thus is not a valid part");
203                         }
204                     }
205                     FilteringXmlMessageSplitter.nodes.set(parts);
206                 }
207             }
208             else
209             {
210                 logger.warn("Unsupported message type, ignoring");
211             }
212         }
213         catch (Exception JavaDoc ex)
214         {
215             throw new IllegalArgumentException JavaDoc("Failed to initialise the payload: "
216                                                + ExceptionUtils.getStackTrace(ex));
217         }
218
219         Map JavaDoc theProperties = new HashMap JavaDoc();
220         for (Iterator JavaDoc iterator = message.getPropertyNames().iterator(); iterator.hasNext();)
221         {
222             String JavaDoc propertyKey = (String JavaDoc)iterator.next();
223             theProperties.put(propertyKey, message.getProperty(propertyKey));
224         }
225         properties.set(theProperties);
226     }
227
228     /**
229      * Retrieves a specific message part for the given endpoint. the message will
230      * then be routed via the provider.
231      *
232      * @param message the current message being processed
233      * @param endpoint the endpoint that will be used to route the resulting message
234      * part
235      * @return the message part to dispatch
236      */

237     protected UMOMessage getMessagePart(UMOMessage message, UMOEndpoint endpoint)
238     {
239         List JavaDoc nodes = (List JavaDoc)FilteringXmlMessageSplitter.nodes.get();
240
241         if (nodes == null)
242         {
243             logger.error("Error: nodes are null");
244             return null;
245         }
246
247         for (int i = 0; i < nodes.size(); i++)
248         {
249             Document doc = (Document)nodes.get(i);
250
251             try
252             {
253                 Map JavaDoc theProperties = (Map JavaDoc)properties.get();
254                 UMOMessage result = new MuleMessage(doc, new HashMap JavaDoc(theProperties));
255
256                 if (endpoint.getFilter() == null || endpoint.getFilter().accept(result))
257                 {
258                     if (logger.isDebugEnabled())
259                     {
260                         logger.debug("Endpoint filter matched for node " + i + " of " + nodes.size()
261                                      + ". Routing message over: " + endpoint.getEndpointURI().toString());
262                     }
263                     nodes.remove(i);
264                     return result;
265                 }
266                 else
267                 {
268                     if (logger.isDebugEnabled())
269                     {
270                         logger.debug("Endpoint filter did not match, returning null");
271                     }
272                 }
273             }
274             catch (Exception JavaDoc e)
275             {
276                 logger.error("Unable to create message for node at position " + i, e);
277                 return null;
278             }
279         }
280
281         return null;
282     }
283
284     protected void setDoSchemaValidation(SAXReader reader, boolean validate) throws SAXException JavaDoc
285     {
286         reader.setValidation(validate);
287         reader.setFeature(APACHE_XML_FEATURES_VALIDATION_SCHEMA, validate);
288         reader.setFeature(APACHE_XML_FEATURES_VALIDATION_SCHEMA_FULL_CHECKING, true);
289     }
290 }
291
Popular Tags