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    package org.apache.oozie.cli;
019    
020    import java.io.File;
021    import java.io.FileInputStream;
022    import java.io.FileReader;
023    import java.io.IOException;
024    import java.io.InputStream;
025    import java.io.PrintStream;
026    import java.text.SimpleDateFormat;
027    import java.util.ArrayList;
028    import java.util.Date;
029    import java.util.List;
030    import java.util.Locale;
031    import java.util.Map;
032    import java.util.Properties;
033    import java.util.TimeZone;
034    import java.util.concurrent.Callable;
035    import java.util.regex.Matcher;
036    import java.util.regex.Pattern;
037    
038    import javax.xml.XMLConstants;
039    import javax.xml.parsers.DocumentBuilder;
040    import javax.xml.parsers.DocumentBuilderFactory;
041    import javax.xml.parsers.ParserConfigurationException;
042    import javax.xml.transform.stream.StreamSource;
043    import javax.xml.validation.Schema;
044    import javax.xml.validation.SchemaFactory;
045    import javax.xml.validation.Validator;
046    
047    import org.apache.commons.cli.CommandLine;
048    import org.apache.commons.cli.Option;
049    import org.apache.commons.cli.OptionBuilder;
050    import org.apache.commons.cli.OptionGroup;
051    import org.apache.commons.cli.Options;
052    import org.apache.commons.cli.ParseException;
053    import org.apache.oozie.BuildInfo;
054    import org.apache.oozie.client.AuthOozieClient;
055    import org.apache.oozie.client.BulkResponse;
056    import org.apache.oozie.client.BundleJob;
057    import org.apache.oozie.client.CoordinatorAction;
058    import org.apache.oozie.client.CoordinatorJob;
059    import org.apache.oozie.client.OozieClient;
060    import org.apache.oozie.client.OozieClient.SYSTEM_MODE;
061    import org.apache.oozie.client.OozieClientException;
062    import org.apache.oozie.client.WorkflowAction;
063    import org.apache.oozie.client.WorkflowJob;
064    import org.apache.oozie.client.XOozieClient;
065    import org.apache.oozie.client.rest.RestConstants;
066    import org.w3c.dom.DOMException;
067    import org.w3c.dom.Document;
068    import org.w3c.dom.Element;
069    import org.w3c.dom.Node;
070    import org.w3c.dom.NodeList;
071    import org.w3c.dom.Text;
072    import org.xml.sax.SAXException;
073    
074    import com.google.common.annotations.VisibleForTesting;
075    
076    /**
077     * Oozie command line utility.
078     */
079    public class OozieCLI {
080        public static final String ENV_OOZIE_URL = "OOZIE_URL";
081        public static final String ENV_OOZIE_DEBUG = "OOZIE_DEBUG";
082        public static final String ENV_OOZIE_TIME_ZONE = "OOZIE_TIMEZONE";
083        public static final String WS_HEADER_PREFIX = "header:";
084    
085        public static final String HELP_CMD = "help";
086        public static final String VERSION_CMD = "version";
087        public static final String JOB_CMD = "job";
088        public static final String JOBS_CMD = "jobs";
089        public static final String ADMIN_CMD = "admin";
090        public static final String VALIDATE_CMD = "validate";
091        public static final String SLA_CMD = "sla";
092        public static final String PIG_CMD = "pig";
093        public static final String HIVE_CMD = "hive";
094        public static final String MR_CMD = "mapreduce";
095        public static final String INFO_CMD = "info";
096    
097        public static final String OOZIE_OPTION = "oozie";
098        public static final String CONFIG_OPTION = "config";
099        public static final String SUBMIT_OPTION = "submit";
100        public static final String OFFSET_OPTION = "offset";
101        public static final String START_OPTION = "start";
102        public static final String RUN_OPTION = "run";
103        public static final String DRYRUN_OPTION = "dryrun";
104        public static final String SUSPEND_OPTION = "suspend";
105        public static final String RESUME_OPTION = "resume";
106        public static final String KILL_OPTION = "kill";
107        public static final String CHANGE_OPTION = "change";
108        public static final String CHANGE_VALUE_OPTION = "value";
109        public static final String RERUN_OPTION = "rerun";
110        public static final String INFO_OPTION = "info";
111        public static final String LOG_OPTION = "log";
112        public static final String ACTION_OPTION = "action";
113        public static final String DEFINITION_OPTION = "definition";
114        public static final String CONFIG_CONTENT_OPTION = "configcontent";
115    
116        public static final String DO_AS_OPTION = "doas";
117    
118        public static final String LEN_OPTION = "len";
119        public static final String FILTER_OPTION = "filter";
120        public static final String JOBTYPE_OPTION = "jobtype";
121        public static final String SYSTEM_MODE_OPTION = "systemmode";
122        public static final String VERSION_OPTION = "version";
123        public static final String STATUS_OPTION = "status";
124        public static final String LOCAL_TIME_OPTION = "localtime";
125        public static final String TIME_ZONE_OPTION = "timezone";
126        public static final String QUEUE_DUMP_OPTION = "queuedump";
127        public static final String RERUN_COORD_OPTION = "coordinator";
128        public static final String DATE_OPTION = "date";
129        public static final String RERUN_REFRESH_OPTION = "refresh";
130        public static final String RERUN_NOCLEANUP_OPTION = "nocleanup";
131    
132        public static final String AUTH_OPTION = "auth";
133    
134        public static final String VERBOSE_OPTION = "verbose";
135        public static final String VERBOSE_DELIMITER = "\t";
136        public static final String DEBUG_OPTION = "debug";
137    
138        public static final String SCRIPTFILE_OPTION = "file";
139    
140        public static final String INFO_TIME_ZONES_OPTION = "timezones";
141    
142        public static final String BULK_OPTION = "bulk";
143    
144        private static final String[] OOZIE_HELP = {
145                "the env variable '" + ENV_OOZIE_URL + "' is used as default value for the '-" + OOZIE_OPTION + "' option",
146                "the env variable '" + ENV_OOZIE_TIME_ZONE + "' is used as default value for the '-" + TIME_ZONE_OPTION + "' option",
147                "custom headers for Oozie web services can be specified using '-D" + WS_HEADER_PREFIX + "NAME=VALUE'" };
148    
149        private static final String RULER;
150        private static final int LINE_WIDTH = 132;
151    
152        private boolean used;
153    
154        private static final String INSTANCE_SEPARATOR = "#";
155    
156        private static final String MAPRED_MAPPER = "mapred.mapper.class";
157        private static final String MAPRED_MAPPER_2 = "mapreduce.map.class";
158        private static final String MAPRED_REDUCER = "mapred.reducer.class";
159        private static final String MAPRED_REDUCER_2 = "mapreduce.reduce.class";
160        private static final String MAPRED_INPUT = "mapred.input.dir";
161        private static final String MAPRED_OUTPUT = "mapred.output.dir";
162    
163        private static final Pattern GMT_OFFSET_SHORTEN_PATTERN = Pattern.compile("(.* )GMT((?:-|\\+)\\d{2}:\\d{2})");
164    
165        static {
166            StringBuilder sb = new StringBuilder();
167            for (int i = 0; i < LINE_WIDTH; i++) {
168                sb.append("-");
169            }
170            RULER = sb.toString();
171        }
172    
173        /**
174         * Entry point for the Oozie CLI when invoked from the command line.
175         * <p/>
176         * Upon completion this method exits the JVM with '0' (success) or '-1' (failure).
177         *
178         * @param args options and arguments for the Oozie CLI.
179         */
180        public static void main(String[] args) {
181            if (!System.getProperties().containsKey(AuthOozieClient.USE_AUTH_TOKEN_CACHE_SYS_PROP)) {
182                System.setProperty(AuthOozieClient.USE_AUTH_TOKEN_CACHE_SYS_PROP, "true");
183            }
184            System.exit(new OozieCLI().run(args));
185        }
186    
187        /**
188         * Create an Oozie CLI instance.
189         */
190        public OozieCLI() {
191            used = false;
192        }
193    
194        /**
195         * Return Oozie CLI top help lines.
196         *
197         * @return help lines.
198         */
199        protected String[] getCLIHelp() {
200            return OOZIE_HELP;
201        }
202    
203        /**
204         * Add authentication specific options to oozie cli
205         *
206         * @param options the collection of options to add auth options
207         */
208        protected void addAuthOptions(Options options) {
209            Option auth = new Option(AUTH_OPTION, true, "select authentication type [SIMPLE|KERBEROS]");
210            options.addOption(auth);
211        }
212    
213        /**
214         * Create option for command line option 'admin'
215         * @return admin options
216         */
217        protected Options createAdminOptions() {
218            Option oozie = new Option(OOZIE_OPTION, true, "Oozie URL");
219            Option system_mode = new Option(SYSTEM_MODE_OPTION, true,
220                    "Supported in Oozie-2.0 or later versions ONLY. Change oozie system mode [NORMAL|NOWEBSERVICE|SAFEMODE]");
221            Option status = new Option(STATUS_OPTION, false, "show the current system status");
222            Option version = new Option(VERSION_OPTION, false, "show Oozie server build version");
223            Option queuedump = new Option(QUEUE_DUMP_OPTION, false, "show Oozie server queue elements");
224            Option doAs = new Option(DO_AS_OPTION, true, "doAs user, impersonates as the specified user");
225            Options adminOptions = new Options();
226            adminOptions.addOption(oozie);
227            adminOptions.addOption(doAs);
228            OptionGroup group = new OptionGroup();
229            group.addOption(system_mode);
230            group.addOption(status);
231            group.addOption(version);
232            group.addOption(queuedump);
233            adminOptions.addOptionGroup(group);
234            addAuthOptions(adminOptions);
235            return adminOptions;
236        }
237    
238        /**
239         * Create option for command line option 'job'
240         * @return job options
241         */
242        protected Options createJobOptions() {
243            Option oozie = new Option(OOZIE_OPTION, true, "Oozie URL");
244            Option config = new Option(CONFIG_OPTION, true, "job configuration file '.xml' or '.properties'");
245            Option submit = new Option(SUBMIT_OPTION, false, "submit a job");
246            Option run = new Option(RUN_OPTION, false, "run a job");
247            Option debug = new Option(DEBUG_OPTION, false, "Use debug mode to see debugging statements on stdout");
248            Option rerun = new Option(RERUN_OPTION, true,
249                    "rerun a job  (coordinator requires -action or -date, bundle requires -coordinator or -date)");
250            Option dryrun = new Option(DRYRUN_OPTION, false, "Dryrun a workflow (since 3.3.2) or coordinator (since 2.0) job without"
251                    + " actually executing it");
252            Option start = new Option(START_OPTION, true, "start a job");
253            Option suspend = new Option(SUSPEND_OPTION, true, "suspend a job");
254            Option resume = new Option(RESUME_OPTION, true, "resume a job");
255            Option kill = new Option(KILL_OPTION, true, "kill a job");
256            Option change = new Option(CHANGE_OPTION, true, "change a coordinator job");
257            Option changeValue = new Option(CHANGE_VALUE_OPTION, true,
258                    "new endtime/concurrency/pausetime value for changing a coordinator job");
259            Option info = new Option(INFO_OPTION, true, "info of a job");
260            Option offset = new Option(OFFSET_OPTION, true, "job info offset of actions (default '1', requires -info)");
261            Option len = new Option(LEN_OPTION, true, "number of actions (default TOTAL ACTIONS, requires -info)");
262            Option filter = new Option(FILTER_OPTION, true,
263                    "status=<S1>[;status=<S2>]* (All Coordinator actions satisfying any one of the status filters will be retreived. Currently, only supported for Coordinator job)");
264            Option localtime = new Option(LOCAL_TIME_OPTION, false, "use local time (same as passing your time zone to -" +
265                    TIME_ZONE_OPTION + "). Overrides -" + TIME_ZONE_OPTION + " option");
266            Option timezone = new Option(TIME_ZONE_OPTION, true,
267                    "use time zone with the specified ID (default GMT).\nSee 'oozie info -timezones' for a list");
268            Option log = new Option(LOG_OPTION, true, "job log");
269            Option definition = new Option(DEFINITION_OPTION, true, "job definition");
270            Option config_content = new Option(CONFIG_CONTENT_OPTION, true, "job configuration");
271            Option verbose = new Option(VERBOSE_OPTION, false, "verbose mode");
272            Option action = new Option(ACTION_OPTION, true,
273                    "coordinator rerun on action ids (requires -rerun); coordinator log retrieval on action ids (requires -log)");
274            Option date = new Option(DATE_OPTION, true,
275                    "coordinator/bundle rerun on action dates (requires -rerun); coordinator log retrieval on action dates (requires -log)");
276            Option rerun_coord = new Option(RERUN_COORD_OPTION, true, "bundle rerun on coordinator names (requires -rerun)");
277            Option rerun_refresh = new Option(RERUN_REFRESH_OPTION, false,
278                    "re-materialize the coordinator rerun actions (requires -rerun)");
279            Option rerun_nocleanup = new Option(RERUN_NOCLEANUP_OPTION, false,
280                    "do not clean up output-events of the coordiantor rerun actions (requires -rerun)");
281            Option property = OptionBuilder.withArgName("property=value").hasArgs(2).withValueSeparator().withDescription(
282                    "set/override value for given property").create("D");
283    
284            Option doAs = new Option(DO_AS_OPTION, true, "doAs user, impersonates as the specified user");
285    
286            OptionGroup actions = new OptionGroup();
287            actions.addOption(submit);
288            actions.addOption(start);
289            actions.addOption(run);
290            actions.addOption(dryrun);
291            actions.addOption(suspend);
292            actions.addOption(resume);
293            actions.addOption(kill);
294            actions.addOption(change);
295            actions.addOption(info);
296            actions.addOption(rerun);
297            actions.addOption(log);
298            actions.addOption(definition);
299            actions.addOption(config_content);
300            actions.setRequired(true);
301            Options jobOptions = new Options();
302            jobOptions.addOption(oozie);
303            jobOptions.addOption(doAs);
304            jobOptions.addOption(config);
305            jobOptions.addOption(property);
306            jobOptions.addOption(changeValue);
307            jobOptions.addOption(localtime);
308            jobOptions.addOption(timezone);
309            jobOptions.addOption(verbose);
310            jobOptions.addOption(debug);
311            jobOptions.addOption(offset);
312            jobOptions.addOption(len);
313            jobOptions.addOption(filter);
314            jobOptions.addOption(action);
315            jobOptions.addOption(date);
316            jobOptions.addOption(rerun_coord);
317            jobOptions.addOption(rerun_refresh);
318            jobOptions.addOption(rerun_nocleanup);
319            jobOptions.addOptionGroup(actions);
320            addAuthOptions(jobOptions);
321            return jobOptions;
322        }
323    
324        /**
325         * Create option for command line option 'jobs'
326         * @return jobs options
327         */
328        protected Options createJobsOptions() {
329            Option oozie = new Option(OOZIE_OPTION, true, "Oozie URL");
330            Option start = new Option(OFFSET_OPTION, true, "jobs offset (default '1')");
331            Option jobtype = new Option(JOBTYPE_OPTION, true,
332                    "job type ('Supported in Oozie-2.0 or later versions ONLY - 'coordinator' or 'bundle' or 'wf'(default))");
333            Option len = new Option(LEN_OPTION, true, "number of jobs (default '100')");
334            Option filter = new Option(FILTER_OPTION, true, "user=<U>\\;name=<N>\\;group=<G>\\;status=<S>\\;frequency=<F>\\;unit=<M> " +
335                            "(Valid unit values are 'months', 'days', 'hours' or 'minutes'.)");
336            Option localtime = new Option(LOCAL_TIME_OPTION, false, "use local time (same as passing your time zone to -" +
337                    TIME_ZONE_OPTION + "). Overrides -" + TIME_ZONE_OPTION + " option");
338            Option timezone = new Option(TIME_ZONE_OPTION, true,
339                    "use time zone with the specified ID (default GMT).\nSee 'oozie info -timezones' for a list");
340            Option verbose = new Option(VERBOSE_OPTION, false, "verbose mode");
341            Option doAs = new Option(DO_AS_OPTION, true, "doAs user, impersonates as the specified user");
342            Option bulkMonitor = new Option(BULK_OPTION, true, "key-value pairs to filter bulk jobs response. e.g. bundle=<B>\\;" +
343                    "coordinators=<C>\\;actionstatus=<S>\\;startcreatedtime=<SC>\\;endcreatedtime=<EC>\\;" +
344                    "startscheduledtime=<SS>\\;endscheduledtime=<ES>\\; coordinators and actionstatus can be multiple comma separated values" +
345                    "bundle and coordinators are 'names' of those jobs. Bundle name is mandatory, other params are optional");
346            start.setType(Integer.class);
347            len.setType(Integer.class);
348            Options jobsOptions = new Options();
349            jobsOptions.addOption(oozie);
350            jobsOptions.addOption(doAs);
351            jobsOptions.addOption(localtime);
352            jobsOptions.addOption(timezone);
353            jobsOptions.addOption(start);
354            jobsOptions.addOption(len);
355            jobsOptions.addOption(oozie);
356            jobsOptions.addOption(filter);
357            jobsOptions.addOption(jobtype);
358            jobsOptions.addOption(verbose);
359            jobsOptions.addOption(bulkMonitor);
360            addAuthOptions(jobsOptions);
361            return jobsOptions;
362        }
363    
364        /**
365         * Create option for command line option 'sla'
366         *
367         * @return sla options
368         */
369        protected Options createSlaOptions() {
370            Option oozie = new Option(OOZIE_OPTION, true, "Oozie URL");
371            Option start = new Option(OFFSET_OPTION, true, "start offset (default '0')");
372            Option len = new Option(LEN_OPTION, true, "number of results (default '100', max '1000')");
373            Option filter = new Option(FILTER_OPTION, true, "filter of SLA events. e.g., jobid=<J>\\;appname=<A>");
374            start.setType(Integer.class);
375            len.setType(Integer.class);
376            Options slaOptions = new Options();
377            slaOptions.addOption(start);
378            slaOptions.addOption(len);
379            slaOptions.addOption(filter);
380            slaOptions.addOption(oozie);
381            addAuthOptions(slaOptions);
382            return slaOptions;
383        }
384    
385        /**
386         * Create option for command line option 'pig' or 'hive'
387         * @return pig or hive options
388         */
389        @SuppressWarnings("static-access")
390        protected Options createScriptLanguageOptions(String jobType) {
391            Option oozie = new Option(OOZIE_OPTION, true, "Oozie URL");
392            Option config = new Option(CONFIG_OPTION, true, "job configuration file '.properties'");
393            Option file = new Option(SCRIPTFILE_OPTION, true, jobType + " script");
394            Option property = OptionBuilder.withArgName("property=value").hasArgs(2).withValueSeparator().withDescription(
395                    "set/override value for given property").create("D");
396            Option params = OptionBuilder.withArgName("property=value").hasArgs(2).withValueSeparator().withDescription(
397                    "set parameters for script").create("P");
398            Option doAs = new Option(DO_AS_OPTION, true, "doAs user, impersonates as the specified user");
399            Options Options = new Options();
400            Options.addOption(oozie);
401            Options.addOption(doAs);
402            Options.addOption(config);
403            Options.addOption(property);
404            Options.addOption(params);
405            Options.addOption(file);
406            addAuthOptions(Options);
407            return Options;
408        }
409    
410        /**
411         * Create option for command line option 'info'
412         * @return info options
413         */
414        protected Options createInfoOptions() {
415            Option timezones = new Option(INFO_TIME_ZONES_OPTION, false, "display a list of available time zones");
416            Options infoOptions = new Options();
417            infoOptions.addOption(timezones);
418            return infoOptions;
419        }
420    
421        /**
422         * Create option for command line option 'mapreduce'
423         * @return mapreduce options
424         */
425        @SuppressWarnings("static-access")
426        protected Options createMROptions() {
427            Option oozie = new Option(OOZIE_OPTION, true, "Oozie URL");
428            Option config = new Option(CONFIG_OPTION, true, "job configuration file '.properties'");
429            Option property = OptionBuilder.withArgName("property=value").hasArgs(2).withValueSeparator().withDescription(
430                    "set/override value for given property").create("D");
431            Option doAs = new Option(DO_AS_OPTION, true, "doAs user, impersonates as the specified user");
432            Options mrOptions = new Options();
433            mrOptions.addOption(oozie);
434            mrOptions.addOption(doAs);
435            mrOptions.addOption(config);
436            mrOptions.addOption(property);
437            addAuthOptions(mrOptions);
438            return mrOptions;
439        }
440    
441        /**
442         * Run a CLI programmatically.
443         * <p/>
444         * It does not exit the JVM.
445         * <p/>
446         * A CLI instance can be used only once.
447         *
448         * @param args options and arguments for the Oozie CLI.
449         * @return '0' (success), '-1' (failure).
450         */
451        public synchronized int run(String[] args) {
452            if (used) {
453                throw new IllegalStateException("CLI instance already used");
454            }
455            used = true;
456    
457            final CLIParser parser = new CLIParser(OOZIE_OPTION, getCLIHelp());
458            parser.addCommand(HELP_CMD, "", "display usage for all commands or specified command", new Options(), false);
459            parser.addCommand(VERSION_CMD, "", "show client version", new Options(), false);
460            parser.addCommand(JOB_CMD, "", "job operations", createJobOptions(), false);
461            parser.addCommand(JOBS_CMD, "", "jobs status", createJobsOptions(), false);
462            parser.addCommand(ADMIN_CMD, "", "admin operations", createAdminOptions(), false);
463            parser.addCommand(VALIDATE_CMD, "", "validate a workflow XML file", new Options(), true);
464            parser.addCommand(SLA_CMD, "", "sla operations (Deprecated with Oozie 4.0)", createSlaOptions(), false);
465            parser.addCommand(PIG_CMD, "-X ", "submit a pig job, everything after '-X' are pass-through parameters to pig, any '-D' "
466                    + "arguments after '-X' are put in <configuration>", createScriptLanguageOptions(PIG_CMD), true);
467            parser.addCommand(HIVE_CMD, "-X ", "submit a hive job, everything after '-X' are pass-through parameters to hive, any '-D' "
468                    + "arguments after '-X' are put in <configuration>", createScriptLanguageOptions(HIVE_CMD), true);
469            parser.addCommand(INFO_CMD, "", "get more detailed info about specific topics", createInfoOptions(), false);
470            parser.addCommand(MR_CMD, "", "submit a mapreduce job", createMROptions(), false);
471    
472            try {
473                final CLIParser.Command command = parser.parse(args);
474    
475                String doAsUser = command.getCommandLine().getOptionValue(DO_AS_OPTION);
476    
477                if (doAsUser != null) {
478                    OozieClient.doAs(doAsUser, new Callable<Void>() {
479                        @Override
480                        public Void call() throws Exception {
481                            processCommand(parser, command);
482                            return null;
483                        }
484                    });
485                }
486                else {
487                    processCommand(parser, command);
488                }
489    
490                return 0;
491            }
492            catch (OozieCLIException ex) {
493                System.err.println("Error: " + ex.getMessage());
494                return -1;
495            }
496            catch (ParseException ex) {
497                System.err.println("Invalid sub-command: " + ex.getMessage());
498                System.err.println();
499                System.err.println(parser.shortHelp());
500                return -1;
501            }
502            catch (Exception ex) {
503                ex.printStackTrace();
504                System.err.println(ex.getMessage());
505                return -1;
506            }
507        }
508    
509        private void processCommand(CLIParser parser, CLIParser.Command command) throws Exception {
510            if (command.getName().equals(HELP_CMD)) {
511                parser.showHelp(command.getCommandLine());
512            }
513            else if (command.getName().equals(JOB_CMD)) {
514                jobCommand(command.getCommandLine());
515            }
516            else if (command.getName().equals(JOBS_CMD)) {
517                jobsCommand(command.getCommandLine());
518            }
519            else if (command.getName().equals(ADMIN_CMD)) {
520                adminCommand(command.getCommandLine());
521            }
522            else if (command.getName().equals(VERSION_CMD)) {
523                versionCommand();
524            }
525            else if (command.getName().equals(VALIDATE_CMD)) {
526                validateCommand(command.getCommandLine());
527            }
528            else if (command.getName().equals(SLA_CMD)) {
529                slaCommand(command.getCommandLine());
530            }
531            else if (command.getName().equals(PIG_CMD)) {
532                scriptLanguageCommand(command.getCommandLine(), PIG_CMD);
533            }
534            else if (command.getName().equals(HIVE_CMD)) {
535                scriptLanguageCommand(command.getCommandLine(), HIVE_CMD);
536            }
537            else if (command.getName().equals(INFO_CMD)) {
538                infoCommand(command.getCommandLine());
539            }
540            else if (command.getName().equals(MR_CMD)){
541                mrCommand(command.getCommandLine());
542            }
543        }
544        protected String getOozieUrl(CommandLine commandLine) {
545            String url = commandLine.getOptionValue(OOZIE_OPTION);
546            if (url == null) {
547                url = System.getenv(ENV_OOZIE_URL);
548                if (url == null) {
549                    throw new IllegalArgumentException(
550                            "Oozie URL is not available neither in command option or in the environment");
551                }
552            }
553            return url;
554        }
555    
556        private String getTimeZoneId(CommandLine commandLine)
557        {
558            if (commandLine.hasOption(LOCAL_TIME_OPTION)) {
559                return null;
560            }
561            if (commandLine.hasOption(TIME_ZONE_OPTION)) {
562                return commandLine.getOptionValue(TIME_ZONE_OPTION);
563            }
564            String timeZoneId = System.getenv(ENV_OOZIE_TIME_ZONE);
565            if (timeZoneId != null) {
566                return timeZoneId;
567            }
568            return "GMT";
569        }
570    
571        // Canibalized from Hadoop <code>Configuration.loadResource()</code>.
572        private Properties parse(InputStream is, Properties conf) throws IOException {
573            try {
574                DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
575                // ignore all comments inside the xml file
576                docBuilderFactory.setIgnoringComments(true);
577                DocumentBuilder builder = docBuilderFactory.newDocumentBuilder();
578                Document doc = builder.parse(is);
579                return parseDocument(doc, conf);
580            }
581            catch (SAXException e) {
582                throw new IOException(e);
583            }
584            catch (ParserConfigurationException e) {
585                throw new IOException(e);
586            }
587        }
588    
589        // Canibalized from Hadoop <code>Configuration.loadResource()</code>.
590        private Properties parseDocument(Document doc, Properties conf) throws IOException {
591            try {
592                Element root = doc.getDocumentElement();
593                if (!"configuration".equals(root.getTagName())) {
594                    throw new RuntimeException("bad conf file: top-level element not <configuration>");
595                }
596                NodeList props = root.getChildNodes();
597                for (int i = 0; i < props.getLength(); i++) {
598                    Node propNode = props.item(i);
599                    if (!(propNode instanceof Element)) {
600                        continue;
601                    }
602                    Element prop = (Element) propNode;
603                    if (!"property".equals(prop.getTagName())) {
604                        throw new RuntimeException("bad conf file: element not <property>");
605                    }
606                    NodeList fields = prop.getChildNodes();
607                    String attr = null;
608                    String value = null;
609                    for (int j = 0; j < fields.getLength(); j++) {
610                        Node fieldNode = fields.item(j);
611                        if (!(fieldNode instanceof Element)) {
612                            continue;
613                        }
614                        Element field = (Element) fieldNode;
615                        if ("name".equals(field.getTagName()) && field.hasChildNodes()) {
616                            attr = ((Text) field.getFirstChild()).getData();
617                        }
618                        if ("value".equals(field.getTagName()) && field.hasChildNodes()) {
619                            value = ((Text) field.getFirstChild()).getData();
620                        }
621                    }
622    
623                    if (attr != null && value != null) {
624                        conf.setProperty(attr, value);
625                    }
626                }
627                return conf;
628            }
629            catch (DOMException e) {
630                throw new IOException(e);
631            }
632        }
633    
634        private Properties getConfiguration(OozieClient wc, CommandLine commandLine) throws IOException {
635            Properties conf = wc.createConfiguration();
636            String configFile = commandLine.getOptionValue(CONFIG_OPTION);
637            if (configFile == null) {
638                throw new IOException("configuration file not specified");
639            }
640            else {
641                File file = new File(configFile);
642                if (!file.exists()) {
643                    throw new IOException("configuration file [" + configFile + "] not found");
644                }
645                if (configFile.endsWith(".properties")) {
646                    conf.load(new FileReader(file));
647                }
648                else if (configFile.endsWith(".xml")) {
649                    parse(new FileInputStream(configFile), conf);
650                }
651                else {
652                    throw new IllegalArgumentException("configuration must be a '.properties' or a '.xml' file");
653                }
654            }
655            if (commandLine.hasOption("D")) {
656                Properties commandLineProperties = commandLine.getOptionProperties("D");
657                conf.putAll(commandLineProperties);
658            }
659            return conf;
660        }
661    
662        /**
663         * @param commandLine command line string.
664         * @return change value specified by -value.
665         * @throws OozieCLIException
666         */
667        private String getChangeValue(CommandLine commandLine) throws OozieCLIException {
668            String changeValue = commandLine.getOptionValue(CHANGE_VALUE_OPTION);
669    
670            if (changeValue == null) {
671                throw new OozieCLIException("-value option needs to be specified for -change option");
672            }
673    
674            return changeValue;
675        }
676    
677        protected void addHeader(OozieClient wc) {
678            for (Map.Entry entry : System.getProperties().entrySet()) {
679                String key = (String) entry.getKey();
680                if (key.startsWith(WS_HEADER_PREFIX)) {
681                    String header = key.substring(WS_HEADER_PREFIX.length());
682                    wc.setHeader(header, (String) entry.getValue());
683                }
684            }
685        }
686    
687        /**
688         * Get auth option from command line
689         *
690         * @param commandLine the command line object
691         * @return auth option
692         */
693        protected String getAuthOption(CommandLine commandLine) {
694            String authOpt = commandLine.getOptionValue(AUTH_OPTION);
695            return authOpt;
696        }
697    
698        /**
699         * Create a OozieClient.
700         * <p/>
701         * It injects any '-Dheader:' as header to the the {@link org.apache.oozie.client.OozieClient}.
702         *
703         * @param commandLine the parsed command line options.
704         * @return a pre configured eXtended workflow client.
705         * @throws OozieCLIException thrown if the OozieClient could not be configured.
706         */
707        protected OozieClient createOozieClient(CommandLine commandLine) throws OozieCLIException {
708            return createXOozieClient(commandLine);
709        }
710    
711        /**
712         * Create a XOozieClient.
713         * <p/>
714         * It injects any '-Dheader:' as header to the the {@link org.apache.oozie.client.OozieClient}.
715         *
716         * @param commandLine the parsed command line options.
717         * @return a pre configured eXtended workflow client.
718         * @throws OozieCLIException thrown if the XOozieClient could not be configured.
719         */
720        protected XOozieClient createXOozieClient(CommandLine commandLine) throws OozieCLIException {
721            XOozieClient wc = new AuthOozieClient(getOozieUrl(commandLine), getAuthOption(commandLine));
722            addHeader(wc);
723            setDebugMode(wc,commandLine.hasOption(DEBUG_OPTION));
724            return wc;
725        }
726    
727        protected void setDebugMode(OozieClient wc, boolean debugOpt) {
728    
729            String debug = System.getenv(ENV_OOZIE_DEBUG);
730            if (debug != null && !debug.isEmpty()) {
731                int debugVal = 0;
732                try {
733                    debugVal = Integer.parseInt(debug.trim());
734                }
735                catch (Exception ex) {
736                    System.out.println("Unable to parse the debug settings. May be not an integer [" + debug + "]");
737                    ex.printStackTrace();
738                }
739                wc.setDebugMode(debugVal);
740            }
741            else if(debugOpt){  // CLI argument "-debug" used
742                wc.setDebugMode(1);
743            }
744        }
745    
746        private static String JOB_ID_PREFIX = "job: ";
747    
748        private void jobCommand(CommandLine commandLine) throws IOException, OozieCLIException {
749            XOozieClient wc = createXOozieClient(commandLine);
750    
751            List<String> options = new ArrayList<String>();
752            for (Option option : commandLine.getOptions()) {
753                options.add(option.getOpt());
754            }
755    
756            try {
757                if (options.contains(SUBMIT_OPTION)) {
758                    System.out.println(JOB_ID_PREFIX + wc.submit(getConfiguration(wc, commandLine)));
759                }
760                else if (options.contains(START_OPTION)) {
761                    wc.start(commandLine.getOptionValue(START_OPTION));
762                }
763                else if (options.contains(DRYRUN_OPTION)) {
764                    String dryrunStr = wc.dryrun(getConfiguration(wc, commandLine));
765                    if (dryrunStr.equals("OK")) {  // workflow
766                        System.out.println("OK");
767                    } else {                        // coordinator
768                        String[] dryrunStrs = dryrunStr.split("action for new instance");
769                        int arraysize = dryrunStrs.length;
770                        System.out.println("***coordJob after parsing: ***");
771                        System.out.println(dryrunStrs[0]);
772                        int aLen = dryrunStrs.length - 1;
773                        if (aLen < 0) {
774                            aLen = 0;
775                        }
776                        System.out.println("***total coord actions is " + aLen + " ***");
777                        for (int i = 1; i <= arraysize - 1; i++) {
778                            System.out.println(RULER);
779                            System.out.println("coordAction instance: " + i + ":");
780                            System.out.println(dryrunStrs[i]);
781                        }
782                    }
783                }
784                else if (options.contains(SUSPEND_OPTION)) {
785                    wc.suspend(commandLine.getOptionValue(SUSPEND_OPTION));
786                }
787                else if (options.contains(RESUME_OPTION)) {
788                    wc.resume(commandLine.getOptionValue(RESUME_OPTION));
789                }
790                else if (options.contains(KILL_OPTION)) {
791                    wc.kill(commandLine.getOptionValue(KILL_OPTION));
792                }
793                else if (options.contains(CHANGE_OPTION)) {
794                    wc.change(commandLine.getOptionValue(CHANGE_OPTION), getChangeValue(commandLine));
795                }
796                else if (options.contains(RUN_OPTION)) {
797                    System.out.println(JOB_ID_PREFIX + wc.run(getConfiguration(wc, commandLine)));
798                }
799                else if (options.contains(RERUN_OPTION)) {
800                    if (commandLine.getOptionValue(RERUN_OPTION).contains("-W")) {
801                        wc.reRun(commandLine.getOptionValue(RERUN_OPTION), getConfiguration(wc, commandLine));
802                    }
803                    else if (commandLine.getOptionValue(RERUN_OPTION).contains("-B")) {
804                        String bundleJobId = commandLine.getOptionValue(RERUN_OPTION);
805                        String coordScope = null;
806                        String dateScope = null;
807                        boolean refresh = false;
808                        boolean noCleanup = false;
809                        if (options.contains(ACTION_OPTION)) {
810                            throw new OozieCLIException("Invalid options provided for bundle rerun. " + ACTION_OPTION
811                                    + " is not valid for bundle rerun");
812                        }
813                        if (options.contains(DATE_OPTION)) {
814                            dateScope = commandLine.getOptionValue(DATE_OPTION);
815                        }
816    
817                        if (options.contains(RERUN_COORD_OPTION)) {
818                            coordScope = commandLine.getOptionValue(RERUN_COORD_OPTION);
819                        }
820    
821                        if (options.contains(RERUN_REFRESH_OPTION)) {
822                            refresh = true;
823                        }
824                        if (options.contains(RERUN_NOCLEANUP_OPTION)) {
825                            noCleanup = true;
826                        }
827                        wc.reRunBundle(bundleJobId, coordScope, dateScope, refresh, noCleanup);
828                        if (coordScope != null && !coordScope.isEmpty()) {
829                            System.out.println("Coordinators [" + coordScope + "] of bundle " + bundleJobId
830                                    + " are scheduled to rerun on date ranges [" + dateScope + "].");
831                        }
832                        else {
833                            System.out.println("All coordinators of bundle " + bundleJobId
834                                    + " are scheduled to rerun on the date ranges [" + dateScope + "].");
835                        }
836                    }
837                    else {
838                        String coordJobId = commandLine.getOptionValue(RERUN_OPTION);
839                        String scope = null;
840                        String rerunType = null;
841                        boolean refresh = false;
842                        boolean noCleanup = false;
843                        if (options.contains(DATE_OPTION) && options.contains(ACTION_OPTION)) {
844                            throw new OozieCLIException("Invalid options provided for rerun: either" + DATE_OPTION + " or "
845                                    + ACTION_OPTION + " expected. Don't use both at the same time.");
846                        }
847                        if (options.contains(DATE_OPTION)) {
848                            rerunType = RestConstants.JOB_COORD_RERUN_DATE;
849                            scope = commandLine.getOptionValue(DATE_OPTION);
850                        }
851                        else if (options.contains(ACTION_OPTION)) {
852                            rerunType = RestConstants.JOB_COORD_RERUN_ACTION;
853                            scope = commandLine.getOptionValue(ACTION_OPTION);
854                        }
855                        else {
856                            throw new OozieCLIException("Invalid options provided for rerun: " + DATE_OPTION + " or "
857                                    + ACTION_OPTION + " expected.");
858                        }
859                        if (options.contains(RERUN_REFRESH_OPTION)) {
860                            refresh = true;
861                        }
862                        if (options.contains(RERUN_NOCLEANUP_OPTION)) {
863                            noCleanup = true;
864                        }
865                        printRerunCoordActions(wc.reRunCoord(coordJobId, rerunType, scope, refresh, noCleanup));
866                    }
867                }
868                else if (options.contains(INFO_OPTION)) {
869                    String timeZoneId = getTimeZoneId(commandLine);
870                    final String optionValue = commandLine.getOptionValue(INFO_OPTION);
871                    if (optionValue.endsWith("-B")) {
872                        String filter = commandLine.getOptionValue(FILTER_OPTION);
873                        if (filter != null) {
874                            throw new OozieCLIException("Filter option is currently not supported for a Bundle job");
875                        }
876                        printBundleJob(wc.getBundleJobInfo(optionValue), timeZoneId,
877                                options.contains(VERBOSE_OPTION));
878                    }
879                    else if (optionValue.endsWith("-C")) {
880                        String s = commandLine.getOptionValue(OFFSET_OPTION);
881                        int start = Integer.parseInt((s != null) ? s : "-1");
882                        s = commandLine.getOptionValue(LEN_OPTION);
883                        int len = Integer.parseInt((s != null) ? s : "-1");
884                        String filter = commandLine.getOptionValue(FILTER_OPTION);
885                        printCoordJob(wc.getCoordJobInfo(optionValue, filter, start, len), timeZoneId,
886                                options.contains(VERBOSE_OPTION));
887                    }
888                    else if (optionValue.contains("-C@")) {
889                        String filter = commandLine.getOptionValue(FILTER_OPTION);
890                        if (filter != null) {
891                            throw new OozieCLIException("Filter option is not supported for a Coordinator action");
892                        }
893                        printCoordAction(wc.getCoordActionInfo(optionValue), timeZoneId);
894                    }
895                    else if (optionValue.contains("-W@")) {
896                        String filter = commandLine.getOptionValue(FILTER_OPTION);
897                        if (filter != null) {
898                            throw new OozieCLIException("Filter option is not supported for a Workflow action");
899                        }
900                        printWorkflowAction(wc.getWorkflowActionInfo(optionValue), timeZoneId,
901                                options.contains(VERBOSE_OPTION));
902                    }
903                    else {
904                        String filter = commandLine.getOptionValue(FILTER_OPTION);
905                        if (filter != null) {
906                            throw new OozieCLIException("Filter option is currently not supported for a Workflow job");
907                        }
908                        String s = commandLine.getOptionValue(OFFSET_OPTION);
909                        int start = Integer.parseInt((s != null) ? s : "0");
910                        s = commandLine.getOptionValue(LEN_OPTION);
911                        String jobtype = commandLine.getOptionValue(JOBTYPE_OPTION);
912                        jobtype = (jobtype != null) ? jobtype : "wf";
913                        int len = Integer.parseInt((s != null) ? s : "0");
914                        printJob(wc.getJobInfo(optionValue, start, len), timeZoneId,
915                                options.contains(VERBOSE_OPTION));
916                    }
917                }
918                else if (options.contains(LOG_OPTION)) {
919                    PrintStream ps = System.out;
920                    if (commandLine.getOptionValue(LOG_OPTION).contains("-C")) {
921                        String logRetrievalScope = null;
922                        String logRetrievalType = null;
923                        if (options.contains(ACTION_OPTION)) {
924                            logRetrievalType = RestConstants.JOB_LOG_ACTION;
925                            logRetrievalScope = commandLine.getOptionValue(ACTION_OPTION);
926                        }
927                        if (options.contains(DATE_OPTION)) {
928                            logRetrievalType = RestConstants.JOB_LOG_DATE;
929                            logRetrievalScope = commandLine.getOptionValue(DATE_OPTION);
930                        }
931                        try {
932                            wc.getJobLog(commandLine.getOptionValue(LOG_OPTION), logRetrievalType, logRetrievalScope, ps);
933                        }
934                        finally {
935                            ps.close();
936                        }
937                    }
938                    else {
939                        if (!options.contains(ACTION_OPTION) && !options.contains(DATE_OPTION)) {
940                            wc.getJobLog(commandLine.getOptionValue(LOG_OPTION), null, null, ps);
941                        }
942                        else {
943                            throw new OozieCLIException("Invalid options provided for log retrieval. " + ACTION_OPTION
944                                    + " and " + DATE_OPTION + " are valid only for coordinator job log retrieval");
945                        }
946                    }
947                }
948                else if (options.contains(DEFINITION_OPTION)) {
949                    System.out.println(wc.getJobDefinition(commandLine.getOptionValue(DEFINITION_OPTION)));
950                }
951                else if (options.contains(CONFIG_CONTENT_OPTION)) {
952                    if (commandLine.getOptionValue(CONFIG_CONTENT_OPTION).endsWith("-C")) {
953                        System.out.println(wc.getCoordJobInfo(commandLine.getOptionValue(CONFIG_CONTENT_OPTION)).getConf());
954                    }
955                    else if (commandLine.getOptionValue(CONFIG_CONTENT_OPTION).endsWith("-W")) {
956                        System.out.println(wc.getJobInfo(commandLine.getOptionValue(CONFIG_CONTENT_OPTION)).getConf());
957                    }
958                    else if (commandLine.getOptionValue(CONFIG_CONTENT_OPTION).endsWith("-B")) {
959                        System.out
960                                .println(wc.getBundleJobInfo(commandLine.getOptionValue(CONFIG_CONTENT_OPTION)).getConf());
961                    }
962                    else {
963                        System.out.println("ERROR:  job id [" + commandLine.getOptionValue(CONFIG_CONTENT_OPTION)
964                                + "] doesn't end with either C or W or B");
965                    }
966                }
967            }
968            catch (OozieClientException ex) {
969                throw new OozieCLIException(ex.toString(), ex);
970            }
971        }
972    
973        @VisibleForTesting
974        void printCoordJob(CoordinatorJob coordJob, String timeZoneId, boolean verbose) {
975            System.out.println("Job ID : " + coordJob.getId());
976    
977            System.out.println(RULER);
978    
979            List<CoordinatorAction> actions = coordJob.getActions();
980            System.out.println("Job Name    : " + maskIfNull(coordJob.getAppName()));
981            System.out.println("App Path    : " + maskIfNull(coordJob.getAppPath()));
982            System.out.println("Status      : " + coordJob.getStatus());
983            System.out.println("Start Time  : " + maskDate(coordJob.getStartTime(), timeZoneId, false));
984            System.out.println("End Time    : " + maskDate(coordJob.getEndTime(), timeZoneId, false));
985            System.out.println("Pause Time  : " + maskDate(coordJob.getPauseTime(), timeZoneId, false));
986            System.out.println("Concurrency : " + coordJob.getConcurrency());
987            System.out.println(RULER);
988    
989            if (verbose) {
990                System.out.println("ID" + VERBOSE_DELIMITER + "Action Number" + VERBOSE_DELIMITER + "Console URL"
991                        + VERBOSE_DELIMITER + "Error Code" + VERBOSE_DELIMITER + "Error Message" + VERBOSE_DELIMITER
992                        + "External ID" + VERBOSE_DELIMITER + "External Status" + VERBOSE_DELIMITER + "Job ID"
993                        + VERBOSE_DELIMITER + "Tracker URI" + VERBOSE_DELIMITER + "Created" + VERBOSE_DELIMITER
994                        + "Nominal Time" + VERBOSE_DELIMITER + "Status" + VERBOSE_DELIMITER + "Last Modified"
995                        + VERBOSE_DELIMITER + "Missing Dependencies");
996                System.out.println(RULER);
997    
998                for (CoordinatorAction action : actions) {
999                    System.out.println(maskIfNull(action.getId()) + VERBOSE_DELIMITER + action.getActionNumber()
1000                            + VERBOSE_DELIMITER + maskIfNull(action.getConsoleUrl()) + VERBOSE_DELIMITER
1001                            + maskIfNull(action.getErrorCode()) + VERBOSE_DELIMITER + maskIfNull(action.getErrorMessage())
1002                            + VERBOSE_DELIMITER + maskIfNull(action.getExternalId()) + VERBOSE_DELIMITER
1003                            + maskIfNull(action.getExternalStatus()) + VERBOSE_DELIMITER + maskIfNull(action.getJobId())
1004                            + VERBOSE_DELIMITER + maskIfNull(action.getTrackerUri()) + VERBOSE_DELIMITER
1005                            + maskDate(action.getCreatedTime(), timeZoneId, verbose) + VERBOSE_DELIMITER
1006                            + maskDate(action.getNominalTime(), timeZoneId, verbose) + action.getStatus() + VERBOSE_DELIMITER
1007                            + maskDate(action.getLastModifiedTime(), timeZoneId, verbose) + VERBOSE_DELIMITER
1008                            + maskIfNull(getFirstMissingDependencies(action)));
1009    
1010                    System.out.println(RULER);
1011                }
1012            }
1013            else {
1014                System.out.println(String.format(COORD_ACTION_FORMATTER, "ID", "Status", "Ext ID", "Err Code", "Created",
1015                        "Nominal Time", "Last Mod"));
1016    
1017                for (CoordinatorAction action : actions) {
1018                    System.out.println(String.format(COORD_ACTION_FORMATTER, maskIfNull(action.getId()),
1019                            action.getStatus(), maskIfNull(action.getExternalId()), maskIfNull(action.getErrorCode()),
1020                            maskDate(action.getCreatedTime(), timeZoneId, verbose), maskDate(action.getNominalTime(), timeZoneId, verbose),
1021                            maskDate(action.getLastModifiedTime(), timeZoneId, verbose)));
1022    
1023                    System.out.println(RULER);
1024                }
1025            }
1026        }
1027    
1028        @VisibleForTesting
1029        void printBundleJob(BundleJob bundleJob, String timeZoneId, boolean verbose) {
1030            System.out.println("Job ID : " + bundleJob.getId());
1031    
1032            System.out.println(RULER);
1033    
1034            List<CoordinatorJob> coordinators = bundleJob.getCoordinators();
1035            System.out.println("Job Name : " + maskIfNull(bundleJob.getAppName()));
1036            System.out.println("App Path : " + maskIfNull(bundleJob.getAppPath()));
1037            System.out.println("Status   : " + bundleJob.getStatus());
1038            System.out.println("Kickoff time   : " + bundleJob.getKickoffTime());
1039            System.out.println(RULER);
1040    
1041            System.out.println(String.format(BUNDLE_COORD_JOBS_FORMATTER, "Job ID", "Status", "Freq", "Unit", "Started",
1042                    "Next Materialized"));
1043            System.out.println(RULER);
1044    
1045            for (CoordinatorJob job : coordinators) {
1046                System.out.println(String.format(BUNDLE_COORD_JOBS_FORMATTER, maskIfNull(job.getId()), job.getStatus(),
1047                        job.getFrequency(), job.getTimeUnit(), maskDate(job.getStartTime(), timeZoneId, verbose),
1048                        maskDate(job.getNextMaterializedTime(), timeZoneId, verbose)));
1049    
1050                System.out.println(RULER);
1051            }
1052        }
1053    
1054        @VisibleForTesting
1055        void printCoordAction(CoordinatorAction coordAction, String timeZoneId) {
1056            System.out.println("ID : " + maskIfNull(coordAction.getId()));
1057    
1058            System.out.println(RULER);
1059    
1060            System.out.println("Action Number        : " + coordAction.getActionNumber());
1061            System.out.println("Console URL          : " + maskIfNull(coordAction.getConsoleUrl()));
1062            System.out.println("Error Code           : " + maskIfNull(coordAction.getErrorCode()));
1063            System.out.println("Error Message        : " + maskIfNull(coordAction.getErrorMessage()));
1064            System.out.println("External ID          : " + maskIfNull(coordAction.getExternalId()));
1065            System.out.println("External Status      : " + maskIfNull(coordAction.getExternalStatus()));
1066            System.out.println("Job ID               : " + maskIfNull(coordAction.getJobId()));
1067            System.out.println("Tracker URI          : " + maskIfNull(coordAction.getTrackerUri()));
1068            System.out.println("Created              : " + maskDate(coordAction.getCreatedTime(), timeZoneId, false));
1069            System.out.println("Nominal Time         : " + maskDate(coordAction.getNominalTime(), timeZoneId, false));
1070            System.out.println("Status               : " + coordAction.getStatus());
1071            System.out.println("Last Modified        : " + maskDate(coordAction.getLastModifiedTime(), timeZoneId, false));
1072            System.out.println("First Missing Dependency : " + maskIfNull(getFirstMissingDependencies(coordAction)));
1073    
1074            System.out.println(RULER);
1075        }
1076    
1077        private void printRerunCoordActions(List<CoordinatorAction> actions) {
1078            if (actions != null && actions.size() > 0) {
1079                System.out.println("Action ID" + VERBOSE_DELIMITER + "Nominal Time");
1080                System.out.println(RULER);
1081                for (CoordinatorAction action : actions) {
1082                    System.out.println(maskIfNull(action.getId()) + VERBOSE_DELIMITER
1083                            + maskDate(action.getNominalTime(), null,false));
1084                }
1085            }
1086            else {
1087                System.out.println("No Actions match your rerun criteria!");
1088            }
1089        }
1090    
1091    
1092        @VisibleForTesting
1093        void printWorkflowAction(WorkflowAction action, String timeZoneId, boolean verbose) {
1094    
1095            System.out.println("ID : " + maskIfNull(action.getId()));
1096    
1097            System.out.println(RULER);
1098    
1099            System.out.println("Console URL       : " + maskIfNull(action.getConsoleUrl()));
1100            System.out.println("Error Code        : " + maskIfNull(action.getErrorCode()));
1101            System.out.println("Error Message     : " + maskIfNull(action.getErrorMessage()));
1102            System.out.println("External ID       : " + maskIfNull(action.getExternalId()));
1103            System.out.println("External Status   : " + maskIfNull(action.getExternalStatus()));
1104            System.out.println("Name              : " + maskIfNull(action.getName()));
1105            System.out.println("Retries           : " + action.getRetries());
1106            System.out.println("Tracker URI       : " + maskIfNull(action.getTrackerUri()));
1107            System.out.println("Type              : " + maskIfNull(action.getType()));
1108            System.out.println("Started           : " + maskDate(action.getStartTime(), timeZoneId, verbose));
1109            System.out.println("Status            : " + action.getStatus());
1110            System.out.println("Ended             : " + maskDate(action.getEndTime(), timeZoneId, verbose));
1111    
1112            if (verbose) {
1113                System.out.println("External Stats    : " + action.getStats());
1114                System.out.println("External ChildIDs : " + action.getExternalChildIDs());
1115            }
1116    
1117            System.out.println(RULER);
1118        }
1119    
1120        private static final String WORKFLOW_JOBS_FORMATTER = "%-41s%-13s%-10s%-10s%-10s%-24s%-24s";
1121        private static final String COORD_JOBS_FORMATTER = "%-41s%-15s%-10s%-5s%-13s%-24s%-24s";
1122        private static final String BUNDLE_JOBS_FORMATTER = "%-41s%-15s%-10s%-20s%-20s%-13s%-13s";
1123        private static final String BUNDLE_COORD_JOBS_FORMATTER = "%-41s%-15s%-5s%-13s%-24s%-24s";
1124    
1125        private static final String WORKFLOW_ACTION_FORMATTER = "%-78s%-10s%-23s%-11s%-10s";
1126        private static final String COORD_ACTION_FORMATTER = "%-43s%-10s%-37s%-10s%-21s%-21s";
1127        private static final String BULK_RESPONSE_FORMATTER = "%-13s%-38s%-13s%-41s%-10s%-38s%-21s%-38s";
1128    
1129        @VisibleForTesting
1130        void printJob(WorkflowJob job, String timeZoneId, boolean verbose) throws IOException {
1131            System.out.println("Job ID : " + maskIfNull(job.getId()));
1132    
1133            System.out.println(RULER);
1134    
1135            System.out.println("Workflow Name : " + maskIfNull(job.getAppName()));
1136            System.out.println("App Path      : " + maskIfNull(job.getAppPath()));
1137            System.out.println("Status        : " + job.getStatus());
1138            System.out.println("Run           : " + job.getRun());
1139            System.out.println("User          : " + maskIfNull(job.getUser()));
1140            System.out.println("Group         : " + maskIfNull(job.getGroup()));
1141            System.out.println("Created       : " + maskDate(job.getCreatedTime(), timeZoneId, verbose));
1142            System.out.println("Started       : " + maskDate(job.getStartTime(), timeZoneId, verbose));
1143            System.out.println("Last Modified : " + maskDate(job.getLastModifiedTime(), timeZoneId, verbose));
1144            System.out.println("Ended         : " + maskDate(job.getEndTime(), timeZoneId, verbose));
1145            System.out.println("CoordAction ID: " + maskIfNull(job.getParentId()));
1146    
1147            List<WorkflowAction> actions = job.getActions();
1148    
1149            if (actions != null && actions.size() > 0) {
1150                System.out.println();
1151                System.out.println("Actions");
1152                System.out.println(RULER);
1153    
1154                if (verbose) {
1155                    System.out.println("ID" + VERBOSE_DELIMITER + "Console URL" + VERBOSE_DELIMITER + "Error Code"
1156                            + VERBOSE_DELIMITER + "Error Message" + VERBOSE_DELIMITER + "External ID" + VERBOSE_DELIMITER
1157                            + "External Status" + VERBOSE_DELIMITER + "Name" + VERBOSE_DELIMITER + "Retries"
1158                            + VERBOSE_DELIMITER + "Tracker URI" + VERBOSE_DELIMITER + "Type" + VERBOSE_DELIMITER
1159                            + "Started" + VERBOSE_DELIMITER + "Status" + VERBOSE_DELIMITER + "Ended");
1160                    System.out.println(RULER);
1161    
1162                    for (WorkflowAction action : job.getActions()) {
1163                        System.out.println(maskIfNull(action.getId()) + VERBOSE_DELIMITER
1164                                + maskIfNull(action.getConsoleUrl()) + VERBOSE_DELIMITER
1165                                + maskIfNull(action.getErrorCode()) + VERBOSE_DELIMITER
1166                                + maskIfNull(action.getErrorMessage()) + VERBOSE_DELIMITER
1167                                + maskIfNull(action.getExternalId()) + VERBOSE_DELIMITER
1168                                + maskIfNull(action.getExternalStatus()) + VERBOSE_DELIMITER + maskIfNull(action.getName())
1169                                + VERBOSE_DELIMITER + action.getRetries() + VERBOSE_DELIMITER
1170                                + maskIfNull(action.getTrackerUri()) + VERBOSE_DELIMITER + maskIfNull(action.getType())
1171                                + VERBOSE_DELIMITER + maskDate(action.getStartTime(), timeZoneId, verbose)
1172                                + VERBOSE_DELIMITER + action.getStatus() + VERBOSE_DELIMITER
1173                                + maskDate(action.getEndTime(), timeZoneId, verbose));
1174    
1175                        System.out.println(RULER);
1176                    }
1177                }
1178                else {
1179                    System.out.println(String.format(WORKFLOW_ACTION_FORMATTER, "ID", "Status", "Ext ID", "Ext Status",
1180                            "Err Code"));
1181    
1182                    System.out.println(RULER);
1183    
1184                    for (WorkflowAction action : job.getActions()) {
1185                        System.out.println(String.format(WORKFLOW_ACTION_FORMATTER, maskIfNull(action.getId()), action
1186                                .getStatus(), maskIfNull(action.getExternalId()), maskIfNull(action.getExternalStatus()),
1187                                maskIfNull(action.getErrorCode())));
1188    
1189                        System.out.println(RULER);
1190                    }
1191                }
1192            }
1193            else {
1194                System.out.println(RULER);
1195            }
1196    
1197            System.out.println();
1198        }
1199    
1200        private void jobsCommand(CommandLine commandLine) throws IOException, OozieCLIException {
1201            XOozieClient wc = createXOozieClient(commandLine);
1202    
1203            String filter = commandLine.getOptionValue(FILTER_OPTION);
1204            String s = commandLine.getOptionValue(OFFSET_OPTION);
1205            int start = Integer.parseInt((s != null) ? s : "0");
1206            s = commandLine.getOptionValue(LEN_OPTION);
1207            String jobtype = commandLine.getOptionValue(JOBTYPE_OPTION);
1208            String timeZoneId = getTimeZoneId(commandLine);
1209            jobtype = (jobtype != null) ? jobtype : "wf";
1210            int len = Integer.parseInt((s != null) ? s : "0");
1211            String bulkFilterString = commandLine.getOptionValue(BULK_OPTION);
1212    
1213            try {
1214                if (bulkFilterString != null) {
1215                    printBulkJobs(wc.getBulkInfo(bulkFilterString, start, len), timeZoneId, commandLine.hasOption(VERBOSE_OPTION));
1216                }
1217                else if (jobtype.toLowerCase().contains("wf")) {
1218                    printJobs(wc.getJobsInfo(filter, start, len), timeZoneId, commandLine.hasOption(VERBOSE_OPTION));
1219                }
1220                else if (jobtype.toLowerCase().startsWith("coord")) {
1221                    printCoordJobs(wc.getCoordJobsInfo(filter, start, len), timeZoneId, commandLine.hasOption(VERBOSE_OPTION));
1222                }
1223                else if (jobtype.toLowerCase().startsWith("bundle")) {
1224                    printBundleJobs(wc.getBundleJobsInfo(filter, start, len), timeZoneId, commandLine.hasOption(VERBOSE_OPTION));
1225                }
1226    
1227            }
1228            catch (OozieClientException ex) {
1229                throw new OozieCLIException(ex.toString(), ex);
1230            }
1231        }
1232    
1233        @VisibleForTesting
1234        void printCoordJobs(List<CoordinatorJob> jobs, String timeZoneId, boolean verbose) throws IOException {
1235            if (jobs != null && jobs.size() > 0) {
1236                if (verbose) {
1237                    System.out.println("Job ID" + VERBOSE_DELIMITER + "App Name" + VERBOSE_DELIMITER + "App Path"
1238                            + VERBOSE_DELIMITER + "Console URL" + VERBOSE_DELIMITER + "User" + VERBOSE_DELIMITER + "Group"
1239                            + VERBOSE_DELIMITER + "Concurrency" + VERBOSE_DELIMITER + "Frequency" + VERBOSE_DELIMITER
1240                            + "Time Unit" + VERBOSE_DELIMITER + "Time Zone" + VERBOSE_DELIMITER + "Time Out"
1241                            + VERBOSE_DELIMITER + "Started" + VERBOSE_DELIMITER + "Next Materialize" + VERBOSE_DELIMITER
1242                            + "Status" + VERBOSE_DELIMITER + "Last Action" + VERBOSE_DELIMITER + "Ended");
1243                    System.out.println(RULER);
1244    
1245                    for (CoordinatorJob job : jobs) {
1246                        System.out.println(maskIfNull(job.getId()) + VERBOSE_DELIMITER + maskIfNull(job.getAppName())
1247                                + VERBOSE_DELIMITER + maskIfNull(job.getAppPath()) + VERBOSE_DELIMITER
1248                                + maskIfNull(job.getConsoleUrl()) + VERBOSE_DELIMITER + maskIfNull(job.getUser())
1249                                + VERBOSE_DELIMITER + maskIfNull(job.getGroup()) + VERBOSE_DELIMITER + job.getConcurrency()
1250                                + VERBOSE_DELIMITER + job.getFrequency() + VERBOSE_DELIMITER + job.getTimeUnit()
1251                                + VERBOSE_DELIMITER + maskIfNull(job.getTimeZone()) + VERBOSE_DELIMITER + job.getTimeout()
1252                                + VERBOSE_DELIMITER + maskDate(job.getStartTime(), timeZoneId, verbose) + VERBOSE_DELIMITER
1253                                + maskDate(job.getNextMaterializedTime(), timeZoneId, verbose) + VERBOSE_DELIMITER
1254                                + job.getStatus() + VERBOSE_DELIMITER
1255                                + maskDate(job.getLastActionTime(), timeZoneId, verbose) + VERBOSE_DELIMITER
1256                                + maskDate(job.getEndTime(), timeZoneId, verbose));
1257    
1258                        System.out.println(RULER);
1259                    }
1260                }
1261                else {
1262                    System.out.println(String.format(COORD_JOBS_FORMATTER, "Job ID", "App Name", "Status", "Freq", "Unit",
1263                            "Started", "Next Materialized"));
1264                    System.out.println(RULER);
1265    
1266                    for (CoordinatorJob job : jobs) {
1267                        System.out.println(String.format(COORD_JOBS_FORMATTER, maskIfNull(job.getId()), maskIfNull(job
1268                                .getAppName()), job.getStatus(), job.getFrequency(), job.getTimeUnit(), maskDate(job
1269                                .getStartTime(), timeZoneId, verbose), maskDate(job.getNextMaterializedTime(), timeZoneId, verbose)));
1270    
1271                        System.out.println(RULER);
1272                    }
1273                }
1274            }
1275            else {
1276                System.out.println("No Jobs match your criteria!");
1277            }
1278        }
1279    
1280        @VisibleForTesting
1281        void printBulkJobs(List<BulkResponse> jobs, String timeZoneId, boolean verbose) throws IOException {
1282            if (jobs != null && jobs.size() > 0) {
1283                for (BulkResponse response : jobs) {
1284                    BundleJob bundle = response.getBundle();
1285                    CoordinatorJob coord = response.getCoordinator();
1286                    CoordinatorAction action = response.getAction();
1287                    if (verbose) {
1288                        System.out.println();
1289                        System.out.println("Bundle Name : " + maskIfNull(bundle.getAppName()));
1290    
1291                        System.out.println(RULER);
1292    
1293                        System.out.println("Bundle ID        : " + maskIfNull(bundle.getId()));
1294                        System.out.println("Coordinator Name : " + maskIfNull(coord.getAppName()));
1295                        System.out.println("Coord Action ID  : " + maskIfNull(action.getId()));
1296                        System.out.println("Action Status    : " + action.getStatus());
1297                        System.out.println("External ID      : " + maskIfNull(action.getExternalId()));
1298                        System.out.println("Created Time     : " + maskDate(action.getCreatedTime(), timeZoneId, false));
1299                        System.out.println("User             : " + maskIfNull(bundle.getUser()));
1300                        System.out.println("Error Message    : " + maskIfNull(action.getErrorMessage()));
1301                        System.out.println(RULER);
1302                    }
1303                    else {
1304                        System.out.println(String.format(BULK_RESPONSE_FORMATTER, "Bundle Name", "Bundle ID", "Coord Name",
1305                                "Coord Action ID", "Status", "External ID", "Created Time", "Error Message"));
1306                        System.out.println(RULER);
1307                        System.out
1308                                .println(String.format(BULK_RESPONSE_FORMATTER, maskIfNull(bundle.getAppName()),
1309                                        maskIfNull(bundle.getId()), maskIfNull(coord.getAppName()),
1310                                        maskIfNull(action.getId()), action.getStatus(), maskIfNull(action.getExternalId()),
1311                                        maskDate(action.getCreatedTime(), timeZoneId, false),
1312                                        maskIfNull(action.getErrorMessage())));
1313                        System.out.println(RULER);
1314                    }
1315                }
1316            }
1317            else {
1318                System.out.println("Bulk request criteria did not match any coordinator actions");
1319            }
1320        }
1321    
1322        @VisibleForTesting
1323        void printBundleJobs(List<BundleJob> jobs, String timeZoneId, boolean verbose) throws IOException {
1324            if (jobs != null && jobs.size() > 0) {
1325                if (verbose) {
1326                    System.out.println("Job ID" + VERBOSE_DELIMITER + "Bundle Name" + VERBOSE_DELIMITER + "Bundle Path"
1327                            + VERBOSE_DELIMITER + "User" + VERBOSE_DELIMITER + "Group" + VERBOSE_DELIMITER + "Status"
1328                            + VERBOSE_DELIMITER + "Kickoff" + VERBOSE_DELIMITER + "Pause" + VERBOSE_DELIMITER + "Created"
1329                            + VERBOSE_DELIMITER + "Console URL");
1330                    System.out.println(RULER);
1331    
1332                    for (BundleJob job : jobs) {
1333                        System.out.println(maskIfNull(job.getId()) + VERBOSE_DELIMITER + maskIfNull(job.getAppName())
1334                                + VERBOSE_DELIMITER + maskIfNull(job.getAppPath()) + VERBOSE_DELIMITER
1335                                + maskIfNull(job.getUser()) + VERBOSE_DELIMITER + maskIfNull(job.getGroup())
1336                                + VERBOSE_DELIMITER + job.getStatus() + VERBOSE_DELIMITER
1337                                + maskDate(job.getKickoffTime(), timeZoneId, verbose) + VERBOSE_DELIMITER
1338                                + maskDate(job.getPauseTime(), timeZoneId, verbose) + VERBOSE_DELIMITER
1339                                + maskDate(job.getCreatedTime(), timeZoneId, verbose) + VERBOSE_DELIMITER
1340                                + maskIfNull(job.getConsoleUrl()));
1341    
1342                        System.out.println(RULER);
1343                    }
1344                }
1345                else {
1346                    System.out.println(String.format(BUNDLE_JOBS_FORMATTER, "Job ID", "Bundle Name", "Status", "Kickoff",
1347                            "Created", "User", "Group"));
1348                    System.out.println(RULER);
1349    
1350                    for (BundleJob job : jobs) {
1351                        System.out.println(String.format(BUNDLE_JOBS_FORMATTER, maskIfNull(job.getId()),
1352                                maskIfNull(job.getAppName()), job.getStatus(),
1353                                maskDate(job.getKickoffTime(), timeZoneId, verbose),
1354                                maskDate(job.getCreatedTime(), timeZoneId, verbose), maskIfNull(job.getUser()),
1355                                maskIfNull(job.getGroup())));
1356                        System.out.println(RULER);
1357                    }
1358                }
1359            }
1360            else {
1361                System.out.println("No Jobs match your criteria!");
1362            }
1363        }
1364    
1365        private void slaCommand(CommandLine commandLine) throws IOException, OozieCLIException {
1366            XOozieClient wc = createXOozieClient(commandLine);
1367            List<String> options = new ArrayList<String>();
1368            for (Option option : commandLine.getOptions()) {
1369                options.add(option.getOpt());
1370            }
1371    
1372            String s = commandLine.getOptionValue(OFFSET_OPTION);
1373            int start = Integer.parseInt((s != null) ? s : "0");
1374            s = commandLine.getOptionValue(LEN_OPTION);
1375            int len = Integer.parseInt((s != null) ? s : "100");
1376            String filter = commandLine.getOptionValue(FILTER_OPTION);
1377    
1378            try {
1379                wc.getSlaInfo(start, len, filter);
1380            }
1381            catch (OozieClientException ex) {
1382                throw new OozieCLIException(ex.toString(), ex);
1383            }
1384        }
1385    
1386        private void adminCommand(CommandLine commandLine) throws OozieCLIException {
1387            XOozieClient wc = createXOozieClient(commandLine);
1388    
1389            List<String> options = new ArrayList<String>();
1390            for (Option option : commandLine.getOptions()) {
1391                options.add(option.getOpt());
1392            }
1393    
1394            try {
1395                SYSTEM_MODE status = SYSTEM_MODE.NORMAL;
1396                if (options.contains(VERSION_OPTION)) {
1397                    System.out.println("Oozie server build version: " + wc.getServerBuildVersion());
1398                }
1399                else if (options.contains(SYSTEM_MODE_OPTION)) {
1400                    String systemModeOption = commandLine.getOptionValue(SYSTEM_MODE_OPTION).toUpperCase();
1401                    try {
1402                        status = SYSTEM_MODE.valueOf(systemModeOption);
1403                    }
1404                    catch (Exception e) {
1405                        throw new OozieCLIException("Invalid input provided for option: " + SYSTEM_MODE_OPTION
1406                                + " value given :" + systemModeOption
1407                                + " Expected values are: NORMAL/NOWEBSERVICE/SAFEMODE ");
1408                    }
1409                    wc.setSystemMode(status);
1410                    System.out.println("System mode: " + status);
1411                }
1412                else if (options.contains(STATUS_OPTION)) {
1413                    status = wc.getSystemMode();
1414                    System.out.println("System mode: " + status);
1415                }
1416                else if (options.contains(QUEUE_DUMP_OPTION)) {
1417    
1418                    List<String> list = wc.getQueueDump();
1419                    if (list != null && list.size() != 0) {
1420                        for (String str : list) {
1421                            System.out.println(str);
1422                        }
1423                    }
1424                    else {
1425                        System.out.println("QueueDump is null!");
1426                    }
1427                }
1428            }
1429            catch (OozieClientException ex) {
1430                throw new OozieCLIException(ex.toString(), ex);
1431            }
1432        }
1433    
1434        private void versionCommand() throws OozieCLIException {
1435            System.out.println("Oozie client build version: "
1436                    + BuildInfo.getBuildInfo().getProperty(BuildInfo.BUILD_VERSION));
1437        }
1438    
1439        @VisibleForTesting
1440        void printJobs(List<WorkflowJob> jobs, String timeZoneId, boolean verbose) throws IOException {
1441            if (jobs != null && jobs.size() > 0) {
1442                if (verbose) {
1443                    System.out.println("Job ID" + VERBOSE_DELIMITER + "App Name" + VERBOSE_DELIMITER + "App Path"
1444                            + VERBOSE_DELIMITER + "Console URL" + VERBOSE_DELIMITER + "User" + VERBOSE_DELIMITER + "Group"
1445                            + VERBOSE_DELIMITER + "Run" + VERBOSE_DELIMITER + "Created" + VERBOSE_DELIMITER + "Started"
1446                            + VERBOSE_DELIMITER + "Status" + VERBOSE_DELIMITER + "Last Modified" + VERBOSE_DELIMITER
1447                            + "Ended");
1448                    System.out.println(RULER);
1449    
1450                    for (WorkflowJob job : jobs) {
1451                        System.out.println(maskIfNull(job.getId()) + VERBOSE_DELIMITER + maskIfNull(job.getAppName())
1452                                + VERBOSE_DELIMITER + maskIfNull(job.getAppPath()) + VERBOSE_DELIMITER
1453                                + maskIfNull(job.getConsoleUrl()) + VERBOSE_DELIMITER + maskIfNull(job.getUser())
1454                                + VERBOSE_DELIMITER + maskIfNull(job.getGroup()) + VERBOSE_DELIMITER + job.getRun()
1455                                + VERBOSE_DELIMITER + maskDate(job.getCreatedTime(), timeZoneId, verbose)
1456                                + VERBOSE_DELIMITER + maskDate(job.getStartTime(), timeZoneId, verbose) + VERBOSE_DELIMITER
1457                                + job.getStatus() + VERBOSE_DELIMITER
1458                                + maskDate(job.getLastModifiedTime(), timeZoneId, verbose) + VERBOSE_DELIMITER
1459                                + maskDate(job.getEndTime(), timeZoneId, verbose));
1460    
1461                        System.out.println(RULER);
1462                    }
1463                }
1464                else {
1465                    System.out.println(String.format(WORKFLOW_JOBS_FORMATTER, "Job ID", "App Name", "Status", "User",
1466                            "Group", "Started", "Ended"));
1467                    System.out.println(RULER);
1468    
1469                    for (WorkflowJob job : jobs) {
1470                        System.out.println(String.format(WORKFLOW_JOBS_FORMATTER, maskIfNull(job.getId()),
1471                                maskIfNull(job.getAppName()), job.getStatus(), maskIfNull(job.getUser()),
1472                                maskIfNull(job.getGroup()), maskDate(job.getStartTime(), timeZoneId, verbose),
1473                                maskDate(job.getEndTime(), timeZoneId, verbose)));
1474    
1475                        System.out.println(RULER);
1476                    }
1477                }
1478            }
1479            else {
1480                System.out.println("No Jobs match your criteria!");
1481            }
1482        }
1483    
1484        private String maskIfNull(String value) {
1485            if (value != null && value.length() > 0) {
1486                return value;
1487            }
1488            return "-";
1489        }
1490    
1491        private String maskDate(Date date, String timeZoneId, boolean verbose) {
1492            if (date == null) {
1493                return "-";
1494            }
1495    
1496            SimpleDateFormat dateFormater = null;
1497            if (verbose) {
1498                dateFormater = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss zzz", Locale.US);
1499            }
1500            else {
1501                dateFormater = new SimpleDateFormat("yyyy-MM-dd HH:mm zzz", Locale.US);
1502            }
1503    
1504            if (timeZoneId != null) {
1505                dateFormater.setTimeZone(TimeZone.getTimeZone(timeZoneId));
1506            }
1507            String dateString = dateFormater.format(date);
1508            // Most TimeZones are 3 or 4 characters; GMT offsets (e.g. GMT-07:00) are 9, so lets remove the "GMT" part to make it 6
1509            // to fit better
1510            Matcher m = GMT_OFFSET_SHORTEN_PATTERN.matcher(dateString);
1511            if (m.matches() && m.groupCount() == 2) {
1512                dateString = m.group(1) + m.group(2);
1513            }
1514            return dateString;
1515        }
1516    
1517        private void validateCommand(CommandLine commandLine) throws OozieCLIException {
1518            String[] args = commandLine.getArgs();
1519            if (args.length != 1) {
1520                throw new OozieCLIException("One file must be specified");
1521            }
1522            File file = new File(args[0]);
1523            if (file.exists()) {
1524                try {
1525                    List<StreamSource> sources = new ArrayList<StreamSource>();
1526                    sources.add(new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream(
1527                            "oozie-workflow-0.1.xsd")));
1528                    sources.add(new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream(
1529                            "shell-action-0.1.xsd")));
1530                    sources.add(new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream(
1531                            "shell-action-0.2.xsd")));
1532                    sources.add(new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream(
1533                            "shell-action-0.3.xsd")));
1534                    sources.add(new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream(
1535                            "email-action-0.1.xsd")));
1536                    sources.add(new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream(
1537                            "distcp-action-0.1.xsd")));
1538                    sources.add(new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream(
1539                            "distcp-action-0.2.xsd")));
1540                    sources.add(new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream(
1541                            "oozie-workflow-0.2.xsd")));
1542                    sources.add(new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream(
1543                            "oozie-workflow-0.2.5.xsd")));
1544                    sources.add(new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream(
1545                            "oozie-workflow-0.3.xsd")));
1546                    sources.add(new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream(
1547                            "oozie-workflow-0.4.xsd")));
1548                    sources.add(new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream(
1549                            "oozie-workflow-0.5.xsd")));
1550                    sources.add(new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream(
1551                            "oozie-coordinator-0.1.xsd")));
1552                    sources.add(new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream(
1553                            "oozie-coordinator-0.2.xsd")));
1554                    sources.add(new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream(
1555                            "oozie-coordinator-0.3.xsd")));
1556                    sources.add(new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream(
1557                            "oozie-coordinator-0.4.xsd")));
1558                    sources.add(new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream(
1559                            "oozie-bundle-0.1.xsd")));
1560                    sources.add(new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream(
1561                            "oozie-bundle-0.2.xsd")));
1562                    sources.add(new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream(
1563                            "oozie-sla-0.1.xsd")));
1564                    sources.add(new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream(
1565                            "oozie-sla-0.2.xsd")));
1566                    sources.add(new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream(
1567                            "hive-action-0.2.xsd")));
1568                    sources.add(new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream(
1569                            "hive-action-0.3.xsd")));
1570                    sources.add(new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream(
1571                            "hive-action-0.4.xsd")));
1572                    sources.add(new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream(
1573                            "hive-action-0.5.xsd")));
1574                    sources.add(new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream(
1575                            "sqoop-action-0.2.xsd")));
1576                    sources.add(new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream(
1577                            "sqoop-action-0.3.xsd")));
1578                    sources.add(new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream(
1579                            "sqoop-action-0.4.xsd")));
1580                    sources.add(new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream(
1581                            "ssh-action-0.1.xsd")));
1582                    sources.add(new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream(
1583                            "ssh-action-0.2.xsd")));
1584                    SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
1585                    Schema schema = factory.newSchema(sources.toArray(new StreamSource[sources.size()]));
1586                    Validator validator = schema.newValidator();
1587                    validator.validate(new StreamSource(new FileReader(file)));
1588                    System.out.println("Valid workflow-app");
1589                }
1590                catch (Exception ex) {
1591                    throw new OozieCLIException("Invalid app definition, " + ex.toString(), ex);
1592                }
1593            }
1594            else {
1595                throw new OozieCLIException("File does not exists");
1596            }
1597        }
1598    
1599        private void scriptLanguageCommand(CommandLine commandLine, String jobType) throws IOException, OozieCLIException {
1600            List<String> args = commandLine.getArgList();
1601            if (args.size() > 0) {
1602                // checking if args starts with -X (because CLIParser cannot check this)
1603                if (!args.get(0).equals("-X")) {
1604                    throw new OozieCLIException("Unrecognized option: " + args.get(0) + " Expecting -X");
1605                }
1606                args.remove(0);
1607            }
1608    
1609            if (!commandLine.hasOption(SCRIPTFILE_OPTION)) {
1610                throw new OozieCLIException("Need to specify -file <scriptfile>");
1611            }
1612    
1613            if (!commandLine.hasOption(CONFIG_OPTION)) {
1614                throw new OozieCLIException("Need to specify -config <configfile>");
1615            }
1616    
1617            try {
1618                XOozieClient wc = createXOozieClient(commandLine);
1619                Properties conf = getConfiguration(wc, commandLine);
1620                String script = commandLine.getOptionValue(SCRIPTFILE_OPTION);
1621                List<String> paramsList = new ArrayList<String>();
1622                if (commandLine.hasOption("P")) {
1623                    Properties params = commandLine.getOptionProperties("P");
1624                    for (String key : params.stringPropertyNames()) {
1625                        paramsList.add(key + "=" + params.getProperty(key));
1626                    }
1627                }
1628                System.out.println(JOB_ID_PREFIX + wc.submitScriptLanguage(conf, script, args.toArray(new String[args.size()]),
1629                        paramsList.toArray(new String[paramsList.size()]), jobType));
1630            }
1631            catch (OozieClientException ex) {
1632                throw new OozieCLIException(ex.toString(), ex);
1633            }
1634        }
1635    
1636        private void infoCommand(CommandLine commandLine) throws OozieCLIException {
1637            for (Option option : commandLine.getOptions()) {
1638                String opt = option.getOpt();
1639                if (opt.equals(INFO_TIME_ZONES_OPTION)) {
1640                    printAvailableTimeZones();
1641                }
1642            }
1643        }
1644    
1645        private void printAvailableTimeZones() {
1646            System.out.println("The format is \"SHORT_NAME (ID)\"\nGive the ID to the -timezone argument");
1647            System.out.println("GMT offsets can also be used (e.g. GMT-07:00, GMT-0700, GMT+05:30, GMT+0530)");
1648            System.out.println("Available Time Zones:");
1649            for (String tzId : TimeZone.getAvailableIDs()) {
1650                // skip id's that are like "Etc/GMT+01:00" because their display names are like "GMT-01:00", which is confusing
1651                if (!tzId.startsWith("Etc/GMT")) {
1652                    TimeZone tZone = TimeZone.getTimeZone(tzId);
1653                    System.out.println("      " + tZone.getDisplayName(false, TimeZone.SHORT) + " (" + tzId + ")");
1654                }
1655            }
1656        }
1657    
1658    
1659        private void mrCommand(CommandLine commandLine) throws IOException, OozieCLIException {
1660            try {
1661                XOozieClient wc = createXOozieClient(commandLine);
1662                Properties conf = getConfiguration(wc, commandLine);
1663    
1664                String mapper = conf.getProperty(MAPRED_MAPPER, conf.getProperty(MAPRED_MAPPER_2));
1665                if (mapper == null) {
1666                    throw new OozieCLIException("mapper (" + MAPRED_MAPPER + " or " + MAPRED_MAPPER_2 + ") must be specified in conf");
1667                }
1668    
1669                String reducer = conf.getProperty(MAPRED_REDUCER, conf.getProperty(MAPRED_REDUCER_2));
1670                if (reducer == null) {
1671                    throw new OozieCLIException("reducer (" + MAPRED_REDUCER + " or " + MAPRED_REDUCER_2
1672                            + ") must be specified in conf");
1673                }
1674    
1675                String inputDir = conf.getProperty(MAPRED_INPUT);
1676                if (inputDir == null) {
1677                    throw new OozieCLIException("input dir (" + MAPRED_INPUT +") must be specified in conf");
1678                }
1679    
1680                String outputDir = conf.getProperty(MAPRED_OUTPUT);
1681                if (outputDir == null) {
1682                    throw new OozieCLIException("output dir (" + MAPRED_OUTPUT +") must be specified in conf");
1683                }
1684    
1685                System.out.println(JOB_ID_PREFIX + wc.submitMapReduce(conf));
1686            }
1687            catch (OozieClientException ex) {
1688                throw new OozieCLIException(ex.toString(), ex);
1689            }
1690        }
1691    
1692        private String getFirstMissingDependencies(CoordinatorAction action) {
1693            StringBuilder allDeps = new StringBuilder();
1694            String missingDep = action.getMissingDependencies();
1695            boolean depExists = false;
1696            if (missingDep != null && !missingDep.isEmpty()) {
1697                allDeps.append(missingDep.split(INSTANCE_SEPARATOR)[0]);
1698                depExists = true;
1699            }
1700            String pushDeps = action.getPushMissingDependencies();
1701            if (pushDeps != null && !pushDeps.isEmpty()) {
1702                if(depExists) {
1703                    allDeps.append(INSTANCE_SEPARATOR);
1704                }
1705                allDeps.append(pushDeps.split(INSTANCE_SEPARATOR)[0]);
1706            }
1707            return allDeps.toString();
1708        }
1709    
1710    }