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
022 import javax.servlet.jsp.el.ELException;
023 import javax.servlet.jsp.el.ExpressionEvaluator;
024 import javax.servlet.jsp.el.FunctionMapper;
025 import javax.servlet.jsp.el.VariableResolver;
026 import java.lang.reflect.Method;
027 import java.lang.reflect.Modifier;
028 import java.util.HashMap;
029 import java.util.Map;
030
031 /**
032 * JSP Expression Language Evaluator. <p/> It provides a more convenient way of using the JSP EL Evaluator.
033 */
034 public class ELEvaluator {
035
036 /**
037 * Provides functions and variables for the EL evaluator. <p/> All functions and variables in the context of an EL
038 * evaluator are accessible from EL expressions.
039 */
040 public static class Context implements VariableResolver, FunctionMapper {
041 private Map<String, Object> vars;
042 private Map<String, Method> functions;
043
044 /**
045 * Create an empty context.
046 */
047 public Context() {
048 vars = new HashMap<String, Object>();
049 functions = new HashMap<String, Method>();
050 }
051
052 /**
053 * Add variables to the context. <p/>
054 *
055 * @param vars variables to add to the context.
056 */
057 public void setVariables(Map<String, Object> vars) {
058 this.vars.putAll(vars);
059 }
060
061 /**
062 * Add a variable to the context. <p/>
063 *
064 * @param name variable name.
065 * @param value variable value.
066 */
067 public void setVariable(String name, Object value) {
068 vars.put(name, value);
069 }
070
071 /**
072 * Return a variable from the context. <p/>
073 *
074 * @param name variable name.
075 * @return the variable value.
076 */
077 public Object getVariable(String name) {
078 return vars.get(name);
079 }
080
081 /**
082 * Add a function to the context. <p/>
083 *
084 * @param prefix function prefix.
085 * @param functionName function name.
086 * @param method method that will be invoked for the function, it must be a static and public method.
087 */
088 public void addFunction(String prefix, String functionName, Method method) {
089 if ((method.getModifiers() & (Modifier.PUBLIC | Modifier.STATIC)) != (Modifier.PUBLIC | Modifier.STATIC)) {
090 throw new IllegalArgumentException(XLog.format("Method[{0}] must be public and static", method));
091 }
092 prefix = (prefix.length() > 0) ? prefix + ":" : "";
093 functions.put(prefix + functionName, method);
094 }
095
096 /**
097 * Resolve a variable name. Used by the EL evaluator implemenation. <p/>
098 *
099 * @param name variable name.
100 * @return the variable value.
101 * @throws ELException thrown if the variable is not defined in the context.
102 */
103 public Object resolveVariable(String name) throws ELException {
104 if (!vars.containsKey(name)) {
105 throw new ELException(XLog.format("variable [{0}] cannot be resolved", name));
106 }
107 return vars.get(name);
108 }
109
110 /**
111 * Resolve a function prefix:name. Used by the EL evaluator implementation. <p/>
112 *
113 * @param prefix function prefix.
114 * @param name function name.
115 * @return the method associated to the function.
116 */
117 public Method resolveFunction(String prefix, String name) {
118 if (prefix.length() > 0) {
119 name = prefix + ":" + name;
120 }
121 return functions.get(name);
122 }
123 }
124
125 private static ThreadLocal<ELEvaluator> current = new ThreadLocal<ELEvaluator>();
126
127 /**
128 * If within the scope of a EL evaluation call, it gives access to the ELEvaluator instance performing the EL
129 * evaluation. <p/> This is useful for EL function methods to get access to the variables of the Evaluator. Because
130 * of this, ELEvaluator variables can be used to pass context to EL function methods (which must be static methods).
131 * <p/>
132 *
133 * @return the ELEvaluator in scope, or <code>null</code> if none.
134 */
135 public static ELEvaluator getCurrent() {
136 return current.get();
137 }
138
139 private Context context;
140
141 private ExpressionEvaluator evaluator = new ExpressionEvaluatorImpl();
142
143 /**
144 * Creates an ELEvaluator with no functions and no variables defined.
145 */
146 public ELEvaluator() {
147 this(new Context());
148 }
149
150 /**
151 * Creates an ELEvaluator with the functions and variables defined in the given {@link ELEvaluator.Context}. <p/>
152 *
153 * @param context the ELSupport with functions and variables to be available for EL evalution.
154 */
155 public ELEvaluator(Context context) {
156 this.context = context;
157 }
158
159 /**
160 * Return the context with the functions and variables of the EL evaluator. <p/>
161 *
162 * @return the context.
163 */
164 public Context getContext() {
165 return context;
166 }
167
168 /**
169 * Convenience method that sets a variable in the EL evaluator context. <p/>
170 *
171 * @param name variable name.
172 * @param value variable value.
173 */
174 public void setVariable(String name, Object value) {
175 context.setVariable(name, value);
176 }
177
178 /**
179 * Convenience method that returns a variable from the EL evaluator context. <p/>
180 *
181 * @param name variable name.
182 * @return the variable value, <code>null</code> if not defined.
183 */
184 public Object getVariable(String name) {
185 return context.getVariable(name);
186 }
187
188 /**
189 * Evaluate an EL expression. <p/>
190 *
191 * @param expr EL expression to evaluate.
192 * @param clazz return type of the EL expression.
193 * @return the object the EL expression evaluated to.
194 * @throws Exception thrown if an EL function failed due to a transient error or EL expression could not be
195 * evaluated.
196 */
197 @SuppressWarnings({"unchecked", "deprecation"})
198 public <T> T evaluate(String expr, Class<T> clazz) throws Exception {
199 ELEvaluator existing = current.get();
200 try {
201 current.set(this);
202 return (T) evaluator.evaluate(expr, clazz, context, context);
203 }
204 catch (ELException ex) {
205 if (ex.getRootCause() instanceof Exception) {
206 throw (Exception) ex.getRootCause();
207 }
208 else {
209 throw ex;
210 }
211 }
212 finally {
213 current.set(existing);
214 }
215 }
216
217 }