Coverage Report - org.mule.expression.DefaultExpressionManager
 
Classes in this File Line Coverage Branch Coverage Complexity
DefaultExpressionManager
0%
0/132
0%
0/64
3.308
DefaultExpressionManager$1
0%
0/5
0%
0/2
3.308
DefaultExpressionManager$2
0%
0/7
0%
0/4
3.308
 
 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.expression;
 8  
 
 9  
 import org.mule.api.MuleContext;
 10  
 import org.mule.api.MuleMessage;
 11  
 import org.mule.api.context.MuleContextAware;
 12  
 import org.mule.api.expression.ExpressionEnricher;
 13  
 import org.mule.api.expression.ExpressionEvaluator;
 14  
 import org.mule.api.expression.ExpressionManager;
 15  
 import org.mule.api.expression.ExpressionRuntimeException;
 16  
 import org.mule.api.expression.InvalidExpressionException;
 17  
 import org.mule.api.expression.RequiredValueException;
 18  
 import org.mule.api.lifecycle.Disposable;
 19  
 import org.mule.config.i18n.CoreMessages;
 20  
 import org.mule.util.TemplateParser;
 21  
 
 22  
 import java.text.MessageFormat;
 23  
 import java.util.Iterator;
 24  
 
 25  
 import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap;
 26  
 import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentMap;
 27  
 import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicBoolean;
 28  
 
 29  
 import org.apache.commons.logging.Log;
 30  
 import org.apache.commons.logging.LogFactory;
 31  
 
 32  
 /**
 33  
  * Provides universal access for evaluating expressions embedded in Mule configurations, such  as Xml, Java,
 34  
  * scripting and annotations.
 35  
  * <p/>
 36  
  * Users can register or unregister {@link ExpressionEvaluator} through this interface.
 37  
  */
 38  0
 public class DefaultExpressionManager implements ExpressionManager, MuleContextAware
 39  
 {
 40  
 
 41  
     /**
 42  
      * logger used by this class
 43  
      */
 44  0
     protected static transient final Log logger = LogFactory.getLog(DefaultExpressionManager.class);
 45  
 
 46  
     // default style parser
 47  0
     private TemplateParser parser = TemplateParser.createMuleStyleParser();
 48  
 
 49  0
     private ConcurrentMap evaluators = new ConcurrentHashMap(8);
 50  0
     private ConcurrentMap enrichers = new ConcurrentHashMap(8);
 51  
 
 52  
     private MuleContext muleContext;
 53  
 
 54  
     public void setMuleContext(MuleContext context)
 55  
     {
 56  0
         this.muleContext = context;
 57  0
     }
 58  
 
 59  
     public void registerEvaluator(ExpressionEvaluator evaluator)
 60  
     {
 61  0
         if (evaluator == null)
 62  
         {
 63  0
             throw new IllegalArgumentException(CoreMessages.objectIsNull("evaluator").getMessage());
 64  
         }
 65  
 
 66  0
         final String name = evaluator.getName();
 67  
         // TODO MULE-3809 Eliminate duplicate evaluators registration
 68  0
         if (logger.isDebugEnabled())
 69  
         {
 70  0
             logger.debug("Evaluators already contain an object named '" + name + "'.  The previous object will be overwritten.");
 71  
         }
 72  0
         evaluators.put(evaluator.getName(), evaluator);
 73  0
     }
 74  
 
 75  
     public void registerEnricher(ExpressionEnricher enricher)
 76  
     {
 77  0
         if (enricher == null)
 78  
         {
 79  0
             throw new IllegalArgumentException(CoreMessages.objectIsNull("enricher").getMessage());
 80  
         }
 81  
 
 82  0
         final String name = enricher.getName();
 83  
         // TODO MULE-3809 Eliminate duplicate evaluators registration
 84  0
         if (logger.isDebugEnabled())
 85  
         {
 86  0
             logger.debug("Enrichers already contain an object named '" + name + "'.  The previous object will be overwritten.");
 87  
         }
 88  0
         enrichers.put(enricher.getName(), enricher);
 89  0
     }
 90  
 
 91  
     /**
 92  
      * Checks whether an evaluator is registered with the manager
 93  
      *
 94  
      * @param name the name of the expression evaluator
 95  
      * @return true if the evaluator is registered with the manager, false otherwise
 96  
      */
 97  
     public boolean isEvaluatorRegistered(String name)
 98  
     {
 99  0
         return evaluators.containsKey(name);
 100  
     }
 101  
     
 102  
     /**
 103  
      * Checks whether an enricher is registered with the manager
 104  
      *
 105  
      * @param name the name of the expression enricher
 106  
      * @return true if the enricher is registered with the manager, false otherwise
 107  
      */
 108  
     public boolean isEnricherRegistered(String name)
 109  
     {
 110  0
         return enrichers.containsKey(name);
 111  
     }
 112  
     
 113  
     /**
 114  
      * Removes the evaluator with the given name
 115  
      *
 116  
      * @param name the name of the evaluator to remove
 117  
      */
 118  
     public ExpressionEvaluator unregisterEvaluator(String name)
 119  
     {
 120  0
         if (name == null)
 121  
         {
 122  0
             return null;
 123  
         }
 124  
 
 125  0
         ExpressionEvaluator evaluator = (ExpressionEvaluator) evaluators.remove(name);
 126  0
         if (evaluator instanceof Disposable)
 127  
         {
 128  0
             ((Disposable) evaluator).dispose();
 129  
         }
 130  0
         return evaluator;
 131  
     }
 132  
     
 133  
     /**
 134  
      * Removes the evaluator with the given name
 135  
      *
 136  
      * @param name the name of the evaluator to remove
 137  
      */
 138  
     public ExpressionEnricher unregisterEnricher(String name)
 139  
     {
 140  0
         if (name == null)
 141  
         {
 142  0
             return null;
 143  
         }
 144  
 
 145  0
         ExpressionEnricher enricher = (ExpressionEnricher) enrichers.remove(name);
 146  0
         if (enricher instanceof Disposable)
 147  
         {
 148  0
             ((Disposable) enricher).dispose();
 149  
         }
 150  0
         return enricher;
 151  
     }
 152  
 
 153  
     /**
 154  
      * Evaluates the given expression.  The expression should be a single expression definition with or without
 155  
      * enclosing braces. i.e. "mule:serviceName" and "#[mule:serviceName]" are both valid. For situations where
 156  
      * one or more expressions need to be parsed within a single text, the {@link org.mule.api.expression.ExpressionManager#parse(String,org.mule.api.MuleMessage,boolean)}
 157  
      * method should be used since it will iterate through all expressions in a string.
 158  
      *
 159  
      * @param expression a single expression i.e. xpath://foo
 160  
      * @param message    the current message to process.  The expression will evaluata on the message.
 161  
      * @return the result of the evaluation. Expressions that return collection will return an empty collection, not null.
 162  
      * @throws ExpressionRuntimeException if the expression is invalid, or a null is found for the expression and
 163  
      *                                    'failIfNull is set to true.
 164  
      */
 165  
     public Object evaluate(String expression, MuleMessage message) throws ExpressionRuntimeException
 166  
     {
 167  0
         return evaluate(expression, message, false);
 168  
     }
 169  
 
 170  
     /**
 171  
      * Evaluates the given expression.  The expression should be a single expression definition with or without
 172  
      * enclosing braces. i.e. "mule:serviceName" and "#[mule:serviceName]" are both valid. For situations where
 173  
      * one or more expressions need to be parsed within a single text, the {@link org.mule.api.expression.ExpressionManager#parse(String,org.mule.api.MuleMessage,boolean)}
 174  
      * method should be used since it will iterate through all expressions in a string.
 175  
      *
 176  
      * @param expression a single expression i.e. xpath://foo
 177  
      * @param message    the current message to process.  The expression will evaluata on the message.
 178  
      * @param failIfNull determines if an exception should be thrown if expression could not be evaluated or returns
 179  
      *                   null.
 180  
      * @return the result of the evaluation.  Expressions that return collection will return an empty collection, not null.
 181  
      * @throws ExpressionRuntimeException if the expression is invalid, or a null is found for the expression and
 182  
      *                                    'failIfNull is set to true.
 183  
      */
 184  
     public Object evaluate(String expression, MuleMessage message, boolean failIfNull) throws ExpressionRuntimeException
 185  
     {
 186  
         String name;
 187  
 
 188  0
         if (expression == null)
 189  
         {
 190  0
             throw new IllegalArgumentException(CoreMessages.objectIsNull("expression").getMessage());
 191  
         }
 192  0
         if (expression.startsWith(DEFAULT_EXPRESSION_PREFIX))
 193  
         {
 194  0
             expression = expression.substring(2, expression.length() - 1);
 195  
         }
 196  0
         int i = expression.indexOf(":");
 197  0
         if (i > -1)
 198  
         {
 199  0
             name = expression.substring(0, i);
 200  0
             expression = expression.substring(i + DEFAULT_EXPRESSION_POSTFIX.length());
 201  
         }
 202  
         else
 203  
         {
 204  0
             name = expression;
 205  0
             expression = null;
 206  
         }
 207  0
         return evaluate(expression, name, message, failIfNull);
 208  
     }
 209  
 
 210  
 
 211  
     public void enrich(String expression, MuleMessage message, Object object)
 212  
         throws ExpressionRuntimeException
 213  
     {
 214  
         String enricherName;
 215  
 
 216  0
         if (expression == null)
 217  
         {
 218  0
             throw new IllegalArgumentException(CoreMessages.objectIsNull("expression").getMessage());
 219  
         }
 220  0
         if (expression.startsWith(DEFAULT_EXPRESSION_PREFIX))
 221  
         {
 222  0
             expression = expression.substring(2, expression.length() - 1);
 223  
         }
 224  0
         int i = expression.indexOf(":");
 225  0
         if (i > -1)
 226  
         {
 227  0
             enricherName = expression.substring(0, i);
 228  0
             expression = expression.substring(i + DEFAULT_EXPRESSION_POSTFIX.length());
 229  
         }
 230  
         else
 231  
         {
 232  0
             enricherName = expression;
 233  0
             expression = null;
 234  
         }
 235  0
         enrich(expression, enricherName, message, object);
 236  0
     }
 237  
 
 238  
     public void enrich(String expression, String enricherName, MuleMessage message, Object object)
 239  
     {
 240  0
         ExpressionEnricher enricher = (ExpressionEnricher) enrichers.get(enricherName);
 241  0
         if (enricher == null)
 242  
         {
 243  0
             throw new IllegalArgumentException(CoreMessages.expressionEnricherNotRegistered(enricherName)
 244  
                 .getMessage());
 245  
         }
 246  0
         enricher.enrich(expression, message, object);
 247  0
     }
 248  
 
 249  
     /**
 250  
      * Evaluates the given expression.  The expression should be a single expression definition with or without
 251  
      * enclosing braces. i.e. "mule:serviceName" and "#[mule:serviceName]" are both valid. For situations where
 252  
      * one or more expressions need to be parsed within a single text, the {@link org.mule.api.expression.ExpressionManager#parse(String,org.mule.api.MuleMessage,boolean)}
 253  
      * method should be used since it will iterate through all expressions in a string.
 254  
      *
 255  
      * @param expression a single expression i.e. xpath://foo
 256  
      * @param evaluator  the evaluator to use when executing the expression
 257  
      * @param message    the current message to process.  The expression will evaluata on the message.
 258  
      * @param failIfNull determines if an exception should be thrown if expression could not be evaluated or returns
 259  
      *                   null or if an exception should be thrown if an empty collection is returned.
 260  
      * @return the result of the evaluation. Expressions that return collection will return an empty collection, not null.
 261  
      * @throws ExpressionRuntimeException if the expression is invalid, or a null is found for the expression and
 262  
      *                                    'failIfNull is set to true.
 263  
      */
 264  
     public Object evaluate(String expression, String evaluator, MuleMessage message, boolean failIfNull) throws ExpressionRuntimeException
 265  
     {
 266  0
         ExpressionEvaluator extractor = (ExpressionEvaluator) evaluators.get(evaluator);
 267  0
         if (extractor == null)
 268  
         {
 269  0
             throw new IllegalArgumentException(CoreMessages.expressionEvaluatorNotRegistered(evaluator).getMessage());
 270  
         }
 271  0
         Object result = extractor.evaluate(expression, message);
 272  
         //TODO Handle empty collections || (result instanceof Collection && ((Collection)result).size()==0)
 273  0
         if (failIfNull && (result == null))
 274  
         {
 275  0
             throw new RequiredValueException(CoreMessages.expressionEvaluatorReturnedNull(evaluator, expression));
 276  
         }
 277  0
         if (logger.isDebugEnabled())
 278  
         {
 279  0
             logger.debug(MessageFormat.format("Result of expression: {0}:{1} is: {2}", evaluator, expression, result));
 280  
         }
 281  0
         return result;
 282  
     }
 283  
 
 284  
     public boolean evaluateBoolean(String expression, String evaluator, MuleMessage message)
 285  
         throws ExpressionRuntimeException
 286  
     {
 287  0
         return evaluateBoolean(expression, evaluator, message, false, false);
 288  
     }
 289  
 
 290  
     public boolean evaluateBoolean(String expression, MuleMessage message) throws ExpressionRuntimeException
 291  
     {
 292  0
         return evaluateBoolean(expression, message, false, false);
 293  
     }
 294  
 
 295  
     public boolean evaluateBoolean(String expression,
 296  
                                    String evaluator,
 297  
                                    MuleMessage message,
 298  
                                    boolean nullReturnsTrue,
 299  
                                    boolean nonBooleanReturnsTrue) throws ExpressionRuntimeException
 300  
     {
 301  
         try
 302  
         {
 303  0
             return resolveBoolean(evaluate(expression, evaluator, message, false), nullReturnsTrue,
 304  
                 nonBooleanReturnsTrue, expression);
 305  
         }
 306  0
         catch (RequiredValueException e)
 307  
         {
 308  0
             return nullReturnsTrue;
 309  
         }
 310  
     }
 311  
 
 312  
     public boolean evaluateBoolean(String expression,
 313  
                                    MuleMessage message,
 314  
                                    boolean nullReturnsTrue,
 315  
                                    boolean nonBooleanReturnsTrue) throws ExpressionRuntimeException
 316  
     {
 317  
         try
 318  
         {
 319  0
             return resolveBoolean(evaluate(expression, message, false), nullReturnsTrue,
 320  
                 nonBooleanReturnsTrue, expression);
 321  
         }
 322  0
         catch (RequiredValueException e)
 323  
         {
 324  0
             return nullReturnsTrue;
 325  
         }
 326  
     }
 327  
 
 328  
     protected boolean resolveBoolean(Object result,
 329  
                                      boolean nullReturnsTrue,
 330  
                                      boolean nonBooleanReturnsTrue,
 331  
                                      String expression)
 332  
     {
 333  0
         if (result == null)
 334  
         {
 335  0
             return nullReturnsTrue;
 336  
         }
 337  0
         else if (result instanceof Boolean)
 338  
         {
 339  0
             return (Boolean) result;
 340  
         }
 341  0
         else if (result instanceof String)
 342  
         {
 343  0
             if (result.toString().toLowerCase().equalsIgnoreCase("false"))
 344  
             {
 345  0
                 return false;
 346  
             }
 347  0
             else if (result.toString().toLowerCase().equalsIgnoreCase("true"))
 348  
             {
 349  0
                 return true;
 350  
             }
 351  
             else
 352  
             {
 353  0
                 return nonBooleanReturnsTrue;
 354  
             }
 355  
         }
 356  
         else
 357  
         {
 358  0
             logger.warn("Expression: " + expression + ", returned an non-boolean result. Returning: "
 359  
                         + nonBooleanReturnsTrue);
 360  0
             return nonBooleanReturnsTrue;
 361  
         }
 362  
     }
 363  
 
 364  
     /**
 365  
      * Evaluates expressions in a given string. This method will iterate through each expression and evaluate it. If
 366  
      * a user needs to evaluate a single expression they can use {@link org.mule.api.expression.ExpressionManager#evaluate(String,org.mule.api.MuleMessage,boolean)}.
 367  
      *
 368  
      * @param expression a single expression i.e. xpath://foo
 369  
      * @param message    the current message to process.  The expression will evaluata on the message.
 370  
      * @return the result of the evaluation. Expressions that return collection will return an empty collection, not null.
 371  
      * @throws org.mule.api.expression.ExpressionRuntimeException
 372  
      *          if the expression is invalid, or a null is found for the expression and
 373  
      *          'failIfNull is set to true.
 374  
      */
 375  
     public String parse(String expression, MuleMessage message) throws ExpressionRuntimeException
 376  
     {
 377  0
         return parse(expression, message, false);
 378  
     }
 379  
 
 380  
     /**
 381  
      * Evaluates expressions in a given string. This method will iterate through each expression and evaluate it. If
 382  
      * a user needs to evaluate a single expression they can use {@link org.mule.api.expression.ExpressionManager#evaluate(String,org.mule.api.MuleMessage,boolean)}.
 383  
      *
 384  
      * @param expression a single expression i.e. xpath://foo
 385  
      * @param message    the current message to process.  The expression will evaluata on the message.
 386  
      * @param failIfNull determines if an exception should be thrown if expression could not be evaluated or returns null.
 387  
      * @return the result of the evaluation. Expressions that return collection will return an empty collection, not null.
 388  
      * @throws ExpressionRuntimeException if the expression is invalid, or a null is found for the expression and
 389  
      *                                    'failIfNull is set to true.
 390  
      */
 391  
     public String parse(final String expression, final MuleMessage message, final boolean failIfNull) throws ExpressionRuntimeException
 392  
     {
 393  0
         return parser.parse(new TemplateParser.TemplateCallback()
 394  0
         {
 395  
             public Object match(String token)
 396  
             {
 397  0
                 Object result = evaluate(token, message, failIfNull);
 398  0
                 if (result instanceof MuleMessage)
 399  
                 {
 400  0
                     return ((MuleMessage) result).getPayload();
 401  
                 }
 402  
                 else
 403  
                 {
 404  0
                     return result;
 405  
                 }
 406  
             }
 407  
         }, expression);
 408  
     }
 409  
 
 410  
     /**
 411  
      * Clears all registered evaluators from the manager.
 412  
      */
 413  
     public synchronized void clearEvaluators()
 414  
     {
 415  0
         for (Iterator iterator = evaluators.values().iterator(); iterator.hasNext();)
 416  
         {
 417  0
             ExpressionEvaluator evaluator = (ExpressionEvaluator) iterator.next();
 418  0
             if (evaluator instanceof Disposable)
 419  
             {
 420  0
                 ((Disposable) evaluator).dispose();
 421  
             }
 422  0
         }
 423  0
         evaluators.clear();
 424  0
     }
 425  
     
 426  
     public void clearEnrichers()
 427  
     {
 428  0
         for (Iterator iterator = enrichers.values().iterator(); iterator.hasNext();)
 429  
         {
 430  0
             ExpressionEnricher enricher = (ExpressionEnricher) iterator.next();
 431  0
             if (enricher instanceof Disposable)
 432  
             {
 433  0
                 ((Disposable) enricher).dispose();
 434  
             }
 435  0
         }
 436  0
         enrichers.clear();
 437  0
     }
 438  
 
 439  
     public boolean isExpression(String string)
 440  
     {
 441  0
         return (string.contains(DEFAULT_EXPRESSION_PREFIX));
 442  
     }
 443  
 
 444  
     /**
 445  
      * Determines if the expression is valid or not.  This method will validate a single expression or
 446  
      * expressions embedded in a string.  the expression must be well formed i.e. #[bean:user]
 447  
      *
 448  
      * @param expression the expression to validate
 449  
      * @return true if the expression evaluator is recognised
 450  
      */
 451  
     public boolean isValidExpression(String expression)
 452  
     {
 453  
         try
 454  
         {
 455  0
             validateExpression(expression);
 456  0
             return true;
 457  
         }
 458  0
         catch (InvalidExpressionException e)
 459  
         {
 460  0
             logger.warn(e.getMessage());
 461  0
             return false;
 462  
         }
 463  
     }
 464  
 
 465  
     public void validateExpression(String expression) throws InvalidExpressionException
 466  
     {
 467  0
         if (!muleContext.getConfiguration().isValidateExpressions())
 468  
         {
 469  0
             if (logger.isDebugEnabled()) {
 470  0
                 logger.debug("Validate expressions is turned off, no checking done for: " + expression);
 471  
             }
 472  0
             return;
 473  
         }
 474  
         try
 475  
         {
 476  0
             parser.validate(expression);
 477  
         }
 478  0
         catch (IllegalArgumentException e)
 479  
         {
 480  0
             throw new InvalidExpressionException(expression, e.getMessage());
 481  0
         }
 482  
 
 483  0
         final AtomicBoolean valid = new AtomicBoolean(true);
 484  0
         final AtomicBoolean match = new AtomicBoolean(false);
 485  0
         final StringBuffer message = new StringBuffer();
 486  0
         parser.parse(new TemplateParser.TemplateCallback()
 487  0
         {
 488  
             public Object match(String token)
 489  
             {
 490  0
                 match.set(true);
 491  0
                 if (token.indexOf(":") == -1)
 492  
                 {
 493  0
                     if (valid.get())
 494  
                     {
 495  0
                         valid.compareAndSet(true, false);
 496  
                     }
 497  0
                     message.append(token).append(" is invalid\n");
 498  
                 }
 499  0
                 return null;
 500  
             }
 501  
         }, expression);
 502  
 
 503  0
         if (message.length() > 0)
 504  
         {
 505  0
             throw new InvalidExpressionException(expression, message.toString());
 506  
         }
 507  0
         else if(!match.get())
 508  
         {
 509  0
             throw new InvalidExpressionException(expression, "Expression string is not an expression.  Use isExpression(String) to validate first");
 510  
         }
 511  0
     }
 512  
 
 513  
 }