Coverage Report - org.mule.module.xml.routing.FilteringXmlMessageSplitter
 
Classes in this File Line Coverage Branch Coverage Complexity
FilteringXmlMessageSplitter
80%
66/82
62%
21/34
3.091
 
 1  
 /*
 2  
  * $Id: FilteringXmlMessageSplitter.java 12273 2008-07-10 13:25:32Z tcarlson $
 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 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.routing;
 12  
 
 13  
 import org.mule.DefaultMuleMessage;
 14  
 import org.mule.api.MuleMessage;
 15  
 import org.mule.api.endpoint.OutboundEndpoint;
 16  
 import org.mule.module.xml.util.XMLUtils;
 17  
 import org.mule.routing.outbound.AbstractMessageSplitter;
 18  
 import org.mule.util.ExceptionUtils;
 19  
 import org.mule.util.StringUtils;
 20  
 
 21  
 import java.util.Collections;
 22  
 import java.util.HashMap;
 23  
 import java.util.Iterator;
 24  
 import java.util.LinkedList;
 25  
 import java.util.List;
 26  
 import java.util.Map;
 27  
 
 28  
 import org.dom4j.Document;
 29  
 import org.dom4j.DocumentHelper;
 30  
 import org.dom4j.Element;
 31  
 import org.dom4j.Node;
 32  
 import org.dom4j.XPath;
 33  
 
 34  
 /**
 35  
  * <code>FilteringXmlMessageSplitter</code> will split a DOM4J document into nodes
 36  
  * based on the "splitExpression" property. <p/> Optionally, you can specify a
 37  
  * <code>namespaces</code> property map that contain prefix/namespace mappings.
 38  
  * Mind if you have a default namespace declared you should map it to some namespace,
 39  
  * and reference it in the <code>splitExpression</code> property. <p/> The splitter
 40  
  * can optionally validate against an XML schema. By default schema validation is
 41  
  * turned off. <p/> You may reference an external schema from the classpath by using
 42  
  * the <code>externalSchemaLocation</code> property. <p/> Note that each part
 43  
  * returned is actually returned as a new Document.
 44  
  */
 45  40
 public class FilteringXmlMessageSplitter extends AbstractMessageSplitter
 46  
 {
 47  40
     protected final ThreadLocal propertiesContext = new ThreadLocal();
 48  40
     protected final ThreadLocal nodesContext = new ThreadLocal();
 49  
 
 50  40
     protected volatile String splitExpression = "";
 51  40
     protected volatile Map namespaces = null;
 52  40
     protected volatile boolean validateSchema = false;
 53  40
     protected volatile String externalSchemaLocation = "";
 54  
 
 55  
     public void setSplitExpression(String splitExpression)
 56  
     {
 57  32
         this.splitExpression = StringUtils.trimToEmpty(splitExpression);
 58  32
     }
 59  
 
 60  
     public void setNamespaces(Map namespaces)
 61  
     {
 62  32
         this.namespaces = namespaces;
 63  32
     }
 64  
 
 65  
     public Map getNamespaces()
 66  
     {
 67  0
         return Collections.unmodifiableMap(namespaces);
 68  
     }
 69  
 
 70  
     public String getSplitExpression()
 71  
     {
 72  0
         return splitExpression;
 73  
     }
 74  
 
 75  
     public boolean isValidateSchema()
 76  
     {
 77  0
         return validateSchema;
 78  
     }
 79  
 
 80  
     public void setValidateSchema(boolean validateSchema)
 81  
     {
 82  38
         this.validateSchema = validateSchema;
 83  38
     }
 84  
 
 85  
     public String getExternalSchemaLocation()
 86  
     {
 87  60
         return externalSchemaLocation;
 88  
     }
 89  
 
 90  
     /**
 91  
      * Set classpath location of the XSD to check against. If the resource cannot be
 92  
      * found, an exception will be thrown at runtime.
 93  
      * 
 94  
      * @param externalSchemaLocation location of XSD
 95  
      */
 96  
     public void setExternalSchemaLocation(String externalSchemaLocation)
 97  
     {
 98  38
         this.externalSchemaLocation = externalSchemaLocation;
 99  38
     }
 100  
 
 101  
     /**
 102  
      * Template method can be used to split the message up before the getMessagePart
 103  
      * method is called .
 104  
      * 
 105  
      * @param message the message being routed
 106  
      */
 107  
     // @Override
 108  
     protected void initialise(MuleMessage message)
 109  
     {
 110  68
         if (logger.isDebugEnabled())
 111  
         {
 112  0
             if (splitExpression.length() == 0)
 113  
             {
 114  0
                 logger.warn("splitExpression is not specified, no processing will take place");
 115  
             }
 116  
             else
 117  
             {
 118  0
                 logger.debug("splitExpression is " + splitExpression);
 119  
             }
 120  
         }
 121  
 
 122  68
         Object src = message.getPayload();
 123  
 
 124  
         Document dom4jDoc;
 125  
         try
 126  
         {
 127  68
             if (validateSchema)
 128  
             {
 129  60
                 dom4jDoc = XMLUtils.toDocument(src, getExternalSchemaLocation());
 130  
             }
 131  
             else
 132  
             {
 133  8
                 dom4jDoc = XMLUtils.toDocument(src);
 134  
             }
 135  60
             if (dom4jDoc == null)
 136  
             {
 137  8
                 logger.error("Non-XML message payload: " + src.getClass().toString());
 138  8
                 return;
 139  
             }
 140  
         }
 141  8
         catch (Exception e)
 142  
         {
 143  8
             throw new IllegalArgumentException("Failed to initialise the payload: "
 144  
                 + ExceptionUtils.getStackTrace(e));
 145  52
         }
 146  
 
 147  52
         if (splitExpression.length() > 0)
 148  
         {
 149  52
             XPath xpath = dom4jDoc.createXPath(splitExpression);
 150  52
             if (namespaces != null)
 151  
             {
 152  52
                 xpath.setNamespaceURIs(namespaces);
 153  
             }
 154  
 
 155  52
             List foundNodes = xpath.selectNodes(dom4jDoc);
 156  52
             if (enableCorrelation != ENABLE_CORRELATION_NEVER)
 157  
             {
 158  48
                 message.setCorrelationGroupSize(foundNodes.size());
 159  
             }
 160  52
             if (logger.isDebugEnabled())
 161  
             {
 162  0
                 logger.debug("Split into " + foundNodes.size());
 163  
             }
 164  
 
 165  52
             List parts = new LinkedList();
 166  
             // Rather than reparsing these when individual messages are
 167  
             // created, lets do it now
 168  
             // We can also avoid parsing the Xml again altogether
 169  52
             for (Iterator iterator = foundNodes.iterator(); iterator.hasNext();)
 170  
             {
 171  128
                 Node node = (Node)iterator.next();
 172  128
                 if (node instanceof Element)
 173  
                 {
 174  
                     // Can't do detach here just in case the source object
 175  
                     // was a document.
 176  128
                     node = (Node)node.clone();
 177  128
                     parts.add(DocumentHelper.createDocument((Element)node));
 178  
                 }
 179  
                 else
 180  
                 {
 181  0
                     logger.warn("Dcoument node: " + node.asXML()
 182  
                                 + " is not an element and thus is not a valid part");
 183  
                 }
 184  128
             }
 185  52
             nodesContext.set(parts);
 186  
         }
 187  
 
 188  52
         Map theProperties = new HashMap();
 189  52
         for (Iterator iterator = message.getPropertyNames().iterator(); iterator.hasNext();)
 190  
         {
 191  48
             String propertyKey = (String)iterator.next();
 192  48
             theProperties.put(propertyKey, message.getProperty(propertyKey));
 193  48
         }
 194  52
         propertiesContext.set(theProperties);
 195  52
     }
 196  
 
 197  
     // @Override
 198  
     protected void cleanup()
 199  
     {
 200  64
         nodesContext.set(null);
 201  64
         propertiesContext.set(null);
 202  64
     }
 203  
     
 204  
     /**
 205  
      * Retrieves a specific message part for the given endpoint. the message will
 206  
      * then be routed via the provider.
 207  
      * 
 208  
      * @param message the current message being processed
 209  
      * @param endpoint the endpoint that will be used to route the resulting message
 210  
      *            part
 211  
      * @return the message part to dispatch
 212  
      */
 213  
     protected MuleMessage getMessagePart(MuleMessage message, OutboundEndpoint endpoint)
 214  
     {
 215  212
         List nodes = (List)nodesContext.get();
 216  
 
 217  212
         if (nodes == null)
 218  
         {
 219  12
             logger.error("Error: nodes are null");
 220  12
             return null;
 221  
         }
 222  
 
 223  200
         for (Iterator i = nodes.iterator(); i.hasNext();)
 224  
         {
 225  80
             Document doc = (Document)i.next();
 226  
 
 227  
             try
 228  
             {
 229  80
                 Map theProperties = (Map)propertiesContext.get();
 230  80
                 MuleMessage result = new DefaultMuleMessage(doc, new HashMap(theProperties));
 231  
 
 232  80
                 if (endpoint.getFilter() == null || endpoint.getFilter().accept(result))
 233  
                 {
 234  80
                     if (logger.isDebugEnabled())
 235  
                     {
 236  0
                         logger.debug("Endpoint filter matched for node " + i + " of " + nodes.size()
 237  
                                      + ". Routing message over: " + endpoint.getEndpointURI().toString());
 238  
                     }
 239  80
                     i.remove();
 240  80
                     return result;
 241  
                 }
 242  
                 else
 243  
                 {
 244  0
                     if (logger.isDebugEnabled())
 245  
                     {
 246  0
                         logger.debug("Endpoint filter did not match, returning null");
 247  
                     }
 248  
                 }
 249  
             }
 250  0
             catch (Exception e)
 251  
             {
 252  0
                 logger.error("Unable to create message for node at position " + i, e);
 253  0
                 return null;
 254  0
             }
 255  0
         }
 256  
 
 257  120
         return null;
 258  
     }
 259  
 }