1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.scxml2.env.jexl;
18
19 import java.io.Serializable;
20 import java.util.HashMap;
21 import java.util.Map;
22 import java.util.UUID;
23
24 import org.apache.commons.jexl2.Expression;
25 import org.apache.commons.jexl2.JexlEngine;
26 import org.apache.commons.jexl2.Script;
27 import org.apache.commons.scxml2.Context;
28 import org.apache.commons.scxml2.Evaluator;
29 import org.apache.commons.scxml2.EvaluatorProvider;
30 import org.apache.commons.scxml2.SCXMLExpressionException;
31 import org.apache.commons.scxml2.XPathBuiltin;
32 import org.apache.commons.scxml2.env.EffectiveContextMap;
33 import org.apache.commons.scxml2.model.SCXML;
34
35
36
37
38
39
40
41
42
43 public class JexlEvaluator implements Evaluator, Serializable {
44
45
46 private static final long serialVersionUID = 1L;
47
48
49
50
51 private static final String ASSIGN_VARIABLE_NAME = "a"+UUID.randomUUID().toString().replace('-','x');
52
53 public static final String SUPPORTED_DATA_MODEL = "jexl";
54
55 public static class JexlEvaluatorProvider implements EvaluatorProvider {
56
57 @Override
58 public String getSupportedDatamodel() {
59 return SUPPORTED_DATA_MODEL;
60 }
61
62 @Override
63 public Evaluator getEvaluator() {
64 return new JexlEvaluator();
65 }
66
67 @Override
68 public Evaluator getEvaluator(final SCXML document) {
69 return new JexlEvaluator();
70 }
71 }
72
73
74 private static final String ERR_CTX_TYPE = "Error evaluating JEXL "
75 + "expression, Context must be a org.apache.commons.scxml2.env.jexl.JexlContext";
76
77
78
79
80 private transient volatile JexlEngine jexlEngine;
81
82
83 private boolean jexlEngineSilent;
84
85 private boolean jexlEngineStrict;
86
87
88 public JexlEvaluator() {
89 super();
90
91 jexlEngine = createJexlEngine();
92 jexlEngineSilent = jexlEngine.isSilent();
93 jexlEngineStrict = jexlEngine.isStrict();
94 }
95
96
97
98
99
100 public boolean isJexlEngineSilent() {
101 return jexlEngineSilent;
102 }
103
104
105
106
107
108
109
110
111 public void setJexlEngineSilent(boolean silent) {
112 synchronized (this) {
113 JexlEngine engine = getJexlEngine();
114 engine.setSilent(silent);
115 this.jexlEngineSilent = silent;
116 }
117 }
118
119
120
121
122
123 public boolean isJexlEngineStrict() {
124 return jexlEngineStrict;
125 }
126
127
128
129
130
131
132
133 public void setJexlEngineStrict(boolean strict) {
134 synchronized (this) {
135 JexlEngine engine = getJexlEngine();
136 engine.setStrict(strict);
137 this.jexlEngineStrict = strict;
138 }
139 }
140
141 @Override
142 public String getSupportedDatamodel() {
143 return SUPPORTED_DATA_MODEL;
144 }
145
146
147
148
149
150
151
152
153
154
155 public Object eval(final Context ctx, final String expr)
156 throws SCXMLExpressionException {
157 if (expr == null) {
158 return null;
159 }
160 if (!(ctx instanceof JexlContext)) {
161 throw new SCXMLExpressionException(ERR_CTX_TYPE);
162 }
163 try {
164 final JexlContext effective = getEffectiveContext((JexlContext)ctx);
165 Expression exp = getJexlEngine().createExpression(expr);
166 return exp.evaluate(effective);
167 } catch (Exception e) {
168 String exMessage = e.getMessage() != null ? e.getMessage() : e.getClass().getCanonicalName();
169 throw new SCXMLExpressionException("eval('" + expr + "'): " + exMessage, e);
170 }
171 }
172
173
174
175
176 public Boolean evalCond(final Context ctx, final String expr)
177 throws SCXMLExpressionException {
178 if (expr == null) {
179 return null;
180 }
181 if (!(ctx instanceof JexlContext)) {
182 throw new SCXMLExpressionException(ERR_CTX_TYPE);
183 }
184 try {
185 final JexlContext effective = getEffectiveContext((JexlContext)ctx);
186 Expression exp = getJexlEngine().createExpression(expr);
187 final Object result = exp.evaluate(effective);
188 return result == null ? Boolean.FALSE : (Boolean)result;
189 } catch (Exception e) {
190 String exMessage = e.getMessage() != null ? e.getMessage() : e.getClass().getCanonicalName();
191 throw new SCXMLExpressionException("evalCond('" + expr + "'): " + exMessage, e);
192 }
193 }
194
195
196
197
198 public Object evalLocation(final Context ctx, final String expr)
199 throws SCXMLExpressionException {
200 if (expr == null) {
201 return null;
202 }
203 else if (ctx.has(expr)) {
204 return expr;
205 }
206
207 if (!(ctx instanceof JexlContext)) {
208 throw new SCXMLExpressionException(ERR_CTX_TYPE);
209 }
210 try {
211 final JexlContext effective = getEffectiveContext((JexlContext)ctx);
212 Expression exp = getJexlEngine().createExpression(expr);
213 return exp.evaluate(effective);
214 } catch (Exception e) {
215 String exMessage = e.getMessage() != null ? e.getMessage() : e.getClass().getCanonicalName();
216 throw new SCXMLExpressionException("evalLocation('" + expr + "'): " + exMessage, e);
217 }
218 }
219
220
221
222
223 public void evalAssign(final Context ctx, final String location, final Object data, final AssignType type,
224 final String attr) throws SCXMLExpressionException {
225
226 Object loc = evalLocation(ctx, location);
227 if (loc != null) {
228
229 if (XPathBuiltin.isXPathLocation(ctx, loc)) {
230 XPathBuiltin.assign(ctx, loc, data, type, attr);
231 }
232 else {
233 StringBuilder sb = new StringBuilder(location).append("=").append(ASSIGN_VARIABLE_NAME);
234 try {
235 ctx.getVars().put(ASSIGN_VARIABLE_NAME, data);
236 eval(ctx, sb.toString());
237 }
238 finally {
239 ctx.getVars().remove(ASSIGN_VARIABLE_NAME);
240 }
241 }
242 }
243 else {
244 throw new SCXMLExpressionException("evalAssign - cannot resolve location: '" + location + "'");
245 }
246 }
247
248
249
250
251 public Object evalScript(final Context ctx, final String script)
252 throws SCXMLExpressionException {
253 if (script == null) {
254 return null;
255 }
256 if (!(ctx instanceof JexlContext)) {
257 throw new SCXMLExpressionException(ERR_CTX_TYPE);
258 }
259 try {
260 final JexlContext effective = getEffectiveContext((JexlContext) ctx);
261 final Script jexlScript = getJexlEngine().createScript(script);
262 return jexlScript.execute(effective);
263 } catch (Exception e) {
264 String exMessage = e.getMessage() != null ? e.getMessage() : e.getClass().getCanonicalName();
265 throw new SCXMLExpressionException("evalScript('" + script + "'): " + exMessage, e);
266 }
267 }
268
269
270
271
272
273
274
275
276 public Context newContext(final Context parent) {
277 return new JexlContext(parent);
278 }
279
280
281
282
283
284
285
286 protected JexlEngine createJexlEngine() {
287 JexlEngine engine = new JexlEngine();
288
289
290 Map<String, Object> funcs = new HashMap<String, Object>();
291 funcs.put(null, JexlBuiltin.class);
292 engine.setFunctions(funcs);
293 engine.setCache(256);
294 return engine;
295 }
296
297
298
299
300
301
302
303
304
305 private JexlEngine getJexlEngine() {
306 JexlEngine engine = jexlEngine;
307 if (engine == null) {
308 synchronized (this) {
309 engine = jexlEngine;
310 if (engine == null) {
311 jexlEngine = engine = createJexlEngine();
312 jexlEngine.setSilent(jexlEngineSilent);
313 jexlEngine.setStrict(jexlEngineStrict);
314 }
315 }
316 }
317 return engine;
318 }
319
320
321
322
323
324
325
326
327
328
329 protected JexlContext getEffectiveContext(final JexlContext nodeCtx) {
330 return new JexlContext(nodeCtx, new EffectiveContextMap(nodeCtx));
331 }
332 }
333