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.service;
019
020 import org.apache.oozie.util.XLog;
021 import org.apache.oozie.util.ELEvaluator;
022 import org.apache.oozie.ErrorCode;
023 import org.apache.hadoop.conf.Configuration;
024
025 import java.lang.reflect.Field;
026 import java.lang.reflect.Method;
027 import java.lang.reflect.Modifier;
028 import java.util.ArrayList;
029 import java.util.HashMap;
030 import java.util.List;
031
032 /**
033 * The ELService creates {@link ELEvaluator} instances preconfigured with constants and functions defined in the
034 * configuration. <p/> The following configuration parameters control the EL service: <p/> {@link #CONF_CONSTANTS} list
035 * of constant definitions to be available for EL evaluations. <p/> {@link #CONF_FUNCTIONS} list of function definitions
036 * to be available for EL evalations. <p/> Definitions must be separated by a comma, definitions are trimmed. <p/> The
037 * syntax for a constant definition is <code>PREFIX:NAME=CLASS_NAME#CONSTANT_NAME</code>. <p/> The syntax for a constant
038 * definition is <code>PREFIX:NAME=CLASS_NAME#METHOD_NAME</code>.
039 */
040 public class ELService implements Service {
041
042 public static final String CONF_PREFIX = Service.CONF_PREFIX + "ELService.";
043
044 public static final String CONF_CONSTANTS = CONF_PREFIX + "constants.";
045
046 public static final String CONF_EXT_CONSTANTS = CONF_PREFIX + "ext.constants.";
047
048 public static final String CONF_FUNCTIONS = CONF_PREFIX + "functions.";
049
050 public static final String CONF_EXT_FUNCTIONS = CONF_PREFIX + "ext.functions.";
051
052 public static final String CONF_GROUPS = CONF_PREFIX + "groups";
053
054 private final XLog log = XLog.getLog(getClass());
055
056 //<Group Name>, <List of constants>
057 private HashMap<String, List<ELConstant>> constants;
058 //<Group Name>, <List of functions>
059 private HashMap<String, List<ELFunction>> functions;
060
061 private static class ELConstant {
062 private String name;
063 private Object value;
064
065 private ELConstant(String prefix, String name, Object value) {
066 if (prefix.length() > 0) {
067 name = prefix + ":" + name;
068 }
069 this.name = name;
070 this.value = value;
071 }
072 }
073
074 private static class ELFunction {
075 private String prefix;
076 private String name;
077 private Method method;
078
079 private ELFunction(String prefix, String name, Method method) {
080 this.prefix = prefix;
081 this.name = name;
082 this.method = method;
083 }
084 }
085
086 private List<ELService.ELConstant> extractConstants(Configuration conf, String key) throws ServiceException {
087 List<ELService.ELConstant> list = new ArrayList<ELService.ELConstant>();
088 if (conf.get(key, "").trim().length() > 0) {
089 for (String function : conf.getStrings(key)) {
090 String[] parts = parseDefinition(function);
091 list.add(new ELConstant(parts[0], parts[1], findConstant(parts[2], parts[3])));
092 log.trace("Registered prefix:constant[{0}:{1}] for class#field[{2}#{3}]", (Object[]) parts);
093 }
094 }
095 return list;
096 }
097
098 private List<ELService.ELFunction> extractFunctions(Configuration conf, String key) throws ServiceException {
099 List<ELService.ELFunction> list = new ArrayList<ELService.ELFunction>();
100 if (conf.get(key, "").trim().length() > 0) {
101 for (String function : conf.getStrings(key)) {
102 String[] parts = parseDefinition(function);
103 list.add(new ELFunction(parts[0], parts[1], findMethod(parts[2], parts[3])));
104 log.trace("Registered prefix:constant[{0}:{1}] for class#field[{2}#{3}]", (Object[]) parts);
105 }
106 }
107 return list;
108 }
109
110 /**
111 * Initialize the EL service.
112 *
113 * @param services services instance.
114 * @throws ServiceException thrown if the EL service could not be initialized.
115 */
116 @Override
117 public synchronized void init(Services services) throws ServiceException {
118 log.trace("Constants and functions registration");
119 constants = new HashMap<String, List<ELConstant>>();
120 functions = new HashMap<String, List<ELFunction>>();
121 //Get the list of group names from configuration file
122 // defined in the property tag: oozie.service.ELSerice.groups
123 //String []groupList = services.getConf().get(CONF_GROUPS, "").trim().split(",");
124 String[] groupList = services.getConf().getStrings(CONF_GROUPS, "");
125 //For each group, collect the required functions and constants
126 // and store it into HashMap
127 for (String group : groupList) {
128 List<ELConstant> tmpConstants = new ArrayList<ELConstant>();
129 tmpConstants.addAll(extractConstants(services.getConf(), CONF_CONSTANTS + group));
130 tmpConstants.addAll(extractConstants(services.getConf(), CONF_EXT_CONSTANTS + group));
131 constants.put(group, tmpConstants);
132 List<ELFunction> tmpFunctions = new ArrayList<ELFunction>();
133 tmpFunctions.addAll(extractFunctions(services.getConf(), CONF_FUNCTIONS + group));
134 tmpFunctions.addAll(extractFunctions(services.getConf(), CONF_EXT_FUNCTIONS + group));
135 functions.put(group, tmpFunctions);
136 }
137 }
138
139 /**
140 * Destroy the EL service.
141 */
142 @Override
143 public void destroy() {
144 constants = null;
145 functions = null;
146 }
147
148 /**
149 * Return the public interface for EL service.
150 *
151 * @return {@link ELService}.
152 */
153 @Override
154 public Class<? extends Service> getInterface() {
155 return ELService.class;
156 }
157
158 /**
159 * Return an {@link ELEvaluator} pre-configured with the constants and functions for the specific group of
160 * EL-functions and variables defined in the configuration. If the group name doesn't exist,
161 * IllegalArgumentException is thrown
162 *
163 * @param group: Name of the group of required EL Evaluator.
164 * @return a preconfigured {@link ELEvaluator}.
165 */
166 public ELEvaluator createEvaluator(String group) {
167 ELEvaluator.Context context = new ELEvaluator.Context();
168 boolean groupDefined = false;
169 if (constants.containsKey(group)) {
170 for (ELConstant constant : constants.get(group)) {
171 context.setVariable(constant.name, constant.value);
172 }
173 groupDefined = true;
174 }
175 if (functions.containsKey(group)) {
176 for (ELFunction function : functions.get(group)) {
177 context.addFunction(function.prefix, function.name, function.method);
178 }
179 groupDefined = true;
180 }
181 if (groupDefined == false) {
182 throw new IllegalArgumentException("Group " + group + " is not defined");
183 }
184 return new ELEvaluator(context);
185 }
186
187 private static String[] parseDefinition(String str) throws ServiceException {
188 try {
189 str = str.trim();
190 if (!str.contains(":")) {
191 str = ":" + str;
192 }
193 String[] parts = str.split(":");
194 String prefix = parts[0];
195 parts = parts[1].split("=");
196 String name = parts[0];
197 parts = parts[1].split("#");
198 String klass = parts[0];
199 String method = parts[1];
200 return new String[]{prefix, name, klass, method};
201 }
202 catch (Exception ex) {
203 throw new ServiceException(ErrorCode.E0110, str, ex.getMessage(), ex);
204 }
205 }
206
207 public static Method findMethod(String className, String methodName) throws ServiceException {
208 Method method = null;
209 try {
210 Class klass = Thread.currentThread().getContextClassLoader().loadClass(className);
211 for (Method m : klass.getMethods()) {
212 if (m.getName().equals(methodName)) {
213 method = m;
214 break;
215 }
216 }
217 if (method == null) {
218 throw new ServiceException(ErrorCode.E0111, className, methodName);
219 }
220 if ((method.getModifiers() & (Modifier.PUBLIC | Modifier.STATIC)) != (Modifier.PUBLIC | Modifier.STATIC)) {
221 throw new ServiceException(ErrorCode.E0112, className, methodName);
222 }
223 }
224 catch (ClassNotFoundException ex) {
225 throw new ServiceException(ErrorCode.E0113, className);
226 }
227 return method;
228 }
229
230 public static Object findConstant(String className, String constantName) throws ServiceException {
231 try {
232 Class klass = Thread.currentThread().getContextClassLoader().loadClass(className);
233 Field field = klass.getField(constantName);
234 if ((field.getModifiers() & (Modifier.PUBLIC | Modifier.STATIC)) != (Modifier.PUBLIC | Modifier.STATIC)) {
235 throw new ServiceException(ErrorCode.E0114, className, constantName);
236 }
237 return field.get(null);
238 }
239 catch (IllegalAccessException ex) {
240 throw new IllegalArgumentException(ex);
241 }
242 catch (NoSuchFieldException ex) {
243 throw new ServiceException(ErrorCode.E0115, className, constantName);
244 }
245 catch (ClassNotFoundException ex) {
246 throw new ServiceException(ErrorCode.E0113, className);
247 }
248 }
249
250 }