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 019package org.apache.oozie.coord; 020 021import java.util.Calendar; 022import java.util.Date; 023import java.util.HashMap; 024import java.util.Iterator; 025import java.util.List; 026import java.util.Map; 027 028import org.apache.hadoop.conf.Configuration; 029import org.apache.oozie.CoordinatorActionBean; 030import org.apache.oozie.command.coord.CoordCommandUtils; 031import org.apache.oozie.coord.input.dependency.CoordInputDependency; 032import org.apache.oozie.coord.input.logic.CoordInputLogicEvaluator; 033import org.apache.oozie.service.ELService; 034import org.apache.oozie.service.Services; 035import org.apache.oozie.util.DateUtils; 036import org.apache.oozie.util.ELEvaluator; 037import org.apache.oozie.util.XmlUtils; 038import org.jdom.Element; 039 040/** 041 * This class provide different evaluators required at different stages 042 */ 043public class CoordELEvaluator { 044 public static final Integer MINUTE = 1; 045 public static final Integer HOUR = 60 * MINUTE; 046 047 /** 048 * Create an evaluator to be used in resolving configuration vars and frequency constant/functions (used in Stage 049 * 1) 050 * 051 * @param conf : Configuration containing property variables 052 * @param group Name of the group of required EL Evaluator. 053 * @return configured ELEvaluator 054 */ 055 public static ELEvaluator createELEvaluatorForGroup(Configuration conf, String group) { 056 ELEvaluator eval = Services.get().get(ELService.class).createEvaluator(group); 057 setConfigToEval(eval, conf); 058 return eval; 059 } 060 061 /** 062 * Create a new Evaluator to resolve the EL functions and variables using action creation time (Phase 2) 063 * 064 * @param event : Xml element for data-in element usually enclosed by <data-in(out)> tag 065 * @param appInst : Application Instance related information such as Action creation Time 066 * @param conf :Configuration to substitute any variables 067 * @return configured ELEvaluator 068 * @throws Exception : If there is any date-time string in wrong format, the exception is thrown 069 */ 070 public static ELEvaluator createInstancesELEvaluator(Element event, SyncCoordAction appInst, Configuration conf) 071 throws Exception { 072 return createInstancesELEvaluator("coord-action-create", event, appInst, conf); 073 } 074 075 public static ELEvaluator createInstancesELEvaluator(String tag, Element event, SyncCoordAction appInst, 076 Configuration conf) throws Exception { 077 ELEvaluator eval = Services.get().get(ELService.class).createEvaluator(tag); 078 setConfigToEval(eval, conf); 079 SyncCoordDataset ds = getDSObject(event); 080 CoordELFunctions.configureEvaluator(eval, ds, appInst); 081 return eval; 082 } 083 084 public static ELEvaluator createELEvaluatorForDataEcho(Configuration conf, String group, 085 HashMap<String, String> dataNameList) throws Exception { 086 ELEvaluator eval = createELEvaluatorForGroup(conf, group); 087 for (Iterator<String> it = dataNameList.keySet().iterator(); it.hasNext();) { 088 String key = it.next(); 089 String value = dataNameList.get(key); 090 eval.setVariable("oozie.dataname." + key, value); 091 } 092 return eval; 093 } 094 095 /** 096 * Create a new evaluator for Lazy resolve (phase 3). For example, coord_latest(n) and coord_actualTime()function 097 * should be resolved when all other data dependencies are met. 098 * 099 * @param actualTime : Action start time 100 * @param nominalTime : Action creation time 101 * @param dEvent :XML element for data-in element usually enclosed by <data-in(out)> tag 102 * @param conf :Configuration to substitute any variables 103 * @return configured ELEvaluator 104 * @throws Exception : If there is any date-time string in wrong format, the exception is thrown 105 */ 106 public static ELEvaluator createLazyEvaluator(Date actualTime, Date nominalTime, Element dEvent, Configuration conf) 107 throws Exception { 108 ELEvaluator eval = Services.get().get(ELService.class).createEvaluator("coord-action-start"); 109 setConfigToEval(eval, conf); 110 SyncCoordDataset ds = getDSObject(dEvent); 111 SyncCoordAction appInst = new SyncCoordAction(); 112 appInst.setNominalTime(nominalTime); 113 appInst.setActualTime(actualTime); 114 CoordELFunctions.configureEvaluator(eval, ds, appInst); 115 eval.setVariable(CoordELFunctions.CONFIGURATION, conf); 116 return eval; 117 } 118 119 /** 120 * Create a SLA evaluator to be used during Materialization 121 * @param eAction the action 122 * @param coordAction the coordinator action 123 * @param conf the configuration 124 * @return eval returns SLA evaluator to be used during Materialization 125 * @throws Exception in case of error 126 */ 127 public static ELEvaluator createSLAEvaluator(Element eAction, CoordinatorActionBean coordAction, Configuration conf) 128 throws Exception { 129 ELEvaluator eval = Services.get().get(ELService.class).createEvaluator("coord-sla-create"); 130 setConfigToEval(eval, conf); 131 SyncCoordAction appInst = new SyncCoordAction();// TODO: 132 appInst.setNominalTime(coordAction.getNominalTime()); 133 appInst.setActualTime(coordAction.getCreatedTime()); 134 appInst.setActionId(coordAction.getId()); 135 appInst.setName(eAction.getAttributeValue("name")); 136 CoordELFunctions.configureEvaluator(eval, null, appInst); 137 138 Element events = eAction.getChild("output-events", eAction.getNamespace()); 139 if (events != null) { 140 for (Object obj : events.getChildren("data-out", eAction.getNamespace())) { 141 Element data = (Element) obj; 142 if (data.getChild("uris", data.getNamespace()) != null) { 143 String uris = data.getChild("uris", data.getNamespace()).getTextTrim(); 144 uris = uris.replaceAll(CoordELFunctions.INSTANCE_SEPARATOR, CoordELFunctions.DIR_SEPARATOR); 145 eval.setVariable(".dataout." + data.getAttributeValue("name"), uris); 146 } 147 if (data.getChild(CoordCommandUtils.UNRESOLVED_INSTANCES_TAG, data.getNamespace()) != null) { 148 eval.setVariable(".dataout." + data.getAttributeValue("name") + ".unresolved", "true"); 149 } 150 } 151 } 152 return eval; 153 } 154 155 /** 156 * Create an Evaluator using conf and input/output-data (used for sla) 157 * @param conf the configuration 158 * @param group the group for the EL expression 159 * @param dataNameList the name list for the data 160 * @return eval returns an Evaluator using conf and input/output-data (used for sla) 161 * @throws Exception in case of error 162 */ 163 public static ELEvaluator createELEvaluatorForDataAndConf(Configuration conf, String group, 164 HashMap<String, String> dataNameList) throws Exception { 165 ELEvaluator eval = createELEvaluatorForDataEcho(conf, group, dataNameList); 166 setConfigToEval(eval, conf); 167 return eval; 168 } 169 170 /** 171 * Create an Evaluator to resolve dataIns and dataOuts of an application instance (used in stage 3) 172 * 173 * @param eJob : XML element for the application instance 174 * @param conf :Configuration to substitute any variables 175 * @param actionId the action Id 176 * @return configured ELEvaluator 177 * @throws Exception : If there is any date-time string in wrong format, the exception is thrown 178 */ 179 180 public static ELEvaluator createDataEvaluator(Element eJob, Configuration conf, String actionId) throws Exception { 181 return createDataEvaluator(eJob, conf, actionId, null, null); 182 } 183 184 public static ELEvaluator createDataEvaluator(Element eJob, Configuration conf, String actionId, 185 CoordInputDependency pullDependencies, CoordInputDependency pushDependencies) throws Exception { 186 ELEvaluator e = Services.get().get(ELService.class).createEvaluator("coord-action-start"); 187 setConfigToEval(e, conf); 188 SyncCoordAction appInst = new SyncCoordAction(); 189 String strNominalTime = eJob.getAttributeValue("action-nominal-time"); 190 if (strNominalTime != null) { 191 appInst.setNominalTime(DateUtils.parseDateOozieTZ(strNominalTime)); 192 appInst.setTimeZone(DateUtils.getTimeZone(eJob.getAttributeValue("timezone"))); 193 appInst.setFrequency(eJob.getAttributeValue("frequency")); 194 appInst.setTimeUnit(TimeUnit.valueOf(eJob.getAttributeValue("freq_timeunit"))); 195 appInst.setActionId(actionId); 196 appInst.setName(eJob.getAttributeValue("name")); 197 appInst.setPullDependencies(pullDependencies); 198 appInst.setPushDependencies(pushDependencies); 199 if (CoordUtils.isInputLogicSpecified(eJob)) { 200 e.setVariable(".actionInputLogic", 201 XmlUtils.prettyPrint(eJob.getChild(CoordInputLogicEvaluator.INPUT_LOGIC, eJob.getNamespace())).toString()); 202 } 203 } 204 String strActualTime = eJob.getAttributeValue("action-actual-time"); 205 if (strActualTime != null) { 206 appInst.setActualTime(DateUtils.parseDateOozieTZ(strActualTime)); 207 } 208 CoordELFunctions.configureEvaluator(e, null, appInst); 209 Element events = eJob.getChild("input-events", eJob.getNamespace()); 210 if (events != null) { 211 for (Element data : (List<Element>) events.getChildren("data-in", eJob.getNamespace())) { 212 if (data.getChild("uris", data.getNamespace()) != null) { 213 String uris = data.getChild("uris", data.getNamespace()).getTextTrim(); 214 uris = uris.replaceAll(CoordELFunctions.INSTANCE_SEPARATOR, CoordELFunctions.DIR_SEPARATOR); 215 e.setVariable(".datain." + data.getAttributeValue("name"), uris); 216 } 217 else { 218 } 219 if (data.getChild(CoordCommandUtils.UNRESOLVED_INSTANCES_TAG, data.getNamespace()) != null) { 220 e.setVariable(".datain." + data.getAttributeValue("name") + ".unresolved", "true"); // TODO: 221 // check 222 // null 223 } 224 Element doneFlagElement = data.getChild("dataset", data.getNamespace()).getChild("done-flag", 225 data.getNamespace()); 226 String doneFlag = CoordUtils.getDoneFlag(doneFlagElement); 227 e.setVariable(".datain." + data.getAttributeValue("name") + ".doneFlag", doneFlag); 228 } 229 } 230 events = eJob.getChild("output-events", eJob.getNamespace()); 231 if (events != null) { 232 for (Element data : (List<Element>) events.getChildren("data-out", eJob.getNamespace())) { 233 if (data.getChild("uris", data.getNamespace()) != null) { 234 String uris = data.getChild("uris", data.getNamespace()).getTextTrim(); 235 uris = uris.replaceAll(CoordELFunctions.INSTANCE_SEPARATOR, CoordELFunctions.DIR_SEPARATOR); 236 e.setVariable(".dataout." + data.getAttributeValue("name"), uris); 237 } 238 else { 239 }// TODO 240 if (data.getChild(CoordCommandUtils.UNRESOLVED_INSTANCES_TAG, data.getNamespace()) != null) { 241 e.setVariable(".dataout." + data.getAttributeValue("name") + ".unresolved", "true"); // TODO: 242 // check 243 // null 244 } 245 } 246 } 247 return e; 248 } 249 250 /** 251 * Create a new Evaluator to resolve URI temple with time specific constant 252 * 253 * @param strDate : Date-time 254 * @return configured ELEvaluator 255 * @throws Exception If there is any date-time string in wrong format, the exception is thrown 256 */ 257 public static ELEvaluator createURIELEvaluator(String strDate) throws Exception { 258 ELEvaluator eval = new ELEvaluator(); 259 Calendar date = Calendar.getInstance(DateUtils.getOozieProcessingTimeZone()); 260 // always??? 261 date.setTime(DateUtils.parseDateOozieTZ(strDate)); 262 eval.setVariable("YEAR", date.get(Calendar.YEAR)); 263 eval.setVariable("MONTH", make2Digits(date.get(Calendar.MONTH) + 1)); 264 eval.setVariable("DAY", make2Digits(date.get(Calendar.DAY_OF_MONTH))); 265 eval.setVariable("HOUR", make2Digits(date.get(Calendar.HOUR_OF_DAY))); 266 eval.setVariable("MINUTE", make2Digits(date.get(Calendar.MINUTE))); 267 return eval; 268 } 269 270 /** 271 * Create Dataset object using the Dataset XML information 272 * 273 * @param eData the xml 274 * @return ds returns Dataset object using the Dataset XML information 275 * @throws Exception if the Dataset object can't be created 276 */ 277 private static SyncCoordDataset getDSObject(Element eData) throws Exception { 278 SyncCoordDataset ds = new SyncCoordDataset(); 279 Element eDataset = eData.getChild("dataset", eData.getNamespace()); 280 // System.out.println("eDATA :"+ XmlUtils.prettyPrint(eData)); 281 Date initInstance = DateUtils.parseDateOozieTZ(eDataset.getAttributeValue("initial-instance")); 282 ds.setInitInstance(initInstance); 283 if (eDataset.getAttributeValue("frequency") != null) { 284 int frequency = Integer.parseInt(eDataset.getAttributeValue("frequency")); 285 ds.setFrequency(frequency); 286 ds.setType("SYNC"); 287 if (eDataset.getAttributeValue("freq_timeunit") == null) { 288 throw new RuntimeException("No freq_timeunit defined in data set definition\n" 289 + XmlUtils.prettyPrint(eDataset)); 290 } 291 ds.setTimeUnit(TimeUnit.valueOf(eDataset.getAttributeValue("freq_timeunit"))); 292 if (eDataset.getAttributeValue("timezone") == null) { 293 throw new RuntimeException("No timezone defined in data set definition\n" 294 + XmlUtils.prettyPrint(eDataset)); 295 } 296 ds.setTimeZone(DateUtils.getTimeZone(eDataset.getAttributeValue("timezone"))); 297 if (eDataset.getAttributeValue("end_of_duration") == null) { 298 throw new RuntimeException("No end_of_duration defined in data set definition\n" 299 + XmlUtils.prettyPrint(eDataset)); 300 } 301 ds.setEndOfDuration(TimeUnit.valueOf(eDataset.getAttributeValue("end_of_duration"))); 302 303 Element doneFlagElement = eDataset.getChild("done-flag", eData.getNamespace()); 304 String doneFlag = CoordUtils.getDoneFlag(doneFlagElement); 305 ds.setDoneFlag(doneFlag); 306 } 307 else { 308 ds.setType("ASYNC"); 309 } 310 String name = eDataset.getAttributeValue("name"); 311 ds.setName(name); 312 // System.out.println(name + " VAL "+ eDataset.getChild("uri-template", 313 // eData.getNamespace())); 314 String uriTemplate = eDataset.getChild("uri-template", eData.getNamespace()).getTextTrim(); 315 ds.setUriTemplate(uriTemplate); 316 // ds.setTimeUnit(TimeUnit.MINUTES); 317 return ds; 318 } 319 320 /** 321 * Set all job configurations properties into evaluator. 322 * 323 * @param eval : Evaluator to set variables 324 * @param conf : configurations to set Evaluator 325 */ 326 private static void setConfigToEval(ELEvaluator eval, Configuration conf) { 327 for (Map.Entry<String, String> entry : conf) { 328 eval.setVariable(entry.getKey(), entry.getValue().trim()); 329 } 330 } 331 332 /** 333 * make any one digit number to two digit string pre-appending a"0" 334 * 335 * @param num : number to make sting 336 * @return :String of length at least two digit. 337 */ 338 private static String make2Digits(int num) { 339 String ret = "" + num; 340 if (num <= 9) { 341 ret = "0" + ret; 342 } 343 return ret; 344 } 345}