This project has retired. For details please refer to its
Attic page.
001 /**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018 package org.apache.oozie.util;
019
020 import org.apache.commons.el.ExpressionEvaluatorImpl;
021 import org.apache.commons.el.ExpressionString;
022
023 import javax.servlet.jsp.el.ELException;
024 import javax.servlet.jsp.el.ExpressionEvaluator;
025 import javax.servlet.jsp.el.FunctionMapper;
026 import javax.servlet.jsp.el.VariableResolver;
027 import java.lang.reflect.Method;
028 import java.lang.reflect.Modifier;
029 import java.util.HashMap;
030 import java.util.Map;
031
032 /**
033 * JSP Expression Language Evaluator. <p/> It provides a more convenient way of using the JSP EL Evaluator.
034 */
035 public class ELEvaluator {
036
037 /**
038 * Provides functions and variables for the EL evaluator. <p/> All functions and variables in the context of an EL
039 * evaluator are accessible from EL expressions.
040 */
041 public static class Context implements VariableResolver, FunctionMapper {
042 private Map<String, Object> vars;
043 private Map<String, Method> functions;
044
045 /**
046 * Create an empty context.
047 */
048 public Context() {
049 vars = new HashMap<String, Object>();
050 functions = new HashMap<String, Method>();
051 }
052
053 /**
054 * Add variables to the context. <p/>
055 *
056 * @param vars variables to add to the context.
057 */
058 public void setVariables(Map<String, Object> vars) {
059 this.vars.putAll(vars);
060 }
061
062 /**
063 * Add a variable to the context. <p/>
064 *
065 * @param name variable name.
066 * @param value variable value.
067 */
068 public void setVariable(String name, Object value) {
069 vars.put(name, value);
070 }
071
072 /**
073 * Return a variable from the context. <p/>
074 *
075 * @param name variable name.
076 * @return the variable value.
077 */
078 public Object getVariable(String name) {
079 return vars.get(name);
080 }
081
082 /**
083 * Add a function to the context. <p/>
084 *
085 * @param prefix function prefix.
086 * @param functionName function name.
087 * @param method method that will be invoked for the function, it must be a static and public method.
088 */
089 public void addFunction(String prefix, String functionName, Method method) {
090 if ((method.getModifiers() & (Modifier.PUBLIC | Modifier.STATIC)) != (Modifier.PUBLIC | Modifier.STATIC)) {
091 throw new IllegalArgumentException(XLog.format("Method[{0}] must be public and static", method));
092 }
093 prefix = (prefix.length() > 0) ? prefix + ":" : "";
094 functions.put(prefix + functionName, method);
095 }
096
097 /**
098 * Resolve a variable name. Used by the EL evaluator implemenation. <p/>
099 *
100 * @param name variable name.
101 * @return the variable value.
102 * @throws ELException thrown if the variable is not defined in the context.
103 */
104 public Object resolveVariable(String name) throws ELException {
105 if (!vars.containsKey(name)) {
106 throw new ELException(XLog.format("variable [{0}] cannot be resolved", name));
107 }
108 return vars.get(name);
109 }
110
111 /**
112 * Resolve a function prefix:name. Used by the EL evaluator implementation. <p/>
113 *
114 * @param prefix function prefix.
115 * @param name function name.
116 * @return the method associated to the function.
117 */
118 public Method resolveFunction(String prefix, String name) {
119 if (prefix.length() > 0) {
120 name = prefix + ":" + name;
121 }
122 return functions.get(name);
123 }
124 }
125
126 private static ThreadLocal<ELEvaluator> current = new ThreadLocal<ELEvaluator>();
127
128 /**
129 * If within the scope of a EL evaluation call, it gives access to the ELEvaluator instance performing the EL
130 * evaluation. <p/> This is useful for EL function methods to get access to the variables of the Evaluator. Because
131 * of this, ELEvaluator variables can be used to pass context to EL function methods (which must be static methods).
132 * <p/>
133 *
134 * @return the ELEvaluator in scope, or <code>null</code> if none.
135 */
136 public static ELEvaluator getCurrent() {
137 return current.get();
138 }
139
140 private Context context;
141
142 private ExpressionEvaluatorImpl evaluator = new ExpressionEvaluatorImpl();
143
144 /**
145 * Creates an ELEvaluator with no functions and no variables defined.
146 */
147 public ELEvaluator() {
148 this(new Context());
149 }
150
151 /**
152 * Creates an ELEvaluator with the functions and variables defined in the given {@link ELEvaluator.Context}. <p/>
153 *
154 * @param context the ELSupport with functions and variables to be available for EL evalution.
155 */
156 public ELEvaluator(Context context) {
157 this.context = context;
158 }
159
160 /**
161 * Return the context with the functions and variables of the EL evaluator. <p/>
162 *
163 * @return the context.
164 */
165 public Context getContext() {
166 return context;
167 }
168
169 /**
170 * Convenience method that sets a variable in the EL evaluator context. <p/>
171 *
172 * @param name variable name.
173 * @param value variable value.
174 */
175 public void setVariable(String name, Object value) {
176 context.setVariable(name, value);
177 }
178
179 /**
180 * Convenience method that returns a variable from the EL evaluator context. <p/>
181 *
182 * @param name variable name.
183 * @return the variable value, <code>null</code> if not defined.
184 */
185 public Object getVariable(String name) {
186 return context.getVariable(name);
187 }
188
189 /**
190 * Evaluate an EL expression. <p/>
191 *
192 * @param expr EL expression to evaluate.
193 * @param clazz return type of the EL expression.
194 * @return the object the EL expression evaluated to.
195 * @throws Exception thrown if an EL function failed due to a transient error or EL expression could not be
196 * evaluated.
197 */
198 @SuppressWarnings({"unchecked", "deprecation"})
199 public <T> T evaluate(String expr, Class<T> clazz) throws Exception {
200 ELEvaluator existing = current.get();
201 try {
202 current.set(this);
203 return (T) evaluator.evaluate(expr, clazz, context, context);
204 }
205 catch (ELException ex) {
206 if (ex.getRootCause() instanceof Exception) {
207 throw (Exception) ex.getRootCause();
208 }
209 else {
210 throw ex;
211 }
212 }
213 finally {
214 current.set(existing);
215 }
216 }
217
218 /**
219 * Check if the input expression contains sequence statically. for example
220 * identify if "," is present outside of a function invocation in the given
221 * expression. Ex "${func('abc')},${func('def'}",
222 *
223 * @param expr - Expression string
224 * @param sequence - char sequence to check in the input expression
225 * @return true if present
226 * @throws Exception Exception thrown if an EL function failed due to a
227 * transient error or EL expression could not be parsed
228 */
229 public boolean checkForExistence(String expr, String sequence)
230 throws Exception {
231 try {
232 Object exprString = evaluator.parseExpressionString(expr);
233 if (exprString instanceof ExpressionString) {
234 for (Object element : ((ExpressionString)exprString).getElements()) {
235 if (element instanceof String &&
236 element.toString().contains(sequence)) {
237 return true;
238 }
239 }
240 } else if (exprString instanceof String) {
241 if (((String)exprString).contains(sequence)) {
242 return true;
243 }
244 }
245 return false;
246 } catch (ELException ex) {
247 if (ex.getRootCause() instanceof Exception) {
248 throw (Exception) ex.getRootCause();
249 }
250 else {
251 throw ex;
252 }
253 }
254 }
255 }