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 }