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