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        // Setting to control if ShellMain should write log4j.properties
078        boolean writeL4J = actionConf.getBoolean(ShellMain.CONF_OOZIE_SHELL_SETUP_HADOOP_CONF_DIR_WRITE_LOG4J_PROPERTIES,
079                ConfigurationService.getBoolean(ShellMain.CONF_OOZIE_SHELL_SETUP_HADOOP_CONF_DIR_WRITE_LOG4J_PROPERTIES));
080        actionConf.setBoolean(ShellMain.CONF_OOZIE_SHELL_SETUP_HADOOP_CONF_DIR_WRITE_LOG4J_PROPERTIES, writeL4J);
081        // Setting of content of log4j.properties, if to be written
082        if (writeL4J) {
083            String l4jContent = actionConf.get(ShellMain.CONF_OOZIE_SHELL_SETUP_HADOOP_CONF_DIR_LOG4J_CONTENT,
084                    ConfigurationService.get(ShellMain.CONF_OOZIE_SHELL_SETUP_HADOOP_CONF_DIR_LOG4J_CONTENT));
085            actionConf.set(ShellMain.CONF_OOZIE_SHELL_SETUP_HADOOP_CONF_DIR_LOG4J_CONTENT, l4jContent);
086        }
087
088        return actionConf;
089    }
090
091    /**
092     * This method read a list of tag from an XML element and set the
093     * Configuration accordingly
094     *
095     * @param tag
096     * @param actionXml
097     * @param actionConf
098     * @param key
099     * @param checkKeyValue
100     * @throws ActionExecutorException
101     */
102    protected void setListInConf(String tag, Element actionXml, Configuration actionConf, String key,
103            boolean checkKeyValue) throws ActionExecutorException {
104        String[] strTagValue = null;
105        Namespace ns = actionXml.getNamespace();
106        List<Element> eTags = actionXml.getChildren(tag, ns);
107        if (eTags != null && eTags.size() > 0) {
108            strTagValue = new String[eTags.size()];
109            for (int i = 0; i < eTags.size(); i++) {
110                strTagValue[i] = eTags.get(i).getTextTrim();
111                if (checkKeyValue) {
112                    checkPair(strTagValue[i]);
113                }
114            }
115        }
116        MapReduceMain.setStrings(actionConf, key, strTagValue);
117    }
118
119    /**
120     * Check if the key=value pair is appropriately formatted
121     * @param pair
122     * @throws ActionExecutorException
123     */
124    private void checkPair(String pair) throws ActionExecutorException {
125        String[] varValue = pair.split("=");
126        if (varValue == null || varValue.length <= 1) {
127            throw new ActionExecutorException(ActionExecutorException.ErrorType.FAILED, "JA010",
128                    "Wrong ENV format [{0}] in <env-var> , key=value format expected ", pair);
129        }
130    }
131
132    @Override
133    protected Configuration setupLauncherConf(Configuration conf, Element actionXml, Path appPath, Context context)
134            throws ActionExecutorException {
135        super.setupLauncherConf(conf, actionXml, appPath, context);
136        addDefaultChildEnv(conf);
137        return conf;
138    }
139
140    /**
141     * This method sets the PATH to current working directory for the launched
142     * map task from where shell command will run.
143     *
144     * @param conf
145     */
146    protected void addDefaultChildEnv(Configuration conf) {
147        String envValues = "PATH=.:$PATH";
148        updateProperty(conf, OOZIE_LAUNCHER_MAP_ENV, envValues);
149        updateProperty(conf, OOZIE_LAUNCHER_CHILD_ENV, envValues);
150    }
151
152    /**
153     * Utility method to append the new value to any property.
154     *
155     * @param conf
156     * @param propertyName
157     * @param appendValue
158     */
159    private void updateProperty(Configuration conf, String propertyName, String appendValue) {
160        if (conf != null) {
161            String val = conf.get(propertyName, "");
162            if (val.length() > 0) {
163                val += ",";
164            }
165            val += appendValue;
166            conf.set(propertyName, val);
167            LOG.debug("action conf is updated with default value for property " + propertyName + ", old value :"
168                    + conf.get(propertyName, "") + ", new value :" + val);
169        }
170    }
171
172}