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