Coverage Report - org.mule.module.xml.expression.AbstractXPathExpressionEvaluator
 
Classes in this File Line Coverage Branch Coverage Complexity
AbstractXPathExpressionEvaluator
0%
0/61
0%
0/16
0
AbstractXPathExpressionEvaluator$1
0%
0/7
0%
0/2
0
 
 1  
 /*
 2  
  * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
 3  
  * The software in this package is published under the terms of the CPAL v1.0
 4  
  * license, a copy of which has been included with this distribution in the
 5  
  * LICENSE.txt file.
 6  
  */
 7  
 package org.mule.module.xml.expression;
 8  
 
 9  
 import org.mule.api.MuleContext;
 10  
 import org.mule.api.MuleMessage;
 11  
 import org.mule.api.MuleRuntimeException;
 12  
 import org.mule.api.context.MuleContextAware;
 13  
 import org.mule.api.context.notification.MuleContextNotificationListener;
 14  
 import org.mule.api.expression.ExpressionEvaluator;
 15  
 import org.mule.api.expression.ExpressionRuntimeException;
 16  
 import org.mule.api.lifecycle.Disposable;
 17  
 import org.mule.api.lifecycle.Initialisable;
 18  
 import org.mule.api.lifecycle.InitialisationException;
 19  
 import org.mule.api.registry.RegistrationException;
 20  
 import org.mule.config.i18n.CoreMessages;
 21  
 import org.mule.context.notification.MuleContextNotification;
 22  
 import org.mule.module.xml.i18n.XmlMessages;
 23  
 import org.mule.module.xml.util.NamespaceManager;
 24  
 
 25  
 import java.util.ArrayList;
 26  
 import java.util.Iterator;
 27  
 import java.util.List;
 28  
 import java.util.Map;
 29  
 import java.util.WeakHashMap;
 30  
 
 31  
 import org.dom4j.Document;
 32  
 import org.jaxen.JaxenException;
 33  
 import org.jaxen.XPath;
 34  
 import org.mule.transformer.types.DataTypeFactory;
 35  
 
 36  
 /**
 37  
  * Provides a base class for XPath property extractors. The XPath engine used is jaxen (http://jaxen.org) which supports
 38  
  * XPath queries on other object models such as JavaBeans as well as Xml
 39  
  */
 40  0
 public abstract class AbstractXPathExpressionEvaluator implements ExpressionEvaluator, Initialisable, Disposable, MuleContextAware
 41  
 {
 42  0
     private Map<String, XPath> cache = new WeakHashMap<String, XPath>(8);
 43  
 
 44  
     private MuleContext muleContext;
 45  
     private NamespaceManager namespaceManager;
 46  
 
 47  
     public void setMuleContext(MuleContext context)
 48  
     {
 49  0
         this.muleContext = context;
 50  
 
 51  0
     }
 52  
 
 53  
     public void initialise() throws InitialisationException
 54  
     {
 55  
         try
 56  
         {
 57  
             /*
 58  
                 Workaround for standalone mode, when registry bootstrap order may be non-deterministic and lead
 59  
                 to failures on startup.
 60  
 
 61  
                 initialise() can't do any lookups as it will have spring create and init beans for things like
 62  
                 global endpoints, interfering with current lifecycle and leading to failure.
 63  
                 TODO AP/RM this will be solved by the @Inject annotation or phase, as discussed
 64  
              */
 65  0
             this.muleContext.registerListener(new MuleContextNotificationListener<MuleContextNotification>(){
 66  
 
 67  
                 public void onNotification(MuleContextNotification notification)
 68  
                 {
 69  
                     // CONTEXT_INITIALIZED fires too soon, before registry is inited, thus using this one
 70  0
                     if (MuleContextNotification.CONTEXT_STARTING == notification.getAction())
 71  
                     {
 72  
                         try
 73  
                         {
 74  0
                             namespaceManager = muleContext.getRegistry().lookupObject(NamespaceManager.class);
 75  
                         }
 76  0
                         catch (RegistrationException e)
 77  
                         {
 78  0
                             throw new RuntimeException(e);
 79  0
                         }
 80  
                     }
 81  0
                 }
 82  
             });
 83  
         }
 84  0
         catch (Throwable t)
 85  
         {
 86  0
             throw new InitialisationException(t, this);
 87  0
         }
 88  0
     }
 89  
 
 90  
     public void inject() throws Exception
 91  
     {
 92  
         try
 93  
         {
 94  0
             namespaceManager = muleContext.getRegistry().lookupObject(NamespaceManager.class);
 95  
         }
 96  0
         catch (RegistrationException e)
 97  
         {
 98  0
             throw new ExpressionRuntimeException(CoreMessages.failedToLoad("NamespaceManager"), e);
 99  0
         }
 100  0
     }
 101  
 
 102  
     /** {@inheritDoc} */
 103  
     public Object evaluate(String expression, MuleMessage message)
 104  
     {
 105  
         try
 106  
         {
 107  0
             Object payload = message.getPayload();
 108  
             //we need to convert to a Dom if its an XML string
 109  0
             if(payload instanceof String)
 110  
             {
 111  0
                 payload = message.getPayload(DataTypeFactory.create(Document.class));
 112  
             }
 113  
 
 114  
             List<?> result;
 115  
 
 116  
             /*  XPath context state is not thread safe so synchronization must be enforced when adding a new namespace and
 117  
                 on evaluation when the context is read.
 118  
              */
 119  0
             XPath xpath = getXPath(expression, payload);
 120  0
             synchronized (xpath)
 121  
             {
 122  0
                 result = xpath.selectNodes(payload);
 123  0
             }
 124  
 
 125  0
             result = extractResultsFromNodes(result);
 126  0
             if(result.size()==1)
 127  
             {
 128  0
                 return result.get(0);
 129  
             }
 130  0
             else if(result.size()==0)
 131  
             {
 132  0
                 return null;
 133  
             }
 134  
             else
 135  
             {
 136  0
                 return result;
 137  
             }
 138  
         }
 139  0
         catch (Exception e)
 140  
         {
 141  0
             throw new MuleRuntimeException(XmlMessages.failedToProcessXPath(expression), e);
 142  
         }
 143  
     }
 144  
 
 145  
     protected void addNamespaces(NamespaceManager manager, XPath xpath)
 146  
     {
 147  0
         for (Iterator<?> iterator = manager.getNamespaces().entrySet().iterator(); iterator.hasNext();)
 148  
         {
 149  0
             Map.Entry<?, ?> entry = (Map.Entry<?, ?>)iterator.next();
 150  
             try
 151  
             {
 152  0
                 xpath.addNamespace(entry.getKey().toString(), entry.getValue().toString());
 153  
             }
 154  0
             catch (JaxenException e)
 155  
             {
 156  0
                 throw new ExpressionRuntimeException(XmlMessages.failedToRegisterNamespace(entry.getKey().toString(), entry.getValue().toString()));
 157  0
             }
 158  0
         }
 159  0
     }
 160  
 
 161  
     /** {@inheritDoc} */
 162  
     public final void setName(String name)
 163  
     {
 164  0
         throw new UnsupportedOperationException("setName");
 165  
     }
 166  
 
 167  
     /*
 168  
         The cache is not thread safe, more than one instance of the same xpath can be created, it wouldn't be an
 169  
         issue in this case since one will eventually be selected for GC
 170  
         A ReadWriteLock could be used to guard the cache but it would add unnecessary complexity
 171  
      */
 172  
     protected XPath getXPath(String expression, Object object) throws JaxenException
 173  
     {
 174  0
         String xPathCacheKey = expression + getXPathClassName(object);
 175  0
         XPath xpath = cache.get(xPathCacheKey);
 176  0
         if(xpath==null)
 177  
         {
 178  0
             xpath = createXPath(expression, object);
 179  0
             synchronized (xpath)
 180  
             {
 181  0
                 if(namespaceManager!=null)
 182  
                 {
 183  0
                     addNamespaces(namespaceManager, xpath);
 184  
                 }
 185  0
             }
 186  0
             cache.put(xPathCacheKey, xpath);
 187  
         }
 188  0
         return xpath;
 189  
     }
 190  
 
 191  
     protected String getXPathClassName(Object object)
 192  
     {
 193  0
         return getClass().getName();
 194  
     }
 195  
 
 196  
     protected abstract XPath createXPath(String expression, Object object) throws JaxenException;
 197  
 
 198  
     protected List<?> extractResultsFromNodes(List<?> results)
 199  
     {
 200  0
         if (results == null)
 201  
         {
 202  0
             return null;
 203  
         }
 204  0
         List<Object> newResults = new ArrayList<Object>(results.size());
 205  0
         for (Object o : results)
 206  
         {
 207  0
             newResults.add(extractResultFromNode(o));
 208  
         }
 209  0
         return newResults;
 210  
     }
 211  
 
 212  
     /**
 213  
      * A lifecycle method where implementor should free up any resources. If an
 214  
      * exception is thrown it should just be logged and processing should continue.
 215  
      * This method should not throw Runtime exceptions.
 216  
      */
 217  
     public void dispose()
 218  
     {
 219  0
         cache.clear();
 220  0
     }
 221  
 
 222  
     public NamespaceManager getNamespaceManager()
 223  
     {
 224  0
         return namespaceManager;
 225  
     }
 226  
 
 227  
     public void setNamespaceManager(NamespaceManager namespaceManager)
 228  
     {
 229  0
         this.namespaceManager = namespaceManager;
 230  0
     }
 231  
 
 232  
     public MuleContext getMuleContext()
 233  
     {
 234  0
         return muleContext;
 235  
     }
 236  
 
 237  
     protected abstract Object extractResultFromNode(Object result);
 238  
 }