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.action.hadoop;
020
021import java.util.List;
022
023import org.apache.hadoop.conf.Configuration;
024import org.apache.hadoop.fs.Path;
025import org.apache.oozie.action.ActionExecutorException;
026import org.jdom.Element;
027import org.jdom.Namespace;
028
029public class ShellActionExecutor extends JavaActionExecutor {
030
031    /**
032     * Config property name to set the child environment
033     */
034    public String OOZIE_LAUNCHER_CHILD_ENV = "mapred.child.env";
035
036    public ShellActionExecutor() {
037        super("shell");
038    }
039
040    @SuppressWarnings("rawtypes")
041    @Override
042    public List<Class> getLauncherClasses() {
043        return null;
044    }
045
046    @Override
047    protected String getLauncherMain(Configuration launcherConf, Element actionXml) {
048        return launcherConf.get(LauncherMapper.CONF_OOZIE_ACTION_MAIN_CLASS, ShellMain.class.getName());
049    }
050
051    @SuppressWarnings("unchecked")
052    @Override
053    Configuration setupActionConf(Configuration actionConf, Context context, Element actionXml, Path appPath)
054            throws ActionExecutorException {
055        super.setupActionConf(actionConf, context, actionXml, appPath);
056        Namespace ns = actionXml.getNamespace();
057
058        String exec = actionXml.getChild("exec", ns).getTextTrim();
059        String execName = new Path(exec).getName();
060        actionConf.set(ShellMain.CONF_OOZIE_SHELL_EXEC, execName);
061
062        // Setting Shell command's arguments
063        setListInConf("argument", actionXml, actionConf, ShellMain.CONF_OOZIE_SHELL_ARGS, false);
064        // Setting Shell command's environment variable key=value
065        setListInConf("env-var", actionXml, actionConf, ShellMain.CONF_OOZIE_SHELL_ENVS, true);
066
067        // Setting capture output flag
068        actionConf.setBoolean(ShellMain.CONF_OOZIE_SHELL_CAPTURE_OUTPUT,
069                actionXml.getChild("capture-output", ns) != null);
070
071        return actionConf;
072    }
073
074    /**
075     * This method read a list of tag from an XML element and set the
076     * Configuration accordingly
077     *
078     * @param tag
079     * @param actionXml
080     * @param actionConf
081     * @param key
082     * @param checkKeyValue
083     * @throws ActionExecutorException
084     */
085    protected void setListInConf(String tag, Element actionXml, Configuration actionConf, String key,
086            boolean checkKeyValue) throws ActionExecutorException {
087        String[] strTagValue = null;
088        Namespace ns = actionXml.getNamespace();
089        List<Element> eTags = actionXml.getChildren(tag, ns);
090        if (eTags != null && eTags.size() > 0) {
091            strTagValue = new String[eTags.size()];
092            for (int i = 0; i < eTags.size(); i++) {
093                strTagValue[i] = eTags.get(i).getTextTrim();
094                if (checkKeyValue) {
095                    checkPair(strTagValue[i]);
096                }
097            }
098        }
099        MapReduceMain.setStrings(actionConf, key, strTagValue);
100    }
101
102    /**
103     * Check if the key=value pair is appropriately formatted
104     * @param pair
105     * @throws ActionExecutorException
106     */
107    private void checkPair(String pair) throws ActionExecutorException {
108        String[] varValue = pair.split("=");
109        if (varValue == null || varValue.length <= 1) {
110            throw new ActionExecutorException(ActionExecutorException.ErrorType.FAILED, "JA010",
111                    "Wrong ENV format [{0}] in <env-var> , key=value format expected ", pair);
112        }
113    }
114
115    @Override
116    protected Configuration setupLauncherConf(Configuration conf, Element actionXml, Path appPath, Context context)
117            throws ActionExecutorException {
118        super.setupLauncherConf(conf, actionXml, appPath, context);
119        conf.setBoolean("mapreduce.job.complete.cancel.delegation.tokens", true);
120        addDefaultChildEnv(conf);
121        return conf;
122    }
123
124    /**
125     * This method sets the PATH to current working directory for the launched
126     * map task from where shell command will run.
127     *
128     * @param conf
129     */
130    protected void addDefaultChildEnv(Configuration conf) {
131        String envValues = "PATH=.:$PATH";
132        updateProperty(conf, OOZIE_LAUNCHER_CHILD_ENV, envValues);
133    }
134
135    /**
136     * Utility method to append the new value to any property.
137     *
138     * @param conf
139     * @param propertyName
140     * @param appendValue
141     */
142    private void updateProperty(Configuration conf, String propertyName, String appendValue) {
143        if (conf != null) {
144            String val = conf.get(propertyName, "");
145            if (val.length() > 0) {
146                val += ",";
147            }
148            val += appendValue;
149            conf.set(propertyName, val);
150            LOG.debug("action conf is updated with default value for property " + propertyName + ", old value :"
151                    + conf.get(propertyName, "") + ", new value :" + val);
152        }
153    }
154
155}