Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
DefaultExpressionManager |
|
| 3.0;3 | ||||
DefaultExpressionManager$1 |
|
| 3.0;3 | ||||
DefaultExpressionManager$2 |
|
| 3.0;3 |
1 | /* | |
2 | * $Id: DefaultExpressionManager.java 19500 2010-09-09 17:29:17Z dzapata $ | |
3 | * -------------------------------------------------------------------------------------- | |
4 | * Copyright (c) MuleSoft, Inc. All rights reserved. http://www.mulesoft.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 | package org.mule.expression; | |
11 | ||
12 | import org.mule.api.MuleContext; | |
13 | import org.mule.api.MuleMessage; | |
14 | import org.mule.api.context.MuleContextAware; | |
15 | import org.mule.api.expression.ExpressionEvaluator; | |
16 | import org.mule.api.expression.ExpressionManager; | |
17 | import org.mule.api.expression.ExpressionRuntimeException; | |
18 | import org.mule.api.expression.InvalidExpressionException; | |
19 | import org.mule.api.expression.RequiredValueException; | |
20 | import org.mule.api.lifecycle.Disposable; | |
21 | import org.mule.config.i18n.CoreMessages; | |
22 | import org.mule.util.TemplateParser; | |
23 | ||
24 | import java.text.MessageFormat; | |
25 | import java.util.Iterator; | |
26 | ||
27 | import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap; | |
28 | import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentMap; | |
29 | import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicBoolean; | |
30 | import org.apache.commons.logging.Log; | |
31 | import org.apache.commons.logging.LogFactory; | |
32 | ||
33 | /** | |
34 | * Provides universal access for evaluating expressions embedded in Mule configurations, such as Xml, Java, | |
35 | * scripting and annotations. | |
36 | * <p/> | |
37 | * Users can register or unregister {@link ExpressionEvaluator} through this interface. | |
38 | */ | |
39 | 0 | public class DefaultExpressionManager implements ExpressionManager, MuleContextAware |
40 | { | |
41 | ||
42 | /** | |
43 | * logger used by this class | |
44 | */ | |
45 | 0 | protected static transient final Log logger = LogFactory.getLog(DefaultExpressionManager.class); |
46 | ||
47 | // default style parser | |
48 | 0 | private TemplateParser parser = TemplateParser.createMuleStyleParser(); |
49 | ||
50 | 0 | private ConcurrentMap evaluators = 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 | /** | |
76 | * Checks whether an evaluator is registered with the manager | |
77 | * | |
78 | * @param name the name of the expression evaluator | |
79 | * @return true if the evaluator is registered with the manager, false otherwise | |
80 | */ | |
81 | public boolean isEvaluatorRegistered(String name) | |
82 | { | |
83 | 0 | return evaluators.containsKey(name); |
84 | } | |
85 | ||
86 | /** | |
87 | * Removes the evaluator with the given name | |
88 | * | |
89 | * @param name the name of the evaluator to remove | |
90 | */ | |
91 | public ExpressionEvaluator unregisterEvaluator(String name) | |
92 | { | |
93 | 0 | if (name == null) |
94 | { | |
95 | 0 | return null; |
96 | } | |
97 | ||
98 | 0 | ExpressionEvaluator evaluator = (ExpressionEvaluator) evaluators.remove(name); |
99 | 0 | if (evaluator instanceof Disposable) |
100 | { | |
101 | 0 | ((Disposable) evaluator).dispose(); |
102 | } | |
103 | 0 | return evaluator; |
104 | } | |
105 | ||
106 | /** | |
107 | * Evaluates the given expression. The expression should be a single expression definition with or without | |
108 | * enclosing braces. i.e. "mule:serviceName" and "#[mule:serviceName]" are both valid. For situations where | |
109 | * 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)} | |
110 | * method should be used since it will iterate through all expressions in a string. | |
111 | * | |
112 | * @param expression a single expression i.e. xpath://foo | |
113 | * @param message the current message to process. The expression will evaluata on the message. | |
114 | * @return the result of the evaluation. Expressions that return collection will return an empty collection, not null. | |
115 | * @throws ExpressionRuntimeException if the expression is invalid, or a null is found for the expression and | |
116 | * 'failIfNull is set to true. | |
117 | */ | |
118 | public Object evaluate(String expression, MuleMessage message) throws ExpressionRuntimeException | |
119 | { | |
120 | 0 | return evaluate(expression, message, false); |
121 | } | |
122 | ||
123 | /** | |
124 | * Evaluates the given expression. The expression should be a single expression definition with or without | |
125 | * enclosing braces. i.e. "mule:serviceName" and "#[mule:serviceName]" are both valid. For situations where | |
126 | * 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)} | |
127 | * method should be used since it will iterate through all expressions in a string. | |
128 | * | |
129 | * @param expression a single expression i.e. xpath://foo | |
130 | * @param message the current message to process. The expression will evaluata on the message. | |
131 | * @param failIfNull determines if an exception should be thrown if expression could not be evaluated or returns | |
132 | * null. | |
133 | * @return the result of the evaluation. Expressions that return collection will return an empty collection, not null. | |
134 | * @throws ExpressionRuntimeException if the expression is invalid, or a null is found for the expression and | |
135 | * 'failIfNull is set to true. | |
136 | */ | |
137 | public Object evaluate(String expression, MuleMessage message, boolean failIfNull) throws ExpressionRuntimeException | |
138 | { | |
139 | String name; | |
140 | ||
141 | 0 | if (expression == null) |
142 | { | |
143 | 0 | throw new IllegalArgumentException(CoreMessages.objectIsNull("expression").getMessage()); |
144 | } | |
145 | 0 | if (expression.startsWith(DEFAULT_EXPRESSION_PREFIX)) |
146 | { | |
147 | 0 | expression = expression.substring(2, expression.length() - 1); |
148 | } | |
149 | 0 | int i = expression.indexOf(":"); |
150 | 0 | if (i > -1) |
151 | { | |
152 | 0 | name = expression.substring(0, i); |
153 | 0 | expression = expression.substring(i + DEFAULT_EXPRESSION_POSTFIX.length()); |
154 | } | |
155 | else | |
156 | { | |
157 | 0 | name = expression; |
158 | 0 | expression = null; |
159 | } | |
160 | 0 | return evaluate(expression, name, message, failIfNull); |
161 | } | |
162 | ||
163 | /** | |
164 | * Evaluates the given expression. The expression should be a single expression definition with or without | |
165 | * enclosing braces. i.e. "mule:serviceName" and "#[mule:serviceName]" are both valid. For situations where | |
166 | * 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)} | |
167 | * method should be used since it will iterate through all expressions in a string. | |
168 | * | |
169 | * @param expression a single expression i.e. xpath://foo | |
170 | * @param evaluator the evaluator to use when executing the expression | |
171 | * @param message the current message to process. The expression will evaluata on the message. | |
172 | * @param failIfNull determines if an exception should be thrown if expression could not be evaluated or returns | |
173 | * null or if an exception should be thrown if an empty collection is returned. | |
174 | * @return the result of the evaluation. Expressions that return collection will return an empty collection, not null. | |
175 | * @throws ExpressionRuntimeException if the expression is invalid, or a null is found for the expression and | |
176 | * 'failIfNull is set to true. | |
177 | */ | |
178 | public Object evaluate(String expression, String evaluator, MuleMessage message, boolean failIfNull) throws ExpressionRuntimeException | |
179 | { | |
180 | 0 | ExpressionEvaluator extractor = (ExpressionEvaluator) evaluators.get(evaluator); |
181 | 0 | if (extractor == null) |
182 | { | |
183 | 0 | throw new IllegalArgumentException(CoreMessages.expressionEvaluatorNotRegistered(evaluator).getMessage()); |
184 | } | |
185 | 0 | Object result = extractor.evaluate(expression, message); |
186 | //TODO Handle empty collections || (result instanceof Collection && ((Collection)result).size()==0) | |
187 | 0 | if (failIfNull && (result == null)) |
188 | { | |
189 | 0 | throw new RequiredValueException(CoreMessages.expressionEvaluatorReturnedNull(evaluator, expression)); |
190 | } | |
191 | 0 | if (logger.isDebugEnabled()) |
192 | { | |
193 | 0 | logger.debug(MessageFormat.format("Result of expression: {0}:{1} is: {2}", evaluator, expression, result)); |
194 | } | |
195 | 0 | return result; |
196 | } | |
197 | ||
198 | ||
199 | /** | |
200 | * Evaluates expressions in a given string. This method will iterate through each expression and evaluate it. If | |
201 | * a user needs to evaluate a single expression they can use {@link org.mule.api.expression.ExpressionManager#evaluate(String,org.mule.api.MuleMessage,boolean)}. | |
202 | * | |
203 | * @param expression a single expression i.e. xpath://foo | |
204 | * @param message the current message to process. The expression will evaluata on the message. | |
205 | * @return the result of the evaluation. Expressions that return collection will return an empty collection, not null. | |
206 | * @throws org.mule.api.expression.ExpressionRuntimeException | |
207 | * if the expression is invalid, or a null is found for the expression and | |
208 | * 'failIfNull is set to true. | |
209 | */ | |
210 | public String parse(String expression, MuleMessage message) throws ExpressionRuntimeException | |
211 | { | |
212 | 0 | return parse(expression, message, false); |
213 | } | |
214 | ||
215 | /** | |
216 | * Evaluates expressions in a given string. This method will iterate through each expression and evaluate it. If | |
217 | * a user needs to evaluate a single expression they can use {@link org.mule.api.expression.ExpressionManager#evaluate(String,org.mule.api.MuleMessage,boolean)}. | |
218 | * | |
219 | * @param expression a single expression i.e. xpath://foo | |
220 | * @param message the current message to process. The expression will evaluata on the message. | |
221 | * @param failIfNull determines if an exception should be thrown if expression could not be evaluated or returns null. | |
222 | * @return the result of the evaluation. Expressions that return collection will return an empty collection, not null. | |
223 | * @throws ExpressionRuntimeException if the expression is invalid, or a null is found for the expression and | |
224 | * 'failIfNull is set to true. | |
225 | */ | |
226 | public String parse(final String expression, final MuleMessage message, final boolean failIfNull) throws ExpressionRuntimeException | |
227 | { | |
228 | 0 | return parser.parse(new TemplateParser.TemplateCallback() |
229 | 0 | { |
230 | public Object match(String token) | |
231 | { | |
232 | 0 | return evaluate(token, message, failIfNull); |
233 | } | |
234 | }, expression); | |
235 | } | |
236 | ||
237 | /** | |
238 | * Clears all registered evaluators from the manager. | |
239 | */ | |
240 | public synchronized void clearEvaluators() | |
241 | { | |
242 | 0 | for (Iterator iterator = evaluators.values().iterator(); iterator.hasNext();) |
243 | { | |
244 | 0 | ExpressionEvaluator evaluator = (ExpressionEvaluator) iterator.next(); |
245 | 0 | if (evaluator instanceof Disposable) |
246 | { | |
247 | 0 | ((Disposable) evaluator).dispose(); |
248 | } | |
249 | 0 | } |
250 | 0 | evaluators.clear(); |
251 | 0 | } |
252 | ||
253 | public boolean isExpression(String string) | |
254 | { | |
255 | 0 | return (string.contains(DEFAULT_EXPRESSION_PREFIX)); |
256 | } | |
257 | ||
258 | /** | |
259 | * Determines if the expression is valid or not. This method will validate a single expression or | |
260 | * expressions embedded in a string. the expression must be well formed i.e. #[bean:user] | |
261 | * | |
262 | * @param expression the expression to validate | |
263 | * @return true if the expression evaluator is recognised | |
264 | */ | |
265 | public boolean isValidExpression(String expression) | |
266 | { | |
267 | try | |
268 | { | |
269 | 0 | validateExpression(expression); |
270 | 0 | return true; |
271 | } | |
272 | 0 | catch (InvalidExpressionException e) |
273 | { | |
274 | 0 | logger.warn(e.getMessage()); |
275 | 0 | return false; |
276 | } | |
277 | } | |
278 | ||
279 | public void validateExpression(String expression) throws InvalidExpressionException | |
280 | { | |
281 | 0 | if (!muleContext.getConfiguration().isValidateExpressions()) |
282 | { | |
283 | 0 | if (logger.isDebugEnabled()) { |
284 | 0 | logger.debug("Validate expressions is turned off, no checking done for: " + expression); |
285 | } | |
286 | 0 | return; |
287 | } | |
288 | try | |
289 | { | |
290 | 0 | parser.validate(expression); |
291 | } | |
292 | 0 | catch (IllegalArgumentException e) |
293 | { | |
294 | 0 | throw new InvalidExpressionException(expression, e.getMessage()); |
295 | 0 | } |
296 | ||
297 | 0 | final AtomicBoolean valid = new AtomicBoolean(true); |
298 | 0 | final AtomicBoolean match = new AtomicBoolean(false); |
299 | 0 | final StringBuffer message = new StringBuffer(); |
300 | 0 | parser.parse(new TemplateParser.TemplateCallback() |
301 | 0 | { |
302 | public Object match(String token) | |
303 | { | |
304 | 0 | match.set(true); |
305 | 0 | if (token.indexOf(":") == -1) |
306 | { | |
307 | 0 | if (valid.get()) |
308 | { | |
309 | 0 | valid.compareAndSet(true, false); |
310 | } | |
311 | 0 | message.append(token).append(" is invalid\n"); |
312 | } | |
313 | 0 | return null; |
314 | } | |
315 | }, expression); | |
316 | ||
317 | 0 | if (message.length() > 0) |
318 | { | |
319 | 0 | throw new InvalidExpressionException(expression, message.toString()); |
320 | } | |
321 | 0 | else if(!match.get()) |
322 | { | |
323 | 0 | throw new InvalidExpressionException(expression, "Expression string is not an expression. Use isExpression(String) to validate first"); |
324 | } | |
325 | 0 | } |
326 | } |