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 */
018package org.apache.oozie.cli;
019
020import java.io.File;
021import java.io.FileInputStream;
022import java.io.FileReader;
023import java.io.IOException;
024import java.io.InputStream;
025import java.io.PrintStream;
026import java.text.SimpleDateFormat;
027import java.util.ArrayList;
028import java.util.Date;
029import java.util.List;
030import java.util.Locale;
031import java.util.Map;
032import java.util.Properties;
033import java.util.TimeZone;
034import java.util.concurrent.Callable;
035import java.util.regex.Matcher;
036import java.util.regex.Pattern;
037
038import javax.xml.XMLConstants;
039import javax.xml.parsers.DocumentBuilder;
040import javax.xml.parsers.DocumentBuilderFactory;
041import javax.xml.parsers.ParserConfigurationException;
042import javax.xml.transform.stream.StreamSource;
043import javax.xml.validation.Schema;
044import javax.xml.validation.SchemaFactory;
045import javax.xml.validation.Validator;
046
047import org.apache.commons.cli.CommandLine;
048import org.apache.commons.cli.Option;
049import org.apache.commons.cli.OptionBuilder;
050import org.apache.commons.cli.OptionGroup;
051import org.apache.commons.cli.Options;
052import org.apache.commons.cli.ParseException;
053import org.apache.oozie.BuildInfo;
054import org.apache.oozie.client.AuthOozieClient;
055import org.apache.oozie.client.BulkResponse;
056import org.apache.oozie.client.BundleJob;
057import org.apache.oozie.client.CoordinatorAction;
058import org.apache.oozie.client.CoordinatorJob;
059import org.apache.oozie.client.OozieClient;
060import org.apache.oozie.client.OozieClient.SYSTEM_MODE;
061import org.apache.oozie.client.OozieClientException;
062import org.apache.oozie.client.WorkflowAction;
063import org.apache.oozie.client.WorkflowJob;
064import org.apache.oozie.client.XOozieClient;
065import org.apache.oozie.client.rest.RestConstants;
066import org.w3c.dom.DOMException;
067import org.w3c.dom.Document;
068import org.w3c.dom.Element;
069import org.w3c.dom.Node;
070import org.w3c.dom.NodeList;
071import org.w3c.dom.Text;
072import org.xml.sax.SAXException;
073
074import com.google.common.annotations.VisibleForTesting;
075
076/**
077 * Oozie command line utility.
078 */
079public 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}