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;
020
021import org.apache.hadoop.conf.Configuration;
022import org.apache.oozie.action.hadoop.MapReduceActionExecutor;
023import org.apache.oozie.client.WorkflowAction;
024import org.apache.oozie.service.CallbackService;
025import org.apache.oozie.workflow.WorkflowInstance;
026import org.apache.oozie.service.Services;
027import org.apache.oozie.util.ELEvaluator;
028import org.apache.oozie.util.PropertiesUtils;
029import org.apache.oozie.util.XConfiguration;
030import org.apache.oozie.util.ParamChecker;
031import org.apache.oozie.util.XmlUtils;
032import org.jdom.JDOMException;
033import java.io.IOException;
034import java.io.StringReader;
035import java.util.Properties;
036import java.util.Map;
037
038/**
039 * DAG EL functions.
040 */
041public class DagELFunctions {
042
043    public static final String HADOOP_JOBS_PREFIX = "hadoopJobs:";
044    private static final String WORKFLOW = "oozie.el.workflow.bean";
045    private static final String ACTION = "oozie.el.action.bean";
046    private static final String ACTION_PROTO_CONF = "oozie.el.action.proto.conf";
047
048    private static final String LAST_ACTION_IN_ERROR = "oozie.el.last.action.in.error";
049
050    private static final String ACTION_DATA = "action.data";
051    private static final String ACTION_ERROR_CODE = "action.error.code";
052    private static final String ACTION_ERROR_MESSAGE = "action.error.message";
053    private static final String ACTION_EXTERNAL_ID = "action.external.id";
054    private static final String ACTION_TRACKER_URI = "action.tracker.uri";
055    private static final String ACTION_EXTERNAL_STATUS = "action.external.status";
056
057    public static void configureEvaluator(ELEvaluator evaluator, WorkflowJobBean workflow, WorkflowActionBean action) {
058        evaluator.setVariable(WORKFLOW, workflow);
059        evaluator.setVariable(ACTION, action);
060
061        for (Map.Entry<String, String> entry : workflow.getWorkflowInstance().getConf()) {
062            if (ParamChecker.isValidIdentifier(entry.getKey())) {
063                String value = entry.getValue().trim();
064                try {
065                    String valueElem = "<value>"+value+"</value>";
066                    XmlUtils.parseXml(valueElem);
067                }
068                catch (JDOMException ex) {
069                    // If happens, try escaping the characters for XML. The escaping may or
070                    // may not solve the problem since the JDOMException could be for a range of issues.
071                    value = XmlUtils.escapeCharsForXML(value);
072                }
073                evaluator.setVariable(entry.getKey().trim(), value);
074            }
075        }
076        try {
077            evaluator.setVariable(ACTION_PROTO_CONF,
078                                  new XConfiguration(new StringReader(workflow.getProtoActionConf())));
079        }
080        catch (IOException ex) {
081            throw new RuntimeException("It should not happen", ex);
082        }
083    }
084
085    public static WorkflowActionBean getAction() {
086        ELEvaluator eval = ELEvaluator.getCurrent();
087        return (WorkflowActionBean) eval.getVariable(ACTION);
088    }
089
090    public static WorkflowJobBean getWorkflow() {
091        ELEvaluator eval = ELEvaluator.getCurrent();
092        return (WorkflowJobBean) eval.getVariable(WORKFLOW);
093    }
094
095    public static Configuration getProtoActionConf() {
096        ELEvaluator eval = ELEvaluator.getCurrent();
097        return (Configuration) eval.getVariable(ACTION_PROTO_CONF);
098    }
099
100    public static void setActionInfo(WorkflowInstance workflowInstance, WorkflowAction action) {
101        if (action.getExternalId() != null) {
102            workflowInstance.setVar(action.getName() + WorkflowInstance.NODE_VAR_SEPARATOR + ACTION_EXTERNAL_ID,
103                                    action.getExternalId());
104        }
105        if (action.getTrackerUri() != null) {
106            workflowInstance.setVar(action.getName() + WorkflowInstance.NODE_VAR_SEPARATOR + ACTION_TRACKER_URI,
107                                    action.getTrackerUri());
108        }
109        if (action.getExternalStatus() != null) {
110            workflowInstance.setVar(action.getName() + WorkflowInstance.NODE_VAR_SEPARATOR + ACTION_EXTERNAL_STATUS,
111                                    action.getExternalStatus());
112        }
113        if (action.getData() != null) {
114            workflowInstance
115                    .setVar(action.getName() + WorkflowInstance.NODE_VAR_SEPARATOR + ACTION_DATA, action.getData());
116        }
117        if (action.getExternalChildIDs() != null) {
118            workflowInstance.setVar(action.getName() + WorkflowInstance.NODE_VAR_SEPARATOR + ACTION_DATA,
119                    HADOOP_JOBS_PREFIX + action.getExternalChildIDs());
120        }
121        if (action.getStats() != null) {
122            workflowInstance.setVar(action.getName() + WorkflowInstance.NODE_VAR_SEPARATOR
123                    + MapReduceActionExecutor.HADOOP_COUNTERS,
124                    action.getStats());
125        }
126        if (action.getErrorCode() != null) {
127            workflowInstance.setVar(action.getName() + WorkflowInstance.NODE_VAR_SEPARATOR + ACTION_ERROR_CODE,
128                                    action.getErrorCode());
129        }
130        if (action.getErrorMessage() != null) {
131            workflowInstance.setVar(action.getName() + WorkflowInstance.NODE_VAR_SEPARATOR + ACTION_ERROR_MESSAGE,
132                                    action.getErrorMessage());
133        }
134        if (action.getStatus() == WorkflowAction.Status.ERROR) {
135            workflowInstance.setVar(LAST_ACTION_IN_ERROR, action.getName());
136        }
137    }
138
139    /**
140     * Return the job Id.
141     *
142     * @return the job Id.
143     */
144    public static String wf_id() {
145        return getWorkflow().getId();
146    }
147
148    /**
149     * Return the application name.
150     *
151     * @return the application name.
152     */
153    public static String wf_name() {
154        return getWorkflow().getAppName();
155    }
156
157    /**
158     * Return the application path.
159     *
160     * @return the application path.
161     */
162    public static String wf_appPath() {
163        return getWorkflow().getAppPath();
164    }
165
166    /**
167     * Return a job configuration property.
168     *
169     * @param property property name.
170     * @return the value of the property, <code>null</code> if the property is undefined.
171     */
172    public static String wf_conf(String property) {
173        return getWorkflow().getWorkflowInstance().getConf().get(property);
174    }
175
176    /**
177     * Return the job owner user name.
178     *
179     * @return the job owner user name.
180     */
181    public static String wf_user() {
182        return getWorkflow().getUser();
183    }
184
185    /**
186     * Return the job owner group name.
187     *
188     * @return the job owner group name.
189     */
190    public static String wf_group() {
191        return getWorkflow().getGroup();
192    }
193
194    /**
195     * Create a callback URL for the current action.
196     *
197     * @param externalStatusVar variable for the caller to inject the external status.
198     * @return the callback URL for the current action.
199     */
200    public static String wf_callback(String externalStatusVar) {
201        return Services.get().get(CallbackService.class).createCallBackUrl(getAction().getId(), externalStatusVar);
202    }
203
204    /**
205     * Return the transition taken by a workflow job action/decision action.
206     *
207     * @param actionName action/decision action name.
208     * @return the transition taken, <code>null</code> if the action has not completed yet.
209     */
210    public static String wf_transition(String actionName) {
211        return getWorkflow().getWorkflowInstance().getTransition(actionName);
212    }
213
214    /**
215     * Return the name of the last action that ended in error.
216     *
217     * @return the name of the last action that ended in error, <code>null</code> if no action in the workflow job has
218     *         ended in error.
219     */
220    public static String wf_lastErrorNode() {
221        return getWorkflow().getWorkflowInstance().getVar(LAST_ACTION_IN_ERROR);
222    }
223
224    /**
225     * Return the error code for an action.
226     *
227     * @param actionName action name.
228     * @return the error code for the action, <code>null</code> if the action has not ended in error.
229     */
230    public static String wf_errorCode(String actionName) {
231        return getWorkflow().getWorkflowInstance()
232                .getVar(actionName + WorkflowInstance.NODE_VAR_SEPARATOR + ACTION_ERROR_CODE);
233    }
234
235    /**
236     * Return the error message for an action.
237     *
238     * @param actionName action name.
239     * @return the error message for the action, <code>null</code> if the action has not ended in error.
240     */
241    public static String wf_errorMessage(String actionName) {
242        return getWorkflow().getWorkflowInstance()
243                .getVar(actionName + WorkflowInstance.NODE_VAR_SEPARATOR + ACTION_ERROR_MESSAGE);
244    }
245
246    /**
247     * Return the workflow run number, unless a rerun it is always 1.
248     *
249     * @return the workflow run number, unless a rerun it is always 1.
250     */
251    public static int wf_run() {
252        return getWorkflow().getRun();
253    }
254
255    /**
256     * Return the action data for an action.
257     *
258     * @param actionName action name.
259     * @return value of the property.
260     */
261    @SuppressWarnings("unchecked")
262    public static Map<String, String> wf_actionData(String actionName) {
263        ELEvaluator eval = ELEvaluator.getCurrent();
264        Properties props = (Properties) eval.getVariable(actionName + ACTION_ERROR_MESSAGE);
265
266        if (props == null) {
267            String data = getWorkflow().getWorkflowInstance()
268                    .getVar(actionName + WorkflowInstance.NODE_VAR_SEPARATOR + ACTION_DATA);
269            if (data != null) {
270                props = PropertiesUtils.stringToProperties(data);
271            }
272            else {
273                props = new Properties();
274            }
275            eval.setVariable(actionName + ACTION_ERROR_MESSAGE, props);
276        }
277        return (Map<String, String>) (Map) props;
278    }
279
280    /**
281     * Return the external ID of an action.
282     *
283     * @param actionName action name.
284     * @return the external ID of an action.
285     */
286    public static String wf_actionExternalId(String actionName) {
287        return getWorkflow().getWorkflowInstance()
288                .getVar(actionName + WorkflowInstance.NODE_VAR_SEPARATOR + ACTION_EXTERNAL_ID);
289    }
290
291    /**
292     * Return the tracker URI of an action.
293     *
294     * @param actionName action name.
295     * @return the tracker URI of an action.
296     */
297    public static String wf_actionTrackerUri(String actionName) {
298        return getWorkflow().getWorkflowInstance()
299                .getVar(actionName + WorkflowInstance.NODE_VAR_SEPARATOR + ACTION_TRACKER_URI);
300    }
301
302    /**
303     * Return the action external status.
304     *
305     * @param actionName action/decision action name.
306     * @return the action external status.
307     */
308    public static String wf_actionExternalStatus(String actionName) {
309        return getWorkflow().getWorkflowInstance()
310                .getVar(actionName + WorkflowInstance.NODE_VAR_SEPARATOR + ACTION_EXTERNAL_STATUS);
311    }
312
313    public static String getActionVar(String actionName, String varName) {
314        return getWorkflow().getWorkflowInstance().getVar(actionName + WorkflowInstance.NODE_VAR_SEPARATOR + varName);
315    }
316
317}