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
019package org.apache.oozie.cli;
020
021import com.google.common.annotations.VisibleForTesting;
022import org.apache.commons.cli.CommandLine;
023import org.apache.commons.cli.Option;
024import org.apache.commons.cli.OptionBuilder;
025import org.apache.commons.cli.OptionGroup;
026import org.apache.commons.cli.Options;
027import org.apache.commons.cli.ParseException;
028import org.apache.oozie.BuildInfo;
029import org.apache.oozie.client.AuthOozieClient;
030import org.apache.oozie.client.BulkResponse;
031import org.apache.oozie.client.BundleJob;
032import org.apache.oozie.client.CoordinatorAction;
033import org.apache.oozie.client.CoordinatorJob;
034import org.apache.oozie.client.OozieClient;
035import org.apache.oozie.client.OozieClient.SYSTEM_MODE;
036import org.apache.oozie.client.OozieClientException;
037import org.apache.oozie.client.WorkflowAction;
038import org.apache.oozie.client.WorkflowJob;
039import org.apache.oozie.client.XOozieClient;
040import org.apache.oozie.client.rest.JsonTags;
041import org.apache.oozie.client.rest.JsonToBean;
042import org.apache.oozie.client.rest.RestConstants;
043import org.json.simple.JSONArray;
044import org.json.simple.JSONObject;
045import org.w3c.dom.DOMException;
046import org.w3c.dom.Document;
047import org.w3c.dom.Element;
048import org.w3c.dom.Node;
049import org.w3c.dom.NodeList;
050import org.w3c.dom.Text;
051import org.xml.sax.SAXException;
052
053import javax.xml.XMLConstants;
054import javax.xml.parsers.DocumentBuilder;
055import javax.xml.parsers.DocumentBuilderFactory;
056import javax.xml.parsers.ParserConfigurationException;
057import javax.xml.transform.stream.StreamSource;
058import javax.xml.validation.Schema;
059import javax.xml.validation.SchemaFactory;
060import javax.xml.validation.Validator;
061import java.io.File;
062import java.io.FileInputStream;
063import java.io.FileReader;
064import java.io.IOException;
065import java.io.InputStream;
066import java.io.PrintStream;
067import java.text.SimpleDateFormat;
068import java.util.ArrayList;
069import java.util.Date;
070import java.util.List;
071import java.util.Locale;
072import java.util.Map;
073import java.util.Properties;
074import java.util.TimeZone;
075import java.util.TreeMap;
076import java.util.concurrent.Callable;
077import java.util.regex.Matcher;
078import java.util.regex.Pattern;
079
080/**
081 * Oozie command line utility.
082 */
083public class OozieCLI {
084    public static final String ENV_OOZIE_URL = "OOZIE_URL";
085    public static final String ENV_OOZIE_DEBUG = "OOZIE_DEBUG";
086    public static final String ENV_OOZIE_TIME_ZONE = "OOZIE_TIMEZONE";
087    public static final String ENV_OOZIE_AUTH = "OOZIE_AUTH";
088    public static final String OOZIE_RETRY_COUNT = "oozie.connection.retry.count";
089    public static final String WS_HEADER_PREFIX = "header:";
090
091    public static final String HELP_CMD = "help";
092    public static final String VERSION_CMD = "version";
093    public static final String JOB_CMD = "job";
094    public static final String JOBS_CMD = "jobs";
095    public static final String ADMIN_CMD = "admin";
096    public static final String VALIDATE_CMD = "validate";
097    public static final String SLA_CMD = "sla";
098    public static final String PIG_CMD = "pig";
099    public static final String HIVE_CMD = "hive";
100    public static final String SQOOP_CMD = "sqoop";
101    public static final String MR_CMD = "mapreduce";
102    public static final String INFO_CMD = "info";
103
104    public static final String OOZIE_OPTION = "oozie";
105    public static final String CONFIG_OPTION = "config";
106    public static final String SUBMIT_OPTION = "submit";
107    public static final String OFFSET_OPTION = "offset";
108    public static final String START_OPTION = "start";
109    public static final String RUN_OPTION = "run";
110    public static final String DRYRUN_OPTION = "dryrun";
111    public static final String SUSPEND_OPTION = "suspend";
112    public static final String RESUME_OPTION = "resume";
113    public static final String KILL_OPTION = "kill";
114    public static final String CHANGE_OPTION = "change";
115    public static final String CHANGE_VALUE_OPTION = "value";
116    public static final String RERUN_OPTION = "rerun";
117    public static final String INFO_OPTION = "info";
118    public static final String LOG_OPTION = "log";
119    public static final String ACTION_OPTION = "action";
120    public static final String DEFINITION_OPTION = "definition";
121    public static final String CONFIG_CONTENT_OPTION = "configcontent";
122    public static final String SQOOP_COMMAND_OPTION = "command";
123    public static final String SHOWDIFF_OPTION = "diff";
124    public static final String UPDATE_OPTION = "update";
125    public static final String IGNORE_OPTION = "ignore";
126    public static final String POLL_OPTION = "poll";
127    public static final String TIMEOUT_OPTION = "timeout";
128    public static final String INTERVAL_OPTION = "interval";
129
130    public static final String DO_AS_OPTION = "doas";
131
132    public static final String LEN_OPTION = "len";
133    public static final String FILTER_OPTION = "filter";
134    public static final String JOBTYPE_OPTION = "jobtype";
135    public static final String SYSTEM_MODE_OPTION = "systemmode";
136    public static final String VERSION_OPTION = "version";
137    public static final String STATUS_OPTION = "status";
138    public static final String LOCAL_TIME_OPTION = "localtime";
139    public static final String TIME_ZONE_OPTION = "timezone";
140    public static final String QUEUE_DUMP_OPTION = "queuedump";
141    public static final String RERUN_COORD_OPTION = "coordinator";
142    public static final String DATE_OPTION = "date";
143    public static final String RERUN_REFRESH_OPTION = "refresh";
144    public static final String RERUN_NOCLEANUP_OPTION = "nocleanup";
145    public static final String RERUN_FAILED_OPTION = "failed";
146    public static final String ORDER_OPTION = "order";
147
148    public static final String UPDATE_SHARELIB_OPTION = "sharelibupdate";
149
150    public static final String LIST_SHARELIB_LIB_OPTION = "shareliblist";
151
152    public static final String SERVER_CONFIGURATION_OPTION = "configuration";
153    public static final String SERVER_OS_ENV_OPTION = "osenv";
154    public static final String SERVER_JAVA_SYSTEM_PROPERTIES_OPTION = "javasysprops";
155
156    public static final String METRICS_OPTION = "metrics";
157    public static final String INSTRUMENTATION_OPTION = "instrumentation";
158
159    public static final String AUTH_OPTION = "auth";
160
161    public static final String VERBOSE_OPTION = "verbose";
162    public static final String VERBOSE_DELIMITER = "\t";
163    public static final String DEBUG_OPTION = "debug";
164
165    public static final String SCRIPTFILE_OPTION = "file";
166
167    public static final String INFO_TIME_ZONES_OPTION = "timezones";
168
169    public static final String BULK_OPTION = "bulk";
170
171    public static final String AVAILABLE_SERVERS_OPTION = "servers";
172
173    public static final String ALL_WORKFLOWS_FOR_COORD_ACTION = "allruns";
174
175    private static final String[] OOZIE_HELP = {
176            "the env variable '" + ENV_OOZIE_URL + "' is used as default value for the '-" + OOZIE_OPTION + "' option",
177            "the env variable '" + ENV_OOZIE_TIME_ZONE + "' is used as default value for the '-" + TIME_ZONE_OPTION + "' option",
178            "the env variable '" + ENV_OOZIE_AUTH + "' is used as default value for the '-" + AUTH_OPTION + "' option",
179            "custom headers for Oozie web services can be specified using '-D" + WS_HEADER_PREFIX + "NAME=VALUE'" };
180
181    private static final String RULER;
182    private static final int LINE_WIDTH = 132;
183
184    private static final int RETRY_COUNT = 4;
185
186    private boolean used;
187
188    private static final String INSTANCE_SEPARATOR = "#";
189
190    private static final String MAPRED_MAPPER = "mapred.mapper.class";
191    private static final String MAPRED_MAPPER_2 = "mapreduce.map.class";
192    private static final String MAPRED_REDUCER = "mapred.reducer.class";
193    private static final String MAPRED_REDUCER_2 = "mapreduce.reduce.class";
194    private static final String MAPRED_INPUT = "mapred.input.dir";
195    private static final String MAPRED_OUTPUT = "mapred.output.dir";
196
197    private static final Pattern GMT_OFFSET_SHORTEN_PATTERN = Pattern.compile("(.* )GMT((?:-|\\+)\\d{2}:\\d{2})");
198
199    static {
200        StringBuilder sb = new StringBuilder();
201        for (int i = 0; i < LINE_WIDTH; i++) {
202            sb.append("-");
203        }
204        RULER = sb.toString();
205    }
206
207    /**
208     * Entry point for the Oozie CLI when invoked from the command line.
209     * <p/>
210     * Upon completion this method exits the JVM with '0' (success) or '-1' (failure).
211     *
212     * @param args options and arguments for the Oozie CLI.
213     */
214    public static void main(String[] args) {
215        if (!System.getProperties().containsKey(AuthOozieClient.USE_AUTH_TOKEN_CACHE_SYS_PROP)) {
216            System.setProperty(AuthOozieClient.USE_AUTH_TOKEN_CACHE_SYS_PROP, "true");
217        }
218        System.exit(new OozieCLI().run(args));
219    }
220
221    /**
222     * Create an Oozie CLI instance.
223     */
224    public OozieCLI() {
225        used = false;
226    }
227
228    /**
229     * Return Oozie CLI top help lines.
230     *
231     * @return help lines.
232     */
233    protected String[] getCLIHelp() {
234        return OOZIE_HELP;
235    }
236
237    /**
238     * Add authentication specific options to oozie cli
239     *
240     * @param options the collection of options to add auth options
241     */
242    protected void addAuthOptions(Options options) {
243        Option auth = new Option(AUTH_OPTION, true, "select authentication type [SIMPLE|KERBEROS]");
244        options.addOption(auth);
245    }
246
247    /**
248     * Create option for command line option 'admin'
249     * @return admin options
250     */
251    protected Options createAdminOptions() {
252        Option oozie = new Option(OOZIE_OPTION, true, "Oozie URL");
253        Option system_mode = new Option(SYSTEM_MODE_OPTION, true,
254                "Supported in Oozie-2.0 or later versions ONLY. Change oozie system mode [NORMAL|NOWEBSERVICE|SAFEMODE]");
255        Option status = new Option(STATUS_OPTION, false, "show the current system status");
256        Option version = new Option(VERSION_OPTION, false, "show Oozie server build version");
257        Option queuedump = new Option(QUEUE_DUMP_OPTION, false, "show Oozie server queue elements");
258        Option doAs = new Option(DO_AS_OPTION, true, "doAs user, impersonates as the specified user");
259        Option availServers = new Option(AVAILABLE_SERVERS_OPTION, false, "list available Oozie servers"
260                + " (more than one only if HA is enabled)");
261        Option sharelibUpdate = new Option(UPDATE_SHARELIB_OPTION, false, "Update server to use a newer version of sharelib");
262        Option serverConfiguration = new Option(SERVER_CONFIGURATION_OPTION, false, "show Oozie system configuration");
263        Option osEnv = new Option(SERVER_OS_ENV_OPTION, false, "show Oozie system OS environment");
264        Option javaSysProps = new Option(SERVER_JAVA_SYSTEM_PROPERTIES_OPTION, false, "show Oozie Java system properties");
265        Option metrics = new Option(METRICS_OPTION, false, "show Oozie system metrics");
266        Option instrumentation = new Option(INSTRUMENTATION_OPTION, false, "show Oozie system instrumentation");
267
268        Option sharelib = new Option(LIST_SHARELIB_LIB_OPTION, false,
269                "List available sharelib that can be specified in a workflow action");
270        sharelib.setOptionalArg(true);
271
272        Options adminOptions = new Options();
273        adminOptions.addOption(oozie);
274        adminOptions.addOption(doAs);
275        OptionGroup group = new OptionGroup();
276        group.addOption(system_mode);
277        group.addOption(status);
278        group.addOption(version);
279        group.addOption(queuedump);
280        group.addOption(availServers);
281        group.addOption(sharelibUpdate);
282        group.addOption(sharelib);
283        group.addOption(serverConfiguration);
284        group.addOption(osEnv);
285        group.addOption(javaSysProps);
286        group.addOption(metrics);
287        group.addOption(instrumentation);
288        adminOptions.addOptionGroup(group);
289        addAuthOptions(adminOptions);
290        return adminOptions;
291    }
292
293    /**
294     * Create option for command line option 'job'
295     * @return job options
296     */
297    protected Options createJobOptions() {
298        Option oozie = new Option(OOZIE_OPTION, true, "Oozie URL");
299        Option config = new Option(CONFIG_OPTION, true, "job configuration file '.xml' or '.properties'");
300        Option submit = new Option(SUBMIT_OPTION, false, "submit a job");
301        Option run = new Option(RUN_OPTION, false, "run a job");
302        Option debug = new Option(DEBUG_OPTION, false, "Use debug mode to see debugging statements on stdout");
303        Option rerun = new Option(RERUN_OPTION, true,
304                "rerun a job  (coordinator requires -action or -date, bundle requires -coordinator or -date)");
305        Option dryrun = new Option(DRYRUN_OPTION, false, "Dryrun a workflow (since 3.3.2) or coordinator (since 2.0) job without"
306                + " actually executing it");
307        Option update = new Option(UPDATE_OPTION, true, "Update coord definition and properties");
308        Option showdiff = new Option(SHOWDIFF_OPTION, true,
309                "Show diff of the new coord definition and properties with the existing one (default true)");
310        Option start = new Option(START_OPTION, true, "start a job");
311        Option suspend = new Option(SUSPEND_OPTION, true, "suspend a job");
312        Option resume = new Option(RESUME_OPTION, true, "resume a job");
313        Option kill = new Option(KILL_OPTION, true, "kill a job (coordinator can mention -action or -date)");
314        Option change = new Option(CHANGE_OPTION, true, "change a coordinator or bundle job");
315        Option changeValue = new Option(CHANGE_VALUE_OPTION, true,
316                "new endtime/concurrency/pausetime value for changing a coordinator job");
317        Option info = new Option(INFO_OPTION, true, "info of a job");
318        Option poll = new Option(POLL_OPTION, true, "poll Oozie until a job reaches a terminal state or a timeout occurs");
319        Option offset = new Option(OFFSET_OPTION, true, "job info offset of actions (default '1', requires -info)");
320        Option len = new Option(LEN_OPTION, true, "number of actions (default TOTAL ACTIONS, requires -info)");
321        Option filter = new Option(FILTER_OPTION, true,
322                "<key><comparator><value>[;<key><comparator><value>]*\n"
323                    + "(All Coordinator actions satisfying the filters will be retreived).\n"
324                    + "key: status or nominaltime\n"
325                    + "comparator: =, !=, <, <=, >, >=. = is used as OR and others as AND\n"
326                    + "status: values are valid status like SUCCEEDED, KILLED etc. Only = and != apply for status\n"
327                    + "nominaltime: time of format yyyy-MM-dd'T'HH:mm'Z'");
328        Option order = new Option(ORDER_OPTION, true,
329                "order to show coord actions (default ascending order, 'desc' for descending order, requires -info)");
330        Option localtime = new Option(LOCAL_TIME_OPTION, false, "use local time (same as passing your time zone to -" +
331                TIME_ZONE_OPTION + "). Overrides -" + TIME_ZONE_OPTION + " option");
332        Option timezone = new Option(TIME_ZONE_OPTION, true,
333                "use time zone with the specified ID (default GMT).\nSee 'oozie info -timezones' for a list");
334        Option log = new Option(LOG_OPTION, true, "job log");
335        Option logFilter = new Option(
336                RestConstants.LOG_FILTER_OPTION, true,
337                "job log search parameter. Can be specified as -logfilter opt1=val1;opt2=val1;opt3=val1. "
338                + "Supported options are recent, start, end, loglevel, text, limit and debug");
339        Option definition = new Option(DEFINITION_OPTION, true, "job definition");
340        Option config_content = new Option(CONFIG_CONTENT_OPTION, true, "job configuration");
341        Option verbose = new Option(VERBOSE_OPTION, false, "verbose mode");
342        Option action = new Option(ACTION_OPTION, true,
343                "coordinator rerun/kill on action ids (requires -rerun/-kill); coordinator log retrieval on action ids"
344                        + "(requires -log)");
345        Option date = new Option(DATE_OPTION, true,
346                "coordinator/bundle rerun on action dates (requires -rerun); coordinator log retrieval on action dates (requires -log)");
347        Option rerun_coord = new Option(RERUN_COORD_OPTION, true, "bundle rerun on coordinator names (requires -rerun)");
348        Option rerun_refresh = new Option(RERUN_REFRESH_OPTION, false,
349                "re-materialize the coordinator rerun actions (requires -rerun)");
350        Option rerun_nocleanup = new Option(RERUN_NOCLEANUP_OPTION, false,
351                "do not clean up output-events of the coordiantor rerun actions (requires -rerun)");
352        Option rerun_failed = new Option(RERUN_FAILED_OPTION, false,
353                "runs the failed workflow actions of the coordinator actions (requires -rerun)");
354        Option property = OptionBuilder.withArgName("property=value").hasArgs(2).withValueSeparator().withDescription(
355                "set/override value for given property").create("D");
356        Option getAllWorkflows = new Option(ALL_WORKFLOWS_FOR_COORD_ACTION, false,
357                "Get workflow jobs corresponding to a coordinator action including all the reruns");
358        Option ignore = new Option(IGNORE_OPTION, true,
359                "change status of a coordinator job or action to IGNORED"
360                + " (-action required to ignore coord actions)");
361        Option timeout = new Option(TIMEOUT_OPTION, true, "timeout in minutes (default is 30, negative values indicate no "
362                + "timeout, requires -poll)");
363        timeout.setType(Integer.class);
364        Option interval = new Option(INTERVAL_OPTION, true, "polling interval in minutes (default is 5, requires -poll)");
365        interval.setType(Integer.class);
366
367        Option doAs = new Option(DO_AS_OPTION, true, "doAs user, impersonates as the specified user");
368
369        OptionGroup actions = new OptionGroup();
370        actions.addOption(submit);
371        actions.addOption(start);
372        actions.addOption(run);
373        actions.addOption(dryrun);
374        actions.addOption(suspend);
375        actions.addOption(resume);
376        actions.addOption(kill);
377        actions.addOption(change);
378        actions.addOption(update);
379        actions.addOption(info);
380        actions.addOption(rerun);
381        actions.addOption(log);
382        actions.addOption(definition);
383        actions.addOption(config_content);
384        actions.addOption(ignore);
385        actions.addOption(poll);
386        actions.setRequired(true);
387        Options jobOptions = new Options();
388        jobOptions.addOption(oozie);
389        jobOptions.addOption(doAs);
390        jobOptions.addOption(config);
391        jobOptions.addOption(property);
392        jobOptions.addOption(changeValue);
393        jobOptions.addOption(localtime);
394        jobOptions.addOption(timezone);
395        jobOptions.addOption(verbose);
396        jobOptions.addOption(debug);
397        jobOptions.addOption(offset);
398        jobOptions.addOption(len);
399        jobOptions.addOption(filter);
400        jobOptions.addOption(order);
401        jobOptions.addOption(action);
402        jobOptions.addOption(date);
403        jobOptions.addOption(rerun_coord);
404        jobOptions.addOption(rerun_refresh);
405        jobOptions.addOption(rerun_nocleanup);
406        jobOptions.addOption(rerun_failed);
407        jobOptions.addOption(getAllWorkflows);
408        jobOptions.addOptionGroup(actions);
409        jobOptions.addOption(logFilter);
410        jobOptions.addOption(timeout);
411        jobOptions.addOption(interval);
412        addAuthOptions(jobOptions);
413        jobOptions.addOption(showdiff);
414
415        //Needed to make dryrun and update mutually exclusive options
416        OptionGroup updateOption = new OptionGroup();
417        updateOption.addOption(dryrun);
418        jobOptions.addOptionGroup(updateOption);
419        return jobOptions;
420    }
421
422    /**
423     * Create option for command line option 'jobs'
424     * @return jobs options
425     */
426    protected Options createJobsOptions() {
427        Option oozie = new Option(OOZIE_OPTION, true, "Oozie URL");
428        Option start = new Option(OFFSET_OPTION, true, "jobs offset (default '1')");
429        Option jobtype = new Option(JOBTYPE_OPTION, true,
430                "job type ('Supported in Oozie-2.0 or later versions ONLY - 'coordinator' or 'bundle' or 'wf'(default))");
431        Option len = new Option(LEN_OPTION, true, "number of jobs (default '100')");
432        Option filter = new Option(FILTER_OPTION, true,
433                "user=<U>\\;name=<N>\\;group=<G>\\;status=<S>\\;frequency=<F>\\;unit=<M>" +
434                        "\\;startcreatedtime=<SC>\\;endcreatedtime=<EC> " +
435                        "(valid unit values are 'months', 'days', 'hours' or 'minutes'. " +
436                        "startcreatedtime, endcreatedtime: time of format yyyy-MM-dd'T'HH:mm'Z')");
437        Option localtime = new Option(LOCAL_TIME_OPTION, false, "use local time (same as passing your time zone to -" +
438                TIME_ZONE_OPTION + "). Overrides -" + TIME_ZONE_OPTION + " option");
439        Option kill = new Option(KILL_OPTION, false, "bulk kill operation");
440        Option suspend = new Option(SUSPEND_OPTION, false, "bulk suspend operation");
441        Option resume = new Option(RESUME_OPTION, false, "bulk resume operation");
442        Option timezone = new Option(TIME_ZONE_OPTION, true,
443                "use time zone with the specified ID (default GMT).\nSee 'oozie info -timezones' for a list");
444        Option verbose = new Option(VERBOSE_OPTION, false, "verbose mode");
445        Option doAs = new Option(DO_AS_OPTION, true, "doAs user, impersonates as the specified user");
446        Option bulkMonitor = new Option(BULK_OPTION, true, "key-value pairs to filter bulk jobs response. e.g. bundle=<B>\\;" +
447                "coordinators=<C>\\;actionstatus=<S>\\;startcreatedtime=<SC>\\;endcreatedtime=<EC>\\;" +
448                "startscheduledtime=<SS>\\;endscheduledtime=<ES>\\; bundle, coordinators and actionstatus can be multiple comma separated values" +
449                "bundle and coordinators can be id(s) or appName(s) of those jobs. Specifying bundle is mandatory, other params are optional");
450        start.setType(Integer.class);
451        len.setType(Integer.class);
452        Options jobsOptions = new Options();
453        jobsOptions.addOption(oozie);
454        jobsOptions.addOption(doAs);
455        jobsOptions.addOption(localtime);
456        jobsOptions.addOption(kill);
457        jobsOptions.addOption(suspend);
458        jobsOptions.addOption(resume);
459        jobsOptions.addOption(timezone);
460        jobsOptions.addOption(start);
461        jobsOptions.addOption(len);
462        jobsOptions.addOption(oozie);
463        jobsOptions.addOption(filter);
464        jobsOptions.addOption(jobtype);
465        jobsOptions.addOption(verbose);
466        jobsOptions.addOption(bulkMonitor);
467        addAuthOptions(jobsOptions);
468        return jobsOptions;
469    }
470
471    /**
472     * Create option for command line option 'sla'
473     *
474     * @return sla options
475     */
476    protected Options createSlaOptions() {
477        Option oozie = new Option(OOZIE_OPTION, true, "Oozie URL");
478        Option start = new Option(OFFSET_OPTION, true, "start offset (default '0')");
479        Option len = new Option(LEN_OPTION, true, "number of results (default '100', max '1000')");
480        Option filter = new Option(FILTER_OPTION, true, "filter of SLA events. e.g., jobid=<J>\\;appname=<A>");
481        start.setType(Integer.class);
482        len.setType(Integer.class);
483        Options slaOptions = new Options();
484        slaOptions.addOption(start);
485        slaOptions.addOption(len);
486        slaOptions.addOption(filter);
487        slaOptions.addOption(oozie);
488        addAuthOptions(slaOptions);
489        return slaOptions;
490    }
491
492    /**
493     * Create option for command line option 'validate'
494     *
495     * @return validate options
496     */
497    protected Options createValidateOptions() {
498        Option oozie = new Option(OOZIE_OPTION, true, "Oozie URL");
499        Options validateOption = new Options();
500        validateOption.addOption(oozie);
501        addAuthOptions(validateOption);
502        return validateOption;
503    }
504
505    /**
506     * Create option for command line option 'pig' or 'hive'
507     * @return pig or hive options
508     */
509    @SuppressWarnings("static-access")
510    protected Options createScriptLanguageOptions(String jobType) {
511        Option oozie = new Option(OOZIE_OPTION, true, "Oozie URL");
512        Option config = new Option(CONFIG_OPTION, true, "job configuration file '.properties'");
513        Option file = new Option(SCRIPTFILE_OPTION, true, jobType + " script");
514        Option property = OptionBuilder.withArgName("property=value").hasArgs(2).withValueSeparator().withDescription(
515                "set/override value for given property").create("D");
516        Option params = OptionBuilder.withArgName("property=value").hasArgs(2).withValueSeparator().withDescription(
517                "set parameters for script").create("P");
518        Option doAs = new Option(DO_AS_OPTION, true, "doAs user, impersonates as the specified user");
519        Options Options = new Options();
520        Options.addOption(oozie);
521        Options.addOption(doAs);
522        Options.addOption(config);
523        Options.addOption(property);
524        Options.addOption(params);
525        Options.addOption(file);
526        addAuthOptions(Options);
527        return Options;
528    }
529
530    /**
531     * Create option for command line option 'sqoop'
532     * @return sqoop options
533     */
534    @SuppressWarnings("static-access")
535    protected Options createSqoopCLIOptions() {
536        Option oozie = new Option(OOZIE_OPTION, true, "Oozie URL");
537        Option config = new Option(CONFIG_OPTION, true, "job configuration file '.properties'");
538        Option command = OptionBuilder.withArgName(SQOOP_COMMAND_OPTION).hasArgs().withValueSeparator().withDescription(
539                "sqoop command").create(SQOOP_COMMAND_OPTION);
540        Option property = OptionBuilder.withArgName("property=value").hasArgs(2).withValueSeparator().withDescription(
541                "set/override value for given property").create("D");
542        Option doAs = new Option(DO_AS_OPTION, true, "doAs user, impersonates as the specified user");
543        Options Options = new Options();
544        Options.addOption(oozie);
545        Options.addOption(doAs);
546        Options.addOption(config);
547        Options.addOption(property);
548        Options.addOption(command);
549        addAuthOptions(Options);
550        return Options;
551    }
552
553    /**
554     * Create option for command line option 'info'
555     * @return info options
556     */
557    protected Options createInfoOptions() {
558        Option timezones = new Option(INFO_TIME_ZONES_OPTION, false, "display a list of available time zones");
559        Options infoOptions = new Options();
560        infoOptions.addOption(timezones);
561        return infoOptions;
562    }
563
564    /**
565     * Create option for command line option 'mapreduce'
566     * @return mapreduce options
567     */
568    @SuppressWarnings("static-access")
569    protected Options createMROptions() {
570        Option oozie = new Option(OOZIE_OPTION, true, "Oozie URL");
571        Option config = new Option(CONFIG_OPTION, true, "job configuration file '.properties'");
572        Option property = OptionBuilder.withArgName("property=value").hasArgs(2).withValueSeparator().withDescription(
573                "set/override value for given property").create("D");
574        Option doAs = new Option(DO_AS_OPTION, true, "doAs user, impersonates as the specified user");
575        Options mrOptions = new Options();
576        mrOptions.addOption(oozie);
577        mrOptions.addOption(doAs);
578        mrOptions.addOption(config);
579        mrOptions.addOption(property);
580        addAuthOptions(mrOptions);
581        return mrOptions;
582    }
583
584    /**
585     * Run a CLI programmatically.
586     * <p/>
587     * It does not exit the JVM.
588     * <p/>
589     * A CLI instance can be used only once.
590     *
591     * @param args options and arguments for the Oozie CLI.
592     * @return '0' (success), '-1' (failure).
593     */
594    public synchronized int run(String[] args) {
595        if (used) {
596            throw new IllegalStateException("CLI instance already used");
597        }
598        used = true;
599        final CLIParser parser = getCLIParser();
600        try {
601            final CLIParser.Command command = parser.parse(args);
602
603            String doAsUser = command.getCommandLine().getOptionValue(DO_AS_OPTION);
604
605            if (doAsUser != null) {
606                OozieClient.doAs(doAsUser, new Callable<Void>() {
607                    @Override
608                    public Void call() throws Exception {
609                        processCommand(parser, command);
610                        return null;
611                    }
612                });
613            }
614            else {
615                processCommand(parser, command);
616            }
617            return 0;
618        }
619        catch (OozieCLIException ex) {
620            System.err.println("Error: " + ex.getMessage());
621            return -1;
622        }
623        catch (ParseException ex) {
624            System.err.println("Invalid sub-command: " + ex.getMessage());
625            System.err.println();
626            System.err.println(parser.shortHelp());
627            return -1;
628        }
629        catch (Exception ex) {
630            ex.printStackTrace();
631            System.err.println(ex.getMessage());
632            return -1;
633        }
634    }
635
636    @VisibleForTesting
637    public CLIParser getCLIParser(){
638        CLIParser parser = new CLIParser(OOZIE_OPTION, getCLIHelp());
639        parser.addCommand(HELP_CMD, "", "display usage for all commands or specified command", new Options(), false);
640        parser.addCommand(VERSION_CMD, "", "show client version", new Options(), false);
641        parser.addCommand(JOB_CMD, "", "job operations", createJobOptions(), false);
642        parser.addCommand(JOBS_CMD, "", "jobs status", createJobsOptions(), false);
643        parser.addCommand(ADMIN_CMD, "", "admin operations", createAdminOptions(), false);
644        parser.addCommand(VALIDATE_CMD, "", "validate a workflow, coordinator, bundle XML file", createValidateOptions(), true);
645        parser.addCommand(SLA_CMD, "", "sla operations (Deprecated with Oozie 4.0)", createSlaOptions(), false);
646        parser.addCommand(PIG_CMD, "-X ", "submit a pig job, everything after '-X' are pass-through parameters to pig, any '-D' "
647                + "arguments after '-X' are put in <configuration>", createScriptLanguageOptions(PIG_CMD), true);
648        parser.addCommand(HIVE_CMD, "-X ", "submit a hive job, everything after '-X' are pass-through parameters to hive, any '-D' "
649                + "arguments after '-X' are put in <configuration>", createScriptLanguageOptions(HIVE_CMD), true);
650        parser.addCommand(SQOOP_CMD, "-X ", "submit a sqoop job, everything after '-X' are pass-through parameters " +
651                "to sqoop, any '-D' arguments after '-X' are put in <configuration>", createSqoopCLIOptions(), true);
652        parser.addCommand(INFO_CMD, "", "get more detailed info about specific topics", createInfoOptions(), false);
653        parser.addCommand(MR_CMD, "", "submit a mapreduce job", createMROptions(), false);
654        return parser;
655    }
656
657    public void processCommand(CLIParser parser, CLIParser.Command command) throws Exception {
658        if (command.getName().equals(HELP_CMD)) {
659            parser.showHelp(command.getCommandLine());
660        }
661        else if (command.getName().equals(JOB_CMD)) {
662            jobCommand(command.getCommandLine());
663        }
664        else if (command.getName().equals(JOBS_CMD)) {
665            jobsCommand(command.getCommandLine());
666        }
667        else if (command.getName().equals(ADMIN_CMD)) {
668            adminCommand(command.getCommandLine());
669        }
670        else if (command.getName().equals(VERSION_CMD)) {
671            versionCommand();
672        }
673        else if (command.getName().equals(VALIDATE_CMD)) {
674            validateCommand(command.getCommandLine());
675        }
676        else if (command.getName().equals(SLA_CMD)) {
677            slaCommand(command.getCommandLine());
678        }
679        else if (command.getName().equals(PIG_CMD)) {
680            scriptLanguageCommand(command.getCommandLine(), PIG_CMD);
681        }
682        else if (command.getName().equals(HIVE_CMD)) {
683            scriptLanguageCommand(command.getCommandLine(), HIVE_CMD);
684        }
685        else if (command.getName().equals(SQOOP_CMD)) {
686            sqoopCommand(command.getCommandLine());
687        }
688        else if (command.getName().equals(INFO_CMD)) {
689            infoCommand(command.getCommandLine());
690        }
691        else if (command.getName().equals(MR_CMD)){
692            mrCommand(command.getCommandLine());
693        }
694    }
695    protected String getOozieUrl(CommandLine commandLine) {
696        String url = commandLine.getOptionValue(OOZIE_OPTION);
697        if (url == null) {
698            url = System.getenv(ENV_OOZIE_URL);
699            if (url == null) {
700                throw new IllegalArgumentException(
701                        "Oozie URL is not available neither in command option or in the environment");
702            }
703        }
704        return url;
705    }
706
707    private String getTimeZoneId(CommandLine commandLine)
708    {
709        if (commandLine.hasOption(LOCAL_TIME_OPTION)) {
710            return null;
711        }
712        if (commandLine.hasOption(TIME_ZONE_OPTION)) {
713            return commandLine.getOptionValue(TIME_ZONE_OPTION);
714        }
715        String timeZoneId = System.getenv(ENV_OOZIE_TIME_ZONE);
716        if (timeZoneId != null) {
717            return timeZoneId;
718        }
719        return "GMT";
720    }
721
722    // Canibalized from Hadoop <code>Configuration.loadResource()</code>.
723    private Properties parse(InputStream is, Properties conf) throws IOException {
724        try {
725            DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
726            // ignore all comments inside the xml file
727            docBuilderFactory.setIgnoringComments(true);
728            DocumentBuilder builder = docBuilderFactory.newDocumentBuilder();
729            Document doc = builder.parse(is);
730            return parseDocument(doc, conf);
731        }
732        catch (SAXException e) {
733            throw new IOException(e);
734        }
735        catch (ParserConfigurationException e) {
736            throw new IOException(e);
737        }
738    }
739
740    // Canibalized from Hadoop <code>Configuration.loadResource()</code>.
741    private Properties parseDocument(Document doc, Properties conf) throws IOException {
742        try {
743            Element root = doc.getDocumentElement();
744            if (!"configuration".equals(root.getTagName())) {
745                throw new RuntimeException("bad conf file: top-level element not <configuration>");
746            }
747            NodeList props = root.getChildNodes();
748            for (int i = 0; i < props.getLength(); i++) {
749                Node propNode = props.item(i);
750                if (!(propNode instanceof Element)) {
751                    continue;
752                }
753                Element prop = (Element) propNode;
754                if (!"property".equals(prop.getTagName())) {
755                    throw new RuntimeException("bad conf file: element not <property>");
756                }
757                NodeList fields = prop.getChildNodes();
758                String attr = null;
759                String value = null;
760                for (int j = 0; j < fields.getLength(); j++) {
761                    Node fieldNode = fields.item(j);
762                    if (!(fieldNode instanceof Element)) {
763                        continue;
764                    }
765                    Element field = (Element) fieldNode;
766                    if ("name".equals(field.getTagName()) && field.hasChildNodes()) {
767                        attr = ((Text) field.getFirstChild()).getData();
768                    }
769                    if ("value".equals(field.getTagName()) && field.hasChildNodes()) {
770                        value = ((Text) field.getFirstChild()).getData();
771                    }
772                }
773
774                if (attr != null && value != null) {
775                    conf.setProperty(attr, value);
776                }
777            }
778            return conf;
779        }
780        catch (DOMException e) {
781            throw new IOException(e);
782        }
783    }
784
785    private Properties getConfiguration(OozieClient wc, CommandLine commandLine) throws IOException {
786        if (!isConfigurationSpecified(wc, commandLine)) {
787            throw new IOException("configuration is not specified");
788        }
789        Properties conf = wc.createConfiguration();
790        String configFile = commandLine.getOptionValue(CONFIG_OPTION);
791        if (configFile != null) {
792            File file = new File(configFile);
793            if (!file.exists()) {
794                throw new IOException("configuration file [" + configFile + "] not found");
795            }
796            if (configFile.endsWith(".properties")) {
797                conf.load(new FileReader(file));
798            }
799            else if (configFile.endsWith(".xml")) {
800                parse(new FileInputStream(configFile), conf);
801            }
802            else {
803                throw new IllegalArgumentException("configuration must be a '.properties' or a '.xml' file");
804            }
805        }
806        if (commandLine.hasOption("D")) {
807            Properties commandLineProperties = commandLine.getOptionProperties("D");
808            conf.putAll(commandLineProperties);
809        }
810        return conf;
811    }
812
813    /**
814     * Check if configuration has specified
815     * @param wc
816     * @param commandLine
817     * @return
818     * @throws IOException
819     */
820    private boolean isConfigurationSpecified(OozieClient wc, CommandLine commandLine) throws IOException {
821        boolean isConf = false;
822        String configFile = commandLine.getOptionValue(CONFIG_OPTION);
823        if (configFile == null) {
824            isConf = false;
825        }
826        else {
827            isConf = new File(configFile).exists();
828        }
829        if (commandLine.hasOption("D")) {
830            isConf = true;
831        }
832        return isConf;
833    }
834
835    /**
836     * @param commandLine command line string.
837     * @return change value specified by -value.
838     * @throws OozieCLIException
839     */
840        private String getChangeValue(CommandLine commandLine) throws OozieCLIException {
841        String changeValue = commandLine.getOptionValue(CHANGE_VALUE_OPTION);
842
843        if (changeValue == null) {
844            throw new OozieCLIException("-value option needs to be specified for -change option");
845        }
846
847        return changeValue;
848    }
849
850    protected void addHeader(OozieClient wc) {
851        for (Map.Entry entry : System.getProperties().entrySet()) {
852            String key = (String) entry.getKey();
853            if (key.startsWith(WS_HEADER_PREFIX)) {
854                String header = key.substring(WS_HEADER_PREFIX.length());
855                wc.setHeader(header, (String) entry.getValue());
856            }
857        }
858    }
859
860    /**
861     * Get auth option from command line
862     *
863     * @param commandLine the command line object
864     * @return auth option
865     */
866    protected String getAuthOption(CommandLine commandLine) {
867        String authOpt = commandLine.getOptionValue(AUTH_OPTION);
868        if (authOpt == null) {
869            authOpt = System.getenv(ENV_OOZIE_AUTH);
870        }
871        if (commandLine.hasOption(DEBUG_OPTION)) {
872            System.out.println(" Auth type : " + authOpt);
873        }
874        return authOpt;
875    }
876
877    /**
878     * Create a OozieClient.
879     * <p/>
880     * It injects any '-Dheader:' as header to the the {@link org.apache.oozie.client.OozieClient}.
881     *
882     * @param commandLine the parsed command line options.
883     * @return a pre configured eXtended workflow client.
884     * @throws OozieCLIException thrown if the OozieClient could not be configured.
885     */
886    protected OozieClient createOozieClient(CommandLine commandLine) throws OozieCLIException {
887        return createXOozieClient(commandLine);
888    }
889
890    /**
891     * Create a XOozieClient.
892     * <p/>
893     * It injects any '-Dheader:' as header to the the {@link org.apache.oozie.client.OozieClient}.
894     *
895     * @param commandLine the parsed command line options.
896     * @return a pre configured eXtended workflow client.
897     * @throws OozieCLIException thrown if the XOozieClient could not be configured.
898     */
899    protected XOozieClient createXOozieClient(CommandLine commandLine) throws OozieCLIException {
900        XOozieClient wc = new AuthOozieClient(getOozieUrl(commandLine), getAuthOption(commandLine));
901        addHeader(wc);
902        setDebugMode(wc,commandLine.hasOption(DEBUG_OPTION));
903        setRetryCount(wc);
904        return wc;
905    }
906
907    protected void setDebugMode(OozieClient wc, boolean debugOpt) {
908
909        String debug = System.getenv(ENV_OOZIE_DEBUG);
910        if (debug != null && !debug.isEmpty()) {
911            int debugVal = 0;
912            try {
913                debugVal = Integer.parseInt(debug.trim());
914            }
915            catch (Exception ex) {
916                System.out.println("Unable to parse the debug settings. May be not an integer [" + debug + "]");
917                ex.printStackTrace();
918            }
919            wc.setDebugMode(debugVal);
920        }
921        else if(debugOpt){  // CLI argument "-debug" used
922            wc.setDebugMode(1);
923        }
924    }
925
926    protected void setRetryCount(OozieClient wc) {
927        String retryCount = System.getProperty(OOZIE_RETRY_COUNT);
928        if (retryCount != null && !retryCount.isEmpty()) {
929            try {
930                int retry = Integer.parseInt(retryCount.trim());
931                wc.setRetryCount(retry);
932            }
933            catch (Exception ex) {
934                System.err.println("Unable to parse the retry settings. May be not an integer [" + retryCount + "]");
935                ex.printStackTrace();
936            }
937        }
938    }
939
940    private static String JOB_ID_PREFIX = "job: ";
941
942    private void jobCommand(CommandLine commandLine) throws IOException, OozieCLIException {
943        XOozieClient wc = createXOozieClient(commandLine);
944
945        List<String> options = new ArrayList<String>();
946        for (Option option : commandLine.getOptions()) {
947            options.add(option.getOpt());
948        }
949
950        try {
951            if (options.contains(SUBMIT_OPTION)) {
952                System.out.println(JOB_ID_PREFIX + wc.submit(getConfiguration(wc, commandLine)));
953            }
954            else if (options.contains(START_OPTION)) {
955                wc.start(commandLine.getOptionValue(START_OPTION));
956            }
957            else if (options.contains(DRYRUN_OPTION) && !options.contains(UPDATE_OPTION)) {
958                String dryrunStr = wc.dryrun(getConfiguration(wc, commandLine));
959                if (dryrunStr.equals("OK")) {  // workflow
960                    System.out.println("OK");
961                } else {                        // coordinator
962                    String[] dryrunStrs = dryrunStr.split("action for new instance");
963                    int arraysize = dryrunStrs.length;
964                    System.out.println("***coordJob after parsing: ***");
965                    System.out.println(dryrunStrs[0]);
966                    int aLen = dryrunStrs.length - 1;
967                    if (aLen < 0) {
968                        aLen = 0;
969                    }
970                    System.out.println("***total coord actions is " + aLen + " ***");
971                    for (int i = 1; i <= arraysize - 1; i++) {
972                        System.out.println(RULER);
973                        System.out.println("coordAction instance: " + i + ":");
974                        System.out.println(dryrunStrs[i]);
975                    }
976                }
977            }
978            else if (options.contains(SUSPEND_OPTION)) {
979                wc.suspend(commandLine.getOptionValue(SUSPEND_OPTION));
980            }
981            else if (options.contains(RESUME_OPTION)) {
982                wc.resume(commandLine.getOptionValue(RESUME_OPTION));
983            }
984            else if (options.contains(IGNORE_OPTION)) {
985                String ignoreScope = null;
986                if (options.contains(ACTION_OPTION)) {
987                    ignoreScope = commandLine.getOptionValue(ACTION_OPTION);
988                    if (ignoreScope == null || ignoreScope.isEmpty()) {
989                        throw new OozieCLIException("-" + ACTION_OPTION + " is empty");
990                    }
991                }
992                printCoordActionsStatus(wc.ignore(commandLine.getOptionValue(IGNORE_OPTION), ignoreScope));
993            }
994            else if (options.contains(KILL_OPTION)) {
995                if (commandLine.getOptionValue(KILL_OPTION).contains("-C")
996                        && (options.contains(DATE_OPTION) || options.contains(ACTION_OPTION))) {
997                    String coordJobId = commandLine.getOptionValue(KILL_OPTION);
998                    String scope = null;
999                    String rangeType = null;
1000                    if (options.contains(DATE_OPTION) && options.contains(ACTION_OPTION)) {
1001                        throw new OozieCLIException("Invalid options provided for rerun: either" + DATE_OPTION + " or "
1002                                + ACTION_OPTION + " expected. Don't use both at the same time.");
1003                    }
1004                    if (options.contains(DATE_OPTION)) {
1005                        rangeType = RestConstants.JOB_COORD_SCOPE_DATE;
1006                        scope = commandLine.getOptionValue(DATE_OPTION);
1007                    }
1008                    else if (options.contains(ACTION_OPTION)) {
1009                        rangeType = RestConstants.JOB_COORD_SCOPE_ACTION;
1010                        scope = commandLine.getOptionValue(ACTION_OPTION);
1011                    }
1012                    else {
1013                        throw new OozieCLIException("Invalid options provided for rerun: " + DATE_OPTION + " or "
1014                                + ACTION_OPTION + " expected.");
1015                    }
1016                    printCoordActions(wc.kill(coordJobId, rangeType, scope));
1017                }
1018                else {
1019                    wc.kill(commandLine.getOptionValue(KILL_OPTION));
1020                }
1021            }
1022            else if (options.contains(CHANGE_OPTION)) {
1023                wc.change(commandLine.getOptionValue(CHANGE_OPTION), getChangeValue(commandLine));
1024            }
1025            else if (options.contains(RUN_OPTION)) {
1026                System.out.println(JOB_ID_PREFIX + wc.run(getConfiguration(wc, commandLine)));
1027            }
1028            else if (options.contains(RERUN_OPTION)) {
1029                if (commandLine.getOptionValue(RERUN_OPTION).contains("-W")) {
1030                    if (isConfigurationSpecified(wc, commandLine)) {
1031                        wc.reRun(commandLine.getOptionValue(RERUN_OPTION), getConfiguration(wc, commandLine));
1032                    }
1033                    else {
1034                        wc.reRun(commandLine.getOptionValue(RERUN_OPTION), new Properties());
1035                    }
1036                }
1037                else if (commandLine.getOptionValue(RERUN_OPTION).contains("-B")) {
1038                    String bundleJobId = commandLine.getOptionValue(RERUN_OPTION);
1039                    String coordScope = null;
1040                    String dateScope = null;
1041                    boolean refresh = false;
1042                    boolean noCleanup = false;
1043                    if (options.contains(ACTION_OPTION)) {
1044                        throw new OozieCLIException("Invalid options provided for bundle rerun. " + ACTION_OPTION
1045                                + " is not valid for bundle rerun");
1046                    }
1047                    if (options.contains(DATE_OPTION)) {
1048                        dateScope = commandLine.getOptionValue(DATE_OPTION);
1049                    }
1050
1051                    if (options.contains(RERUN_COORD_OPTION)) {
1052                        coordScope = commandLine.getOptionValue(RERUN_COORD_OPTION);
1053                    }
1054
1055                    if (options.contains(RERUN_REFRESH_OPTION)) {
1056                        refresh = true;
1057                    }
1058                    if (options.contains(RERUN_NOCLEANUP_OPTION)) {
1059                        noCleanup = true;
1060                    }
1061                    wc.reRunBundle(bundleJobId, coordScope, dateScope, refresh, noCleanup);
1062                    if (coordScope != null && !coordScope.isEmpty()) {
1063                        System.out.println("Coordinators [" + coordScope + "] of bundle " + bundleJobId
1064                                + " are scheduled to rerun on date ranges [" + dateScope + "].");
1065                    }
1066                    else {
1067                        System.out.println("All coordinators of bundle " + bundleJobId
1068                                + " are scheduled to rerun on the date ranges [" + dateScope + "].");
1069                    }
1070                }
1071                else {
1072                    String coordJobId = commandLine.getOptionValue(RERUN_OPTION);
1073                    String scope = null;
1074                    String rerunType = null;
1075                    boolean refresh = false;
1076                    boolean noCleanup = false;
1077                    if (options.contains(DATE_OPTION) && options.contains(ACTION_OPTION)) {
1078                        throw new OozieCLIException("Invalid options provided for rerun: either" + DATE_OPTION + " or "
1079                                + ACTION_OPTION + " expected. Don't use both at the same time.");
1080                    }
1081                    if (options.contains(DATE_OPTION)) {
1082                        rerunType = RestConstants.JOB_COORD_SCOPE_DATE;
1083                        scope = commandLine.getOptionValue(DATE_OPTION);
1084                    }
1085                    else if (options.contains(ACTION_OPTION)) {
1086                        rerunType = RestConstants.JOB_COORD_SCOPE_ACTION;
1087                        scope = commandLine.getOptionValue(ACTION_OPTION);
1088                    }
1089                    else {
1090                        throw new OozieCLIException("Invalid options provided for rerun: " + DATE_OPTION + " or "
1091                                + ACTION_OPTION + " expected.");
1092                    }
1093                    if (options.contains(RERUN_REFRESH_OPTION)) {
1094                        refresh = true;
1095                    }
1096                    if (options.contains(RERUN_NOCLEANUP_OPTION)) {
1097                        noCleanup = true;
1098                    }
1099                    if (options.contains(RERUN_FAILED_OPTION)) {
1100                        printCoordActions(wc.reRunCoord(coordJobId, rerunType, scope, refresh, noCleanup, true));
1101                    } else {
1102                        printCoordActions(wc.reRunCoord(coordJobId, rerunType, scope, refresh, noCleanup));
1103                    }
1104                }
1105            }
1106            else if (options.contains(INFO_OPTION)) {
1107                String timeZoneId = getTimeZoneId(commandLine);
1108                final String optionValue = commandLine.getOptionValue(INFO_OPTION);
1109                if (optionValue.endsWith("-B")) {
1110                    String filter = commandLine.getOptionValue(FILTER_OPTION);
1111                    if (filter != null) {
1112                        throw new OozieCLIException("Filter option is currently not supported for a Bundle job");
1113                    }
1114                    printBundleJob(wc.getBundleJobInfo(optionValue), timeZoneId,
1115                            options.contains(VERBOSE_OPTION));
1116                }
1117                else if (optionValue.endsWith("-C")) {
1118                    String s = commandLine.getOptionValue(OFFSET_OPTION);
1119                    int start = Integer.parseInt((s != null) ? s : "-1");
1120                    s = commandLine.getOptionValue(LEN_OPTION);
1121                    int len = Integer.parseInt((s != null) ? s : "-1");
1122                    String filter = commandLine.getOptionValue(FILTER_OPTION);
1123                    String order = commandLine.getOptionValue(ORDER_OPTION);
1124                    printCoordJob(wc.getCoordJobInfo(optionValue, filter, start, len, order), timeZoneId,
1125                            options.contains(VERBOSE_OPTION));
1126                }
1127                else if (optionValue.contains("-C@")) {
1128                    if (options.contains(ALL_WORKFLOWS_FOR_COORD_ACTION)) {
1129                        printWfsForCoordAction(wc.getWfsForCoordAction(optionValue), timeZoneId);
1130                    }
1131                    else {
1132                        String filter = commandLine.getOptionValue(FILTER_OPTION);
1133                        if (filter != null) {
1134                            throw new OozieCLIException("Filter option is not supported for a Coordinator action");
1135                        }
1136                        printCoordAction(wc.getCoordActionInfo(optionValue), timeZoneId);
1137                    }
1138                }
1139                else if (optionValue.contains("-W@")) {
1140                    String filter = commandLine.getOptionValue(FILTER_OPTION);
1141                    if (filter != null) {
1142                        throw new OozieCLIException("Filter option is not supported for a Workflow action");
1143                    }
1144                    printWorkflowAction(wc.getWorkflowActionInfo(optionValue), timeZoneId,
1145                            options.contains(VERBOSE_OPTION));
1146                }
1147                else {
1148                    String filter = commandLine.getOptionValue(FILTER_OPTION);
1149                    if (filter != null) {
1150                        throw new OozieCLIException("Filter option is currently not supported for a Workflow job");
1151                    }
1152                    String s = commandLine.getOptionValue(OFFSET_OPTION);
1153                    int start = Integer.parseInt((s != null) ? s : "0");
1154                    s = commandLine.getOptionValue(LEN_OPTION);
1155                    String jobtype = commandLine.getOptionValue(JOBTYPE_OPTION);
1156                    jobtype = (jobtype != null) ? jobtype : "wf";
1157                    int len = Integer.parseInt((s != null) ? s : "0");
1158                    printJob(wc.getJobInfo(optionValue, start, len), timeZoneId,
1159                            options.contains(VERBOSE_OPTION));
1160                }
1161            }
1162            else if (options.contains(LOG_OPTION)) {
1163                PrintStream ps = System.out;
1164                String logFilter = null;
1165                if (options.contains(RestConstants.LOG_FILTER_OPTION)) {
1166                    logFilter = commandLine.getOptionValue(RestConstants.LOG_FILTER_OPTION);
1167                }
1168                if (commandLine.getOptionValue(LOG_OPTION).contains("-C")) {
1169                    String logRetrievalScope = null;
1170                    String logRetrievalType = null;
1171                    if (options.contains(ACTION_OPTION)) {
1172                        logRetrievalType = RestConstants.JOB_LOG_ACTION;
1173                        logRetrievalScope = commandLine.getOptionValue(ACTION_OPTION);
1174                    }
1175                    if (options.contains(DATE_OPTION)) {
1176                        logRetrievalType = RestConstants.JOB_LOG_DATE;
1177                        logRetrievalScope = commandLine.getOptionValue(DATE_OPTION);
1178                    }
1179                    try {
1180                        wc.getJobLog(commandLine.getOptionValue(LOG_OPTION), logRetrievalType, logRetrievalScope,
1181                                logFilter, ps);
1182                    }
1183                    finally {
1184                        ps.close();
1185                    }
1186                }
1187                else {
1188                    if (!options.contains(ACTION_OPTION) && !options.contains(DATE_OPTION)) {
1189                        wc.getJobLog(commandLine.getOptionValue(LOG_OPTION), null, null, logFilter, ps);
1190                    }
1191                    else {
1192                        throw new OozieCLIException("Invalid options provided for log retrieval. " + ACTION_OPTION
1193                                + " and " + DATE_OPTION + " are valid only for coordinator job log retrieval");
1194                    }
1195                }
1196            }
1197            else if (options.contains(DEFINITION_OPTION)) {
1198                System.out.println(wc.getJobDefinition(commandLine.getOptionValue(DEFINITION_OPTION)));
1199            }
1200            else if (options.contains(CONFIG_CONTENT_OPTION)) {
1201                if (commandLine.getOptionValue(CONFIG_CONTENT_OPTION).endsWith("-C")) {
1202                    System.out.println(wc.getCoordJobInfo(commandLine.getOptionValue(CONFIG_CONTENT_OPTION)).getConf());
1203                }
1204                else if (commandLine.getOptionValue(CONFIG_CONTENT_OPTION).endsWith("-W")) {
1205                    System.out.println(wc.getJobInfo(commandLine.getOptionValue(CONFIG_CONTENT_OPTION)).getConf());
1206                }
1207                else if (commandLine.getOptionValue(CONFIG_CONTENT_OPTION).endsWith("-B")) {
1208                    System.out
1209                            .println(wc.getBundleJobInfo(commandLine.getOptionValue(CONFIG_CONTENT_OPTION)).getConf());
1210                }
1211                else {
1212                    System.out.println("ERROR:  job id [" + commandLine.getOptionValue(CONFIG_CONTENT_OPTION)
1213                            + "] doesn't end with either C or W or B");
1214                }
1215            }
1216            else if (options.contains(UPDATE_OPTION)) {
1217                String coordJobId = commandLine.getOptionValue(UPDATE_OPTION);
1218                Properties conf = null;
1219
1220                String dryrun = "";
1221                String showdiff = "";
1222
1223                if (commandLine.getOptionValue(CONFIG_OPTION) != null) {
1224                    conf = getConfiguration(wc, commandLine);
1225                }
1226                if (options.contains(DRYRUN_OPTION)) {
1227                    dryrun = "true";
1228                }
1229                if (commandLine.getOptionValue(SHOWDIFF_OPTION) != null) {
1230                    showdiff = commandLine.getOptionValue(SHOWDIFF_OPTION);
1231                }
1232                if (conf == null) {
1233                    System.out.println(wc.updateCoord(coordJobId, dryrun, showdiff));
1234                }
1235                else {
1236                    System.out.println(wc.updateCoord(coordJobId, conf, dryrun, showdiff));
1237                }
1238            }
1239            else if (options.contains(POLL_OPTION)) {
1240                String jobId = commandLine.getOptionValue(POLL_OPTION);
1241                int timeout = 30;
1242                int interval = 5;
1243                String timeoutS = commandLine.getOptionValue(TIMEOUT_OPTION);
1244                if (timeoutS != null) {
1245                    timeout = Integer.parseInt(timeoutS);
1246                }
1247                String intervalS = commandLine.getOptionValue(INTERVAL_OPTION);
1248                if (intervalS != null) {
1249                    interval = Integer.parseInt(intervalS);
1250                }
1251                boolean verbose = commandLine.hasOption(VERBOSE_OPTION);
1252                wc.pollJob(jobId, timeout, interval, verbose);
1253            }
1254        }
1255        catch (OozieClientException ex) {
1256            throw new OozieCLIException(ex.toString(), ex);
1257        }
1258    }
1259
1260    @VisibleForTesting
1261    void printCoordJob(CoordinatorJob coordJob, String timeZoneId, boolean verbose) {
1262        System.out.println("Job ID : " + coordJob.getId());
1263
1264        System.out.println(RULER);
1265
1266        List<CoordinatorAction> actions = coordJob.getActions();
1267        System.out.println("Job Name    : " + maskIfNull(coordJob.getAppName()));
1268        System.out.println("App Path    : " + maskIfNull(coordJob.getAppPath()));
1269        System.out.println("Status      : " + coordJob.getStatus());
1270        System.out.println("Start Time  : " + maskDate(coordJob.getStartTime(), timeZoneId, false));
1271        System.out.println("End Time    : " + maskDate(coordJob.getEndTime(), timeZoneId, false));
1272        System.out.println("Pause Time  : " + maskDate(coordJob.getPauseTime(), timeZoneId, false));
1273        System.out.println("Concurrency : " + coordJob.getConcurrency());
1274        System.out.println(RULER);
1275
1276        if (verbose) {
1277            System.out.println("ID" + VERBOSE_DELIMITER + "Action Number" + VERBOSE_DELIMITER + "Console URL"
1278                    + VERBOSE_DELIMITER + "Error Code" + VERBOSE_DELIMITER + "Error Message" + VERBOSE_DELIMITER
1279                    + "External ID" + VERBOSE_DELIMITER + "External Status" + VERBOSE_DELIMITER + "Job ID"
1280                    + VERBOSE_DELIMITER + "Tracker URI" + VERBOSE_DELIMITER + "Created" + VERBOSE_DELIMITER
1281                    + "Nominal Time" + VERBOSE_DELIMITER + "Status" + VERBOSE_DELIMITER + "Last Modified"
1282                    + VERBOSE_DELIMITER + "Missing Dependencies");
1283            System.out.println(RULER);
1284
1285            for (CoordinatorAction action : actions) {
1286                System.out.println(maskIfNull(action.getId()) + VERBOSE_DELIMITER + action.getActionNumber()
1287                        + VERBOSE_DELIMITER + maskIfNull(action.getConsoleUrl()) + VERBOSE_DELIMITER
1288                        + maskIfNull(action.getErrorCode()) + VERBOSE_DELIMITER + maskIfNull(action.getErrorMessage())
1289                        + VERBOSE_DELIMITER + maskIfNull(action.getExternalId()) + VERBOSE_DELIMITER
1290                        + maskIfNull(action.getExternalStatus()) + VERBOSE_DELIMITER + maskIfNull(action.getJobId())
1291                        + VERBOSE_DELIMITER + maskIfNull(action.getTrackerUri()) + VERBOSE_DELIMITER
1292                        + maskDate(action.getCreatedTime(), timeZoneId, verbose) + VERBOSE_DELIMITER
1293                        + maskDate(action.getNominalTime(), timeZoneId, verbose) + action.getStatus() + VERBOSE_DELIMITER
1294                        + maskDate(action.getLastModifiedTime(), timeZoneId, verbose) + VERBOSE_DELIMITER
1295                        + maskIfNull(getFirstMissingDependencies(action)));
1296
1297                System.out.println(RULER);
1298            }
1299        }
1300        else {
1301            System.out.println(String.format(COORD_ACTION_FORMATTER, "ID", "Status", "Ext ID", "Err Code", "Created",
1302                    "Nominal Time", "Last Mod"));
1303
1304            for (CoordinatorAction action : actions) {
1305                System.out.println(String.format(COORD_ACTION_FORMATTER, maskIfNull(action.getId()),
1306                        action.getStatus(), maskIfNull(action.getExternalId()), maskIfNull(action.getErrorCode()),
1307                        maskDate(action.getCreatedTime(), timeZoneId, verbose), maskDate(action.getNominalTime(), timeZoneId, verbose),
1308                        maskDate(action.getLastModifiedTime(), timeZoneId, verbose)));
1309
1310                System.out.println(RULER);
1311            }
1312        }
1313    }
1314
1315    @VisibleForTesting
1316    void printBundleJob(BundleJob bundleJob, String timeZoneId, boolean verbose) {
1317        System.out.println("Job ID : " + bundleJob.getId());
1318
1319        System.out.println(RULER);
1320
1321        List<CoordinatorJob> coordinators = bundleJob.getCoordinators();
1322        System.out.println("Job Name : " + maskIfNull(bundleJob.getAppName()));
1323        System.out.println("App Path : " + maskIfNull(bundleJob.getAppPath()));
1324        System.out.println("Status   : " + bundleJob.getStatus());
1325        System.out.println("Kickoff time   : " + bundleJob.getKickoffTime());
1326        System.out.println(RULER);
1327
1328        System.out.println(String.format(BUNDLE_COORD_JOBS_FORMATTER, "Job ID", "Status", "Freq", "Unit", "Started",
1329                "Next Materialized"));
1330        System.out.println(RULER);
1331
1332        for (CoordinatorJob job : coordinators) {
1333            System.out.println(String.format(BUNDLE_COORD_JOBS_FORMATTER, maskIfNull(job.getId()), job.getStatus(),
1334                    job.getFrequency(), job.getTimeUnit(), maskDate(job.getStartTime(), timeZoneId, verbose),
1335                    maskDate(job.getNextMaterializedTime(), timeZoneId, verbose)));
1336
1337            System.out.println(RULER);
1338        }
1339    }
1340
1341    @VisibleForTesting
1342    void printCoordAction(CoordinatorAction coordAction, String timeZoneId) {
1343        System.out.println("ID : " + maskIfNull(coordAction.getId()));
1344
1345        System.out.println(RULER);
1346
1347        System.out.println("Action Number        : " + coordAction.getActionNumber());
1348        System.out.println("Console URL          : " + maskIfNull(coordAction.getConsoleUrl()));
1349        System.out.println("Error Code           : " + maskIfNull(coordAction.getErrorCode()));
1350        System.out.println("Error Message        : " + maskIfNull(coordAction.getErrorMessage()));
1351        System.out.println("External ID          : " + maskIfNull(coordAction.getExternalId()));
1352        System.out.println("External Status      : " + maskIfNull(coordAction.getExternalStatus()));
1353        System.out.println("Job ID               : " + maskIfNull(coordAction.getJobId()));
1354        System.out.println("Tracker URI          : " + maskIfNull(coordAction.getTrackerUri()));
1355        System.out.println("Created              : " + maskDate(coordAction.getCreatedTime(), timeZoneId, false));
1356        System.out.println("Nominal Time         : " + maskDate(coordAction.getNominalTime(), timeZoneId, false));
1357        System.out.println("Status               : " + coordAction.getStatus());
1358        System.out.println("Last Modified        : " + maskDate(coordAction.getLastModifiedTime(), timeZoneId, false));
1359        System.out.println("First Missing Dependency : " + maskIfNull(getFirstMissingDependencies(coordAction)));
1360
1361        System.out.println(RULER);
1362    }
1363
1364    private void printCoordActions(List<CoordinatorAction> actions) {
1365        if (actions != null && actions.size() > 0) {
1366            System.out.println("Action ID" + VERBOSE_DELIMITER + "Nominal Time");
1367            System.out.println(RULER);
1368            for (CoordinatorAction action : actions) {
1369                System.out.println(maskIfNull(action.getId()) + VERBOSE_DELIMITER
1370                        + maskDate(action.getNominalTime(), null,false));
1371            }
1372        }
1373        else {
1374            System.out.println("No Actions match your criteria!");
1375        }
1376    }
1377
1378    private void printCoordActionsStatus(List<CoordinatorAction> actions) {
1379        if (actions != null && actions.size() > 0) {
1380            System.out.println("Action ID" + VERBOSE_DELIMITER + "Nominal Time" + VERBOSE_DELIMITER + "Status");
1381            System.out.println(RULER);
1382            for (CoordinatorAction action : actions) {
1383                System.out.println(maskIfNull(action.getId()) + VERBOSE_DELIMITER
1384                        + maskDate(action.getNominalTime(), null, false) + VERBOSE_DELIMITER
1385                        + maskIfNull(action.getStatus().name()));
1386            }
1387        }
1388    }
1389
1390    @VisibleForTesting
1391    void printWorkflowAction(WorkflowAction action, String timeZoneId, boolean verbose) {
1392
1393        System.out.println("ID : " + maskIfNull(action.getId()));
1394
1395        System.out.println(RULER);
1396
1397        System.out.println("Console URL       : " + maskIfNull(action.getConsoleUrl()));
1398        System.out.println("Error Code        : " + maskIfNull(action.getErrorCode()));
1399        System.out.println("Error Message     : " + maskIfNull(action.getErrorMessage()));
1400        System.out.println("External ID       : " + maskIfNull(action.getExternalId()));
1401        System.out.println("External Status   : " + maskIfNull(action.getExternalStatus()));
1402        System.out.println("Name              : " + maskIfNull(action.getName()));
1403        System.out.println("Retries           : " + action.getRetries());
1404        System.out.println("Tracker URI       : " + maskIfNull(action.getTrackerUri()));
1405        System.out.println("Type              : " + maskIfNull(action.getType()));
1406        System.out.println("Started           : " + maskDate(action.getStartTime(), timeZoneId, verbose));
1407        System.out.println("Status            : " + action.getStatus());
1408        System.out.println("Ended             : " + maskDate(action.getEndTime(), timeZoneId, verbose));
1409
1410        if (verbose) {
1411            System.out.println("External Stats    : " + action.getStats());
1412            System.out.println("External ChildIDs : " + action.getExternalChildIDs());
1413        }
1414
1415        System.out.println(RULER);
1416    }
1417
1418    private static final String WORKFLOW_JOBS_FORMATTER = "%-41s%-13s%-10s%-10s%-10s%-24s%-24s";
1419    private static final String COORD_JOBS_FORMATTER = "%-41s%-15s%-10s%-5s%-13s%-24s%-24s";
1420    private static final String BUNDLE_JOBS_FORMATTER = "%-41s%-15s%-10s%-20s%-20s%-13s%-13s";
1421    private static final String BUNDLE_COORD_JOBS_FORMATTER = "%-41s%-15s%-5s%-13s%-24s%-24s";
1422
1423    private static final String WORKFLOW_ACTION_FORMATTER = "%-78s%-10s%-23s%-11s%-10s";
1424    private static final String COORD_ACTION_FORMATTER = "%-43s%-10s%-37s%-10s%-21s%-21s";
1425    private static final String BULK_RESPONSE_FORMATTER = "%-13s%-38s%-13s%-41s%-10s%-38s%-21s%-38s";
1426
1427    @VisibleForTesting
1428    void printJob(WorkflowJob job, String timeZoneId, boolean verbose) throws IOException {
1429        System.out.println("Job ID : " + maskIfNull(job.getId()));
1430
1431        System.out.println(RULER);
1432
1433        System.out.println("Workflow Name : " + maskIfNull(job.getAppName()));
1434        System.out.println("App Path      : " + maskIfNull(job.getAppPath()));
1435        System.out.println("Status        : " + job.getStatus());
1436        System.out.println("Run           : " + job.getRun());
1437        System.out.println("User          : " + maskIfNull(job.getUser()));
1438        System.out.println("Group         : " + maskIfNull(job.getGroup()));
1439        System.out.println("Created       : " + maskDate(job.getCreatedTime(), timeZoneId, verbose));
1440        System.out.println("Started       : " + maskDate(job.getStartTime(), timeZoneId, verbose));
1441        System.out.println("Last Modified : " + maskDate(job.getLastModifiedTime(), timeZoneId, verbose));
1442        System.out.println("Ended         : " + maskDate(job.getEndTime(), timeZoneId, verbose));
1443        System.out.println("CoordAction ID: " + maskIfNull(job.getParentId()));
1444
1445        List<WorkflowAction> actions = job.getActions();
1446
1447        if (actions != null && actions.size() > 0) {
1448            System.out.println();
1449            System.out.println("Actions");
1450            System.out.println(RULER);
1451
1452            if (verbose) {
1453                System.out.println("ID" + VERBOSE_DELIMITER + "Console URL" + VERBOSE_DELIMITER + "Error Code"
1454                        + VERBOSE_DELIMITER + "Error Message" + VERBOSE_DELIMITER + "External ID" + VERBOSE_DELIMITER
1455                        + "External Status" + VERBOSE_DELIMITER + "Name" + VERBOSE_DELIMITER + "Retries"
1456                        + VERBOSE_DELIMITER + "Tracker URI" + VERBOSE_DELIMITER + "Type" + VERBOSE_DELIMITER
1457                        + "Started" + VERBOSE_DELIMITER + "Status" + VERBOSE_DELIMITER + "Ended");
1458                System.out.println(RULER);
1459
1460                for (WorkflowAction action : job.getActions()) {
1461                    System.out.println(maskIfNull(action.getId()) + VERBOSE_DELIMITER
1462                            + maskIfNull(action.getConsoleUrl()) + VERBOSE_DELIMITER
1463                            + maskIfNull(action.getErrorCode()) + VERBOSE_DELIMITER
1464                            + maskIfNull(action.getErrorMessage()) + VERBOSE_DELIMITER
1465                            + maskIfNull(action.getExternalId()) + VERBOSE_DELIMITER
1466                            + maskIfNull(action.getExternalStatus()) + VERBOSE_DELIMITER + maskIfNull(action.getName())
1467                            + VERBOSE_DELIMITER + action.getRetries() + VERBOSE_DELIMITER
1468                            + maskIfNull(action.getTrackerUri()) + VERBOSE_DELIMITER + maskIfNull(action.getType())
1469                            + VERBOSE_DELIMITER + maskDate(action.getStartTime(), timeZoneId, verbose)
1470                            + VERBOSE_DELIMITER + action.getStatus() + VERBOSE_DELIMITER
1471                            + maskDate(action.getEndTime(), timeZoneId, verbose));
1472
1473                    System.out.println(RULER);
1474                }
1475            }
1476            else {
1477                System.out.println(String.format(WORKFLOW_ACTION_FORMATTER, "ID", "Status", "Ext ID", "Ext Status",
1478                        "Err Code"));
1479
1480                System.out.println(RULER);
1481
1482                for (WorkflowAction action : job.getActions()) {
1483                    System.out.println(String.format(WORKFLOW_ACTION_FORMATTER, maskIfNull(action.getId()), action
1484                            .getStatus(), maskIfNull(action.getExternalId()), maskIfNull(action.getExternalStatus()),
1485                            maskIfNull(action.getErrorCode())));
1486
1487                    System.out.println(RULER);
1488                }
1489            }
1490        }
1491        else {
1492            System.out.println(RULER);
1493        }
1494
1495        System.out.println();
1496    }
1497
1498    private void jobsCommand(CommandLine commandLine) throws IOException, OozieCLIException {
1499        XOozieClient wc = createXOozieClient(commandLine);
1500
1501        List<String> options = new ArrayList<String>();
1502        for (Option option : commandLine.getOptions()) {
1503            options.add(option.getOpt());
1504        }
1505
1506        String filter = commandLine.getOptionValue(FILTER_OPTION);
1507        String s = commandLine.getOptionValue(OFFSET_OPTION);
1508        int start = Integer.parseInt((s != null) ? s : "0");
1509        s = commandLine.getOptionValue(LEN_OPTION);
1510        String jobtype = commandLine.getOptionValue(JOBTYPE_OPTION);
1511        String timeZoneId = getTimeZoneId(commandLine);
1512        jobtype = (jobtype != null) ? jobtype : "wf";
1513        int len = Integer.parseInt((s != null) ? s : "0");
1514        String bulkFilterString = commandLine.getOptionValue(BULK_OPTION);
1515
1516        try {
1517            if (options.contains(KILL_OPTION)) {
1518                printBulkModifiedJobs(wc.killJobs(filter, jobtype, start, len), timeZoneId, "killed");
1519            }
1520            else if (options.contains(SUSPEND_OPTION)) {
1521                printBulkModifiedJobs(wc.suspendJobs(filter, jobtype, start, len), timeZoneId, "suspended");
1522            }
1523            else if (options.contains(RESUME_OPTION)) {
1524                printBulkModifiedJobs(wc.resumeJobs(filter, jobtype, start, len), timeZoneId, "resumed");
1525            }
1526            else if (bulkFilterString != null) {
1527                printBulkJobs(wc.getBulkInfo(bulkFilterString, start, len), timeZoneId, commandLine.hasOption(VERBOSE_OPTION));
1528            }
1529            else if (jobtype.toLowerCase().contains("wf")) {
1530                printJobs(wc.getJobsInfo(filter, start, len), timeZoneId, commandLine.hasOption(VERBOSE_OPTION));
1531            }
1532            else if (jobtype.toLowerCase().startsWith("coord")) {
1533                printCoordJobs(wc.getCoordJobsInfo(filter, start, len), timeZoneId, commandLine.hasOption(VERBOSE_OPTION));
1534            }
1535            else if (jobtype.toLowerCase().startsWith("bundle")) {
1536                printBundleJobs(wc.getBundleJobsInfo(filter, start, len), timeZoneId, commandLine.hasOption(VERBOSE_OPTION));
1537            }
1538
1539        }
1540        catch (OozieClientException ex) {
1541            throw new OozieCLIException(ex.toString(), ex);
1542        }
1543    }
1544
1545    @VisibleForTesting
1546    void printBulkModifiedJobs(JSONObject json, String timeZoneId, String action) throws IOException {
1547        if (json.containsKey(JsonTags.WORKFLOWS_JOBS)) {
1548            JSONArray workflows = (JSONArray) json.get(JsonTags.WORKFLOWS_JOBS);
1549            if (workflows == null) {
1550                workflows = new JSONArray();
1551            }
1552            List<WorkflowJob> wfs = JsonToBean.createWorkflowJobList(workflows);
1553            if (wfs.isEmpty()) {
1554                System.out.println("bulk modify command did not modify any jobs");
1555            }
1556            else {
1557                System.out.println("the following jobs have been " + action);
1558                printJobs(wfs, timeZoneId, false);
1559            }
1560        }
1561        else if (json.containsKey(JsonTags.COORDINATOR_JOBS)) {
1562            JSONArray coordinators = (JSONArray) json.get(JsonTags.COORDINATOR_JOBS);
1563            if (coordinators == null) {
1564                coordinators = new JSONArray();
1565            }
1566            List<CoordinatorJob> coords = JsonToBean.createCoordinatorJobList(coordinators);
1567            if (coords.isEmpty()) {
1568                System.out.println("bulk modify command did not modify any jobs");
1569            }
1570            else {
1571                System.out.println("the following jobs have been " + action);
1572                printCoordJobs(coords, timeZoneId, false);
1573            }
1574        }
1575        else {
1576            JSONArray bundles = (JSONArray) json.get(JsonTags.BUNDLE_JOBS);
1577            if (bundles == null) {
1578                bundles = new JSONArray();
1579            }
1580            List<BundleJob> bundleJobs = JsonToBean.createBundleJobList(bundles);
1581            if (bundleJobs.isEmpty()) {
1582                System.out.println("bulk modify command did not modify any jobs");
1583            }
1584            else {
1585                System.out.println("the following jobs have been " + action);
1586                printBundleJobs(bundleJobs, timeZoneId, false);
1587            }
1588        }
1589    }
1590
1591    @VisibleForTesting
1592    void printCoordJobs(List<CoordinatorJob> jobs, String timeZoneId, boolean verbose) throws IOException {
1593        if (jobs != null && jobs.size() > 0) {
1594            if (verbose) {
1595                System.out.println("Job ID" + VERBOSE_DELIMITER + "App Name" + VERBOSE_DELIMITER + "App Path"
1596                        + VERBOSE_DELIMITER + "Console URL" + VERBOSE_DELIMITER + "User" + VERBOSE_DELIMITER + "Group"
1597                        + VERBOSE_DELIMITER + "Concurrency" + VERBOSE_DELIMITER + "Frequency" + VERBOSE_DELIMITER
1598                        + "Time Unit" + VERBOSE_DELIMITER + "Time Zone" + VERBOSE_DELIMITER + "Time Out"
1599                        + VERBOSE_DELIMITER + "Started" + VERBOSE_DELIMITER + "Next Materialize" + VERBOSE_DELIMITER
1600                        + "Status" + VERBOSE_DELIMITER + "Last Action" + VERBOSE_DELIMITER + "Ended");
1601                System.out.println(RULER);
1602
1603                for (CoordinatorJob job : jobs) {
1604                    System.out.println(maskIfNull(job.getId()) + VERBOSE_DELIMITER + maskIfNull(job.getAppName())
1605                            + VERBOSE_DELIMITER + maskIfNull(job.getAppPath()) + VERBOSE_DELIMITER
1606                            + maskIfNull(job.getConsoleUrl()) + VERBOSE_DELIMITER + maskIfNull(job.getUser())
1607                            + VERBOSE_DELIMITER + maskIfNull(job.getGroup()) + VERBOSE_DELIMITER + job.getConcurrency()
1608                            + VERBOSE_DELIMITER + job.getFrequency() + VERBOSE_DELIMITER + job.getTimeUnit()
1609                            + VERBOSE_DELIMITER + maskIfNull(job.getTimeZone()) + VERBOSE_DELIMITER + job.getTimeout()
1610                            + VERBOSE_DELIMITER + maskDate(job.getStartTime(), timeZoneId, verbose) + VERBOSE_DELIMITER
1611                            + maskDate(job.getNextMaterializedTime(), timeZoneId, verbose) + VERBOSE_DELIMITER
1612                            + job.getStatus() + VERBOSE_DELIMITER
1613                            + maskDate(job.getLastActionTime(), timeZoneId, verbose) + VERBOSE_DELIMITER
1614                            + maskDate(job.getEndTime(), timeZoneId, verbose));
1615
1616                    System.out.println(RULER);
1617                }
1618            }
1619            else {
1620                System.out.println(String.format(COORD_JOBS_FORMATTER, "Job ID", "App Name", "Status", "Freq", "Unit",
1621                        "Started", "Next Materialized"));
1622                System.out.println(RULER);
1623
1624                for (CoordinatorJob job : jobs) {
1625                    System.out.println(String.format(COORD_JOBS_FORMATTER, maskIfNull(job.getId()), maskIfNull(job
1626                            .getAppName()), job.getStatus(), job.getFrequency(), job.getTimeUnit(), maskDate(job
1627                            .getStartTime(), timeZoneId, verbose), maskDate(job.getNextMaterializedTime(), timeZoneId, verbose)));
1628
1629                    System.out.println(RULER);
1630                }
1631            }
1632        }
1633        else {
1634            System.out.println("No Jobs match your criteria!");
1635        }
1636    }
1637
1638    @VisibleForTesting
1639    void printBulkJobs(List<BulkResponse> jobs, String timeZoneId, boolean verbose) throws IOException {
1640        if (jobs != null && jobs.size() > 0) {
1641            for (BulkResponse response : jobs) {
1642                BundleJob bundle = response.getBundle();
1643                CoordinatorJob coord = response.getCoordinator();
1644                CoordinatorAction action = response.getAction();
1645                if (verbose) {
1646                    System.out.println();
1647                    System.out.println("Bundle Name : " + maskIfNull(bundle.getAppName()));
1648
1649                    System.out.println(RULER);
1650
1651                    System.out.println("Bundle ID        : " + maskIfNull(bundle.getId()));
1652                    System.out.println("Coordinator Name : " + maskIfNull(coord.getAppName()));
1653                    System.out.println("Coord Action ID  : " + maskIfNull(action.getId()));
1654                    System.out.println("Action Status    : " + action.getStatus());
1655                    System.out.println("External ID      : " + maskIfNull(action.getExternalId()));
1656                    System.out.println("Created Time     : " + maskDate(action.getCreatedTime(), timeZoneId, false));
1657                    System.out.println("User             : " + maskIfNull(bundle.getUser()));
1658                    System.out.println("Error Message    : " + maskIfNull(action.getErrorMessage()));
1659                    System.out.println(RULER);
1660                }
1661                else {
1662                    System.out.println(String.format(BULK_RESPONSE_FORMATTER, "Bundle Name", "Bundle ID", "Coord Name",
1663                            "Coord Action ID", "Status", "External ID", "Created Time", "Error Message"));
1664                    System.out.println(RULER);
1665                    System.out
1666                            .println(String.format(BULK_RESPONSE_FORMATTER, maskIfNull(bundle.getAppName()),
1667                                    maskIfNull(bundle.getId()), maskIfNull(coord.getAppName()),
1668                                    maskIfNull(action.getId()), action.getStatus(), maskIfNull(action.getExternalId()),
1669                                    maskDate(action.getCreatedTime(), timeZoneId, false),
1670                                    maskIfNull(action.getErrorMessage())));
1671                    System.out.println(RULER);
1672                }
1673            }
1674        }
1675        else {
1676            System.out.println("Bulk request criteria did not match any coordinator actions");
1677        }
1678    }
1679
1680    @VisibleForTesting
1681    void printBundleJobs(List<BundleJob> jobs, String timeZoneId, boolean verbose) throws IOException {
1682        if (jobs != null && jobs.size() > 0) {
1683            if (verbose) {
1684                System.out.println("Job ID" + VERBOSE_DELIMITER + "Bundle Name" + VERBOSE_DELIMITER + "Bundle Path"
1685                        + VERBOSE_DELIMITER + "User" + VERBOSE_DELIMITER + "Group" + VERBOSE_DELIMITER + "Status"
1686                        + VERBOSE_DELIMITER + "Kickoff" + VERBOSE_DELIMITER + "Pause" + VERBOSE_DELIMITER + "Created"
1687                        + VERBOSE_DELIMITER + "Console URL");
1688                System.out.println(RULER);
1689
1690                for (BundleJob job : jobs) {
1691                    System.out.println(maskIfNull(job.getId()) + VERBOSE_DELIMITER + maskIfNull(job.getAppName())
1692                            + VERBOSE_DELIMITER + maskIfNull(job.getAppPath()) + VERBOSE_DELIMITER
1693                            + maskIfNull(job.getUser()) + VERBOSE_DELIMITER + maskIfNull(job.getGroup())
1694                            + VERBOSE_DELIMITER + job.getStatus() + VERBOSE_DELIMITER
1695                            + maskDate(job.getKickoffTime(), timeZoneId, verbose) + VERBOSE_DELIMITER
1696                            + maskDate(job.getPauseTime(), timeZoneId, verbose) + VERBOSE_DELIMITER
1697                            + maskDate(job.getCreatedTime(), timeZoneId, verbose) + VERBOSE_DELIMITER
1698                            + maskIfNull(job.getConsoleUrl()));
1699
1700                    System.out.println(RULER);
1701                }
1702            }
1703            else {
1704                System.out.println(String.format(BUNDLE_JOBS_FORMATTER, "Job ID", "Bundle Name", "Status", "Kickoff",
1705                        "Created", "User", "Group"));
1706                System.out.println(RULER);
1707
1708                for (BundleJob job : jobs) {
1709                    System.out.println(String.format(BUNDLE_JOBS_FORMATTER, maskIfNull(job.getId()),
1710                            maskIfNull(job.getAppName()), job.getStatus(),
1711                            maskDate(job.getKickoffTime(), timeZoneId, verbose),
1712                            maskDate(job.getCreatedTime(), timeZoneId, verbose), maskIfNull(job.getUser()),
1713                            maskIfNull(job.getGroup())));
1714                    System.out.println(RULER);
1715                }
1716            }
1717        }
1718        else {
1719            System.out.println("No Jobs match your criteria!");
1720        }
1721    }
1722
1723    private void slaCommand(CommandLine commandLine) throws IOException, OozieCLIException {
1724        XOozieClient wc = createXOozieClient(commandLine);
1725        List<String> options = new ArrayList<String>();
1726        for (Option option : commandLine.getOptions()) {
1727            options.add(option.getOpt());
1728        }
1729
1730        String s = commandLine.getOptionValue(OFFSET_OPTION);
1731        int start = Integer.parseInt((s != null) ? s : "0");
1732        s = commandLine.getOptionValue(LEN_OPTION);
1733        int len = Integer.parseInt((s != null) ? s : "100");
1734        String filter = commandLine.getOptionValue(FILTER_OPTION);
1735
1736        try {
1737            wc.getSlaInfo(start, len, filter);
1738        }
1739        catch (OozieClientException ex) {
1740            throw new OozieCLIException(ex.toString(), ex);
1741        }
1742    }
1743
1744    private void adminCommand(CommandLine commandLine) throws OozieCLIException {
1745        XOozieClient wc = createXOozieClient(commandLine);
1746
1747        List<String> options = new ArrayList<String>();
1748        for (Option option : commandLine.getOptions()) {
1749            options.add(option.getOpt());
1750        }
1751
1752        try {
1753            SYSTEM_MODE status = SYSTEM_MODE.NORMAL;
1754            if (options.contains(VERSION_OPTION)) {
1755                System.out.println("Oozie server build version: " + wc.getServerBuildVersion());
1756            }
1757            else if (options.contains(SYSTEM_MODE_OPTION)) {
1758                String systemModeOption = commandLine.getOptionValue(SYSTEM_MODE_OPTION).toUpperCase();
1759                try {
1760                    status = SYSTEM_MODE.valueOf(systemModeOption);
1761                }
1762                catch (Exception e) {
1763                    throw new OozieCLIException("Invalid input provided for option: " + SYSTEM_MODE_OPTION
1764                            + " value given :" + systemModeOption
1765                            + " Expected values are: NORMAL/NOWEBSERVICE/SAFEMODE ");
1766                }
1767                wc.setSystemMode(status);
1768                System.out.println("System mode: " + status);
1769            }
1770            else if (options.contains(STATUS_OPTION)) {
1771                status = wc.getSystemMode();
1772                System.out.println("System mode: " + status);
1773            }
1774
1775            else if (options.contains(UPDATE_SHARELIB_OPTION)) {
1776                System.out.println(wc.updateShareLib());
1777            }
1778
1779            else if (options.contains(LIST_SHARELIB_LIB_OPTION)) {
1780                String sharelibKey = null;
1781                if (commandLine.getArgList().size() > 0) {
1782                    sharelibKey = (String) commandLine.getArgList().get(0);
1783                }
1784                System.out.println(wc.listShareLib(sharelibKey));
1785            }
1786
1787            else if (options.contains(QUEUE_DUMP_OPTION)) {
1788
1789                List<String> list = wc.getQueueDump();
1790                if (list != null && list.size() != 0) {
1791                    for (String str : list) {
1792                        System.out.println(str);
1793                    }
1794                }
1795                else {
1796                    System.out.println("QueueDump is null!");
1797                }
1798            }
1799            else if (options.contains(AVAILABLE_SERVERS_OPTION)) {
1800                Map<String, String> availableOozieServers = new TreeMap<String, String>(wc.getAvailableOozieServers());
1801                for (Map.Entry<String, String> ent : availableOozieServers.entrySet()) {
1802                    System.out.println(ent.getKey() + " : " + ent.getValue());
1803                }
1804            } else if (options.contains(SERVER_CONFIGURATION_OPTION)) {
1805                Map<String, String> serverConfig = new TreeMap<String, String>(wc.getServerConfiguration());
1806                for (Map.Entry<String, String> ent : serverConfig.entrySet()) {
1807                    System.out.println(ent.getKey() + " : " + ent.getValue());
1808                }
1809            } else if (options.contains(SERVER_OS_ENV_OPTION)) {
1810                Map<String, String> osEnv = new TreeMap<String, String>(wc.getOSEnv());
1811                for (Map.Entry<String, String> ent : osEnv.entrySet()) {
1812                    System.out.println(ent.getKey() + " : " + ent.getValue());
1813                }
1814            } else if (options.contains(SERVER_JAVA_SYSTEM_PROPERTIES_OPTION)) {
1815                Map<String, String> javaSysProps = new TreeMap<String, String>(wc.getJavaSystemProperties());
1816                for (Map.Entry<String, String> ent : javaSysProps.entrySet()) {
1817                    System.out.println(ent.getKey() + " : " + ent.getValue());
1818                }
1819            } else if (options.contains(METRICS_OPTION)) {
1820                OozieClient.Metrics metrics = wc.getMetrics();
1821                if (metrics == null) {
1822                    System.out.println("Metrics are unavailable.  Try Instrumentation (-" + INSTRUMENTATION_OPTION + ") instead");
1823                } else {
1824                    printMetrics(metrics);
1825                }
1826            } else if (options.contains(INSTRUMENTATION_OPTION)) {
1827                OozieClient.Instrumentation instrumentation = wc.getInstrumentation();
1828                if (instrumentation == null) {
1829                    System.out.println("Instrumentation is unavailable.  Try Metrics (-" + METRICS_OPTION + ") instead");
1830                } else {
1831                    printInstrumentation(instrumentation);
1832                }
1833            }
1834        }
1835        catch (OozieClientException ex) {
1836            throw new OozieCLIException(ex.toString(), ex);
1837        }
1838    }
1839
1840    private void versionCommand() throws OozieCLIException {
1841        System.out.println("Oozie client build version: "
1842                + BuildInfo.getBuildInfo().getProperty(BuildInfo.BUILD_VERSION));
1843    }
1844
1845    @VisibleForTesting
1846    void printJobs(List<WorkflowJob> jobs, String timeZoneId, boolean verbose) throws IOException {
1847        if (jobs != null && jobs.size() > 0) {
1848            if (verbose) {
1849                System.out.println("Job ID" + VERBOSE_DELIMITER + "App Name" + VERBOSE_DELIMITER + "App Path"
1850                        + VERBOSE_DELIMITER + "Console URL" + VERBOSE_DELIMITER + "User" + VERBOSE_DELIMITER + "Group"
1851                        + VERBOSE_DELIMITER + "Run" + VERBOSE_DELIMITER + "Created" + VERBOSE_DELIMITER + "Started"
1852                        + VERBOSE_DELIMITER + "Status" + VERBOSE_DELIMITER + "Last Modified" + VERBOSE_DELIMITER
1853                        + "Ended");
1854                System.out.println(RULER);
1855
1856                for (WorkflowJob job : jobs) {
1857                    System.out.println(maskIfNull(job.getId()) + VERBOSE_DELIMITER + maskIfNull(job.getAppName())
1858                            + VERBOSE_DELIMITER + maskIfNull(job.getAppPath()) + VERBOSE_DELIMITER
1859                            + maskIfNull(job.getConsoleUrl()) + VERBOSE_DELIMITER + maskIfNull(job.getUser())
1860                            + VERBOSE_DELIMITER + maskIfNull(job.getGroup()) + VERBOSE_DELIMITER + job.getRun()
1861                            + VERBOSE_DELIMITER + maskDate(job.getCreatedTime(), timeZoneId, verbose)
1862                            + VERBOSE_DELIMITER + maskDate(job.getStartTime(), timeZoneId, verbose) + VERBOSE_DELIMITER
1863                            + job.getStatus() + VERBOSE_DELIMITER
1864                            + maskDate(job.getLastModifiedTime(), timeZoneId, verbose) + VERBOSE_DELIMITER
1865                            + maskDate(job.getEndTime(), timeZoneId, verbose));
1866
1867                    System.out.println(RULER);
1868                }
1869            }
1870            else {
1871                System.out.println(String.format(WORKFLOW_JOBS_FORMATTER, "Job ID", "App Name", "Status", "User",
1872                        "Group", "Started", "Ended"));
1873                System.out.println(RULER);
1874
1875                for (WorkflowJob job : jobs) {
1876                    System.out.println(String.format(WORKFLOW_JOBS_FORMATTER, maskIfNull(job.getId()),
1877                            maskIfNull(job.getAppName()), job.getStatus(), maskIfNull(job.getUser()),
1878                            maskIfNull(job.getGroup()), maskDate(job.getStartTime(), timeZoneId, verbose),
1879                            maskDate(job.getEndTime(), timeZoneId, verbose)));
1880
1881                    System.out.println(RULER);
1882                }
1883            }
1884        }
1885        else {
1886            System.out.println("No Jobs match your criteria!");
1887        }
1888    }
1889
1890    void printWfsForCoordAction(List<WorkflowJob> jobs, String timeZoneId) throws IOException {
1891        if (jobs != null && jobs.size() > 0) {
1892            System.out.println(String.format("%-41s%-10s%-24s%-24s", "Job ID", "Status", "Started", "Ended"));
1893            System.out.println(RULER);
1894
1895            for (WorkflowJob job : jobs) {
1896                System.out
1897                        .println(String.format("%-41s%-10s%-24s%-24s", maskIfNull(job.getId()), job.getStatus(),
1898                                maskDate(job.getStartTime(), timeZoneId, false),
1899                                maskDate(job.getEndTime(), timeZoneId, false)));
1900                System.out.println(RULER);
1901            }
1902        }
1903    }
1904
1905    private String maskIfNull(String value) {
1906        if (value != null && value.length() > 0) {
1907            return value;
1908        }
1909        return "-";
1910    }
1911
1912    private String maskDate(Date date, String timeZoneId, boolean verbose) {
1913        if (date == null) {
1914            return "-";
1915        }
1916
1917        SimpleDateFormat dateFormater = null;
1918        if (verbose) {
1919            dateFormater = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss zzz", Locale.US);
1920        }
1921        else {
1922            dateFormater = new SimpleDateFormat("yyyy-MM-dd HH:mm zzz", Locale.US);
1923        }
1924
1925        if (timeZoneId != null) {
1926            dateFormater.setTimeZone(TimeZone.getTimeZone(timeZoneId));
1927        }
1928        String dateString = dateFormater.format(date);
1929        // 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
1930        // to fit better
1931        Matcher m = GMT_OFFSET_SHORTEN_PATTERN.matcher(dateString);
1932        if (m.matches() && m.groupCount() == 2) {
1933            dateString = m.group(1) + m.group(2);
1934        }
1935        return dateString;
1936    }
1937
1938    private void validateCommand(CommandLine commandLine) throws OozieCLIException {
1939        String[] args = commandLine.getArgs();
1940        if (args.length != 1) {
1941            throw new OozieCLIException("One file must be specified");
1942        }
1943        try {
1944            XOozieClient wc = createXOozieClient(commandLine);
1945            String result = wc.validateXML(args[0].toString());
1946            if (result == null) {
1947                // TODO This is only for backward compatibility. Need to remove after 4.2.0 higher version.
1948                System.out.println("Using client-side validation. Check out Oozie server version.");
1949                validateCommandV41(commandLine);
1950                return;
1951            }
1952            System.out.println(result);
1953        } catch (OozieClientException e) {
1954            throw new OozieCLIException(e.getMessage(), e);
1955        }
1956    }
1957
1958    /**
1959     * Validate on client-side. This is only for backward compatibility. Need to removed after <tt>4.2.0</tt> higher version.
1960     * @param commandLine
1961     * @throws OozieCLIException
1962     */
1963    @Deprecated
1964    @VisibleForTesting
1965    void validateCommandV41(CommandLine commandLine) throws OozieCLIException {
1966        String[] args = commandLine.getArgs();
1967        if (args.length != 1) {
1968            throw new OozieCLIException("One file must be specified");
1969        }
1970        File file = new File(args[0]);
1971        if (file.exists()) {
1972            try {
1973                List<StreamSource> sources = new ArrayList<StreamSource>();
1974                sources.add(new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream(
1975                        "oozie-workflow-0.1.xsd")));
1976                sources.add(new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream(
1977                        "shell-action-0.1.xsd")));
1978                sources.add(new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream(
1979                        "shell-action-0.2.xsd")));
1980                sources.add(new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream(
1981                        "shell-action-0.3.xsd")));
1982                sources.add(new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream(
1983                        "email-action-0.1.xsd")));
1984                sources.add(new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream(
1985                        "email-action-0.2.xsd")));
1986                sources.add(new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream(
1987                        "distcp-action-0.1.xsd")));
1988                sources.add(new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream(
1989                        "distcp-action-0.2.xsd")));
1990                sources.add(new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream(
1991                        "oozie-workflow-0.2.xsd")));
1992                sources.add(new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream(
1993                        "oozie-workflow-0.2.5.xsd")));
1994                sources.add(new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream(
1995                        "oozie-workflow-0.3.xsd")));
1996                sources.add(new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream(
1997                        "oozie-workflow-0.4.xsd")));
1998                sources.add(new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream(
1999                        "oozie-workflow-0.4.5.xsd")));
2000                sources.add(new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream(
2001                        "oozie-workflow-0.5.xsd")));
2002                sources.add(new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream(
2003                        "oozie-coordinator-0.1.xsd")));
2004                sources.add(new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream(
2005                        "oozie-coordinator-0.2.xsd")));
2006                sources.add(new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream(
2007                        "oozie-coordinator-0.3.xsd")));
2008                sources.add(new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream(
2009                        "oozie-coordinator-0.4.xsd")));
2010                sources.add(new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream(
2011                        "oozie-bundle-0.1.xsd")));
2012                sources.add(new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream(
2013                        "oozie-bundle-0.2.xsd")));
2014                sources.add(new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream(
2015                        "oozie-sla-0.1.xsd")));
2016                sources.add(new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream(
2017                        "oozie-sla-0.2.xsd")));
2018                sources.add(new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream(
2019                        "hive-action-0.2.xsd")));
2020                sources.add(new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream(
2021                        "hive-action-0.3.xsd")));
2022                sources.add(new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream(
2023                        "hive-action-0.4.xsd")));
2024                sources.add(new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream(
2025                        "hive-action-0.5.xsd")));
2026                sources.add(new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream(
2027                        "hive-action-0.6.xsd")));
2028                sources.add(new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream(
2029                        "sqoop-action-0.2.xsd")));
2030                sources.add(new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream(
2031                        "sqoop-action-0.3.xsd")));
2032                sources.add(new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream(
2033                        "sqoop-action-0.4.xsd")));
2034                sources.add(new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream(
2035                        "ssh-action-0.1.xsd")));
2036                sources.add(new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream(
2037                        "ssh-action-0.2.xsd")));
2038                sources.add(new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream(
2039                        "hive2-action-0.1.xsd")));
2040                sources.add(new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream(
2041                        "hive2-action-0.2.xsd")));
2042                sources.add(new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream(
2043                        "spark-action-0.1.xsd")));
2044                SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
2045                Schema schema = factory.newSchema(sources.toArray(new StreamSource[sources.size()]));
2046                Validator validator = schema.newValidator();
2047                validator.validate(new StreamSource(new FileReader(file)));
2048                System.out.println("Valid workflow-app");
2049            }
2050            catch (Exception ex) {
2051                throw new OozieCLIException("Invalid app definition, " + ex.toString(), ex);
2052            }
2053        }
2054        else {
2055            throw new OozieCLIException("File does not exists");
2056        }
2057    }
2058
2059    private void scriptLanguageCommand(CommandLine commandLine, String jobType) throws IOException, OozieCLIException {
2060        List<String> args = commandLine.getArgList();
2061        if (args.size() > 0) {
2062            // checking if args starts with -X (because CLIParser cannot check this)
2063            if (!args.get(0).equals("-X")) {
2064                throw new OozieCLIException("Unrecognized option: " + args.get(0) + " Expecting -X");
2065            }
2066            args.remove(0);
2067        }
2068
2069        if (!commandLine.hasOption(SCRIPTFILE_OPTION)) {
2070            throw new OozieCLIException("Need to specify -file <scriptfile>");
2071        }
2072
2073        if (!commandLine.hasOption(CONFIG_OPTION)) {
2074            throw new OozieCLIException("Need to specify -config <configfile>");
2075        }
2076
2077        try {
2078            XOozieClient wc = createXOozieClient(commandLine);
2079            Properties conf = getConfiguration(wc, commandLine);
2080            String script = commandLine.getOptionValue(SCRIPTFILE_OPTION);
2081            List<String> paramsList = new ArrayList<String>();
2082            if (commandLine.hasOption("P")) {
2083                Properties params = commandLine.getOptionProperties("P");
2084                for (String key : params.stringPropertyNames()) {
2085                    paramsList.add(key + "=" + params.getProperty(key));
2086                }
2087            }
2088            System.out.println(JOB_ID_PREFIX + wc.submitScriptLanguage(conf, script, args.toArray(new String[args.size()]),
2089                    paramsList.toArray(new String[paramsList.size()]), jobType));
2090        }
2091        catch (OozieClientException ex) {
2092            throw new OozieCLIException(ex.toString(), ex);
2093        }
2094    }
2095
2096    private void sqoopCommand(CommandLine commandLine) throws IOException, OozieCLIException {
2097        List<String> args = commandLine.getArgList();
2098        if (args.size() > 0) {
2099            // checking if args starts with -X (because CLIParser cannot check this)
2100            if (!args.get(0).equals("-X")) {
2101                throw new OozieCLIException("Unrecognized option: " + args.get(0) + " Expecting -X");
2102            }
2103            args.remove(0);
2104        }
2105
2106        if (!commandLine.hasOption(SQOOP_COMMAND_OPTION)) {
2107            throw new OozieCLIException("Need to specify -command");
2108        }
2109
2110        if (!commandLine.hasOption(CONFIG_OPTION)) {
2111            throw new OozieCLIException("Need to specify -config <configfile>");
2112        }
2113
2114        try {
2115            XOozieClient wc = createXOozieClient(commandLine);
2116            Properties conf = getConfiguration(wc, commandLine);
2117            String[] command = commandLine.getOptionValues(SQOOP_COMMAND_OPTION);
2118            System.out.println(JOB_ID_PREFIX + wc.submitSqoop(conf, command, args.toArray(new String[args.size()])));
2119        }
2120        catch (OozieClientException ex) {
2121            throw new OozieCLIException(ex.toString(), ex);
2122        }
2123    }
2124
2125    private void infoCommand(CommandLine commandLine) throws OozieCLIException {
2126        for (Option option : commandLine.getOptions()) {
2127            String opt = option.getOpt();
2128            if (opt.equals(INFO_TIME_ZONES_OPTION)) {
2129                printAvailableTimeZones();
2130            }
2131        }
2132    }
2133
2134    private void printAvailableTimeZones() {
2135        System.out.println("The format is \"SHORT_NAME (ID)\"\nGive the ID to the -timezone argument");
2136        System.out.println("GMT offsets can also be used (e.g. GMT-07:00, GMT-0700, GMT+05:30, GMT+0530)");
2137        System.out.println("Available Time Zones:");
2138        for (String tzId : TimeZone.getAvailableIDs()) {
2139            // skip id's that are like "Etc/GMT+01:00" because their display names are like "GMT-01:00", which is confusing
2140            if (!tzId.startsWith("Etc/GMT")) {
2141                TimeZone tZone = TimeZone.getTimeZone(tzId);
2142                System.out.println("      " + tZone.getDisplayName(false, TimeZone.SHORT) + " (" + tzId + ")");
2143            }
2144        }
2145    }
2146
2147
2148    private void mrCommand(CommandLine commandLine) throws IOException, OozieCLIException {
2149        try {
2150            XOozieClient wc = createXOozieClient(commandLine);
2151            Properties conf = getConfiguration(wc, commandLine);
2152
2153            String mapper = conf.getProperty(MAPRED_MAPPER, conf.getProperty(MAPRED_MAPPER_2));
2154            if (mapper == null) {
2155                throw new OozieCLIException("mapper (" + MAPRED_MAPPER + " or " + MAPRED_MAPPER_2 + ") must be specified in conf");
2156            }
2157
2158            String reducer = conf.getProperty(MAPRED_REDUCER, conf.getProperty(MAPRED_REDUCER_2));
2159            if (reducer == null) {
2160                throw new OozieCLIException("reducer (" + MAPRED_REDUCER + " or " + MAPRED_REDUCER_2
2161                        + ") must be specified in conf");
2162            }
2163
2164            String inputDir = conf.getProperty(MAPRED_INPUT);
2165            if (inputDir == null) {
2166                throw new OozieCLIException("input dir (" + MAPRED_INPUT +") must be specified in conf");
2167            }
2168
2169            String outputDir = conf.getProperty(MAPRED_OUTPUT);
2170            if (outputDir == null) {
2171                throw new OozieCLIException("output dir (" + MAPRED_OUTPUT +") must be specified in conf");
2172            }
2173
2174            System.out.println(JOB_ID_PREFIX + wc.submitMapReduce(conf));
2175        }
2176        catch (OozieClientException ex) {
2177            throw new OozieCLIException(ex.toString(), ex);
2178        }
2179    }
2180
2181    private String getFirstMissingDependencies(CoordinatorAction action) {
2182        StringBuilder allDeps = new StringBuilder();
2183        String missingDep = action.getMissingDependencies();
2184        boolean depExists = false;
2185        if (missingDep != null && !missingDep.isEmpty()) {
2186            allDeps.append(missingDep.split(INSTANCE_SEPARATOR)[0]);
2187            depExists = true;
2188        }
2189        String pushDeps = action.getPushMissingDependencies();
2190        if (pushDeps != null && !pushDeps.isEmpty()) {
2191            if(depExists) {
2192                allDeps.append(INSTANCE_SEPARATOR);
2193            }
2194            allDeps.append(pushDeps.split(INSTANCE_SEPARATOR)[0]);
2195        }
2196        return allDeps.toString();
2197    }
2198
2199    private void printMetrics(OozieClient.Metrics metrics) {
2200        System.out.println("COUNTERS");
2201        System.out.println("--------");
2202        Map<String, Long> counters = new TreeMap<String, Long>(metrics.getCounters());
2203        for (Map.Entry<String, Long> ent : counters.entrySet()) {
2204            System.out.println(ent.getKey() + " : " + ent.getValue());
2205        }
2206        System.out.println("\nGAUGES");
2207        System.out.println("------");
2208        Map<String, Object> gauges = new TreeMap<String, Object>(metrics.getGauges());
2209        for (Map.Entry<String, Object> ent : gauges.entrySet()) {
2210            System.out.println(ent.getKey() + " : " + ent.getValue());
2211        }
2212        System.out.println("\nTIMERS");
2213        System.out.println("------");
2214        Map<String, OozieClient.Metrics.Timer> timers = new TreeMap<String, OozieClient.Metrics.Timer>(metrics.getTimers());
2215        for (Map.Entry<String, OozieClient.Metrics.Timer> ent : timers.entrySet()) {
2216            System.out.println(ent.getKey());
2217            System.out.println(ent.getValue());
2218        }
2219        System.out.println("\nHISTOGRAMS");
2220        System.out.println("----------");
2221        Map<String, OozieClient.Metrics.Histogram> histograms =
2222                new TreeMap<String, OozieClient.Metrics.Histogram>(metrics.getHistograms());
2223        for (Map.Entry<String, OozieClient.Metrics.Histogram> ent : histograms.entrySet()) {
2224            System.out.println(ent.getKey());
2225            System.out.println(ent.getValue());
2226        }
2227    }
2228
2229    private void printInstrumentation(OozieClient.Instrumentation instrumentation) {
2230        System.out.println("COUNTERS");
2231        System.out.println("--------");
2232        Map<String, Long> counters = new TreeMap<String, Long>(instrumentation.getCounters());
2233        for (Map.Entry<String, Long> ent : counters.entrySet()) {
2234            System.out.println(ent.getKey() + " : " + ent.getValue());
2235        }
2236        System.out.println("\nVARIABLES");
2237        System.out.println("---------");
2238        Map<String, Object> variables = new TreeMap<String, Object>(instrumentation.getVariables());
2239        for (Map.Entry<String, Object> ent : variables.entrySet()) {
2240            System.out.println(ent.getKey() + " : " + ent.getValue());
2241        }
2242        System.out.println("\nSAMPLERS");
2243        System.out.println("---------");
2244        Map<String, Double> samplers = new TreeMap<String, Double>(instrumentation.getSamplers());
2245        for (Map.Entry<String, Double> ent : samplers.entrySet()) {
2246            System.out.println(ent.getKey() + " : " + ent.getValue());
2247        }
2248        System.out.println("\nTIMERS");
2249        System.out.println("---------");
2250        Map<String, OozieClient.Instrumentation.Timer> timers =
2251                new TreeMap<String, OozieClient.Instrumentation.Timer>(instrumentation.getTimers());
2252        for (Map.Entry<String, OozieClient.Instrumentation.Timer> ent : timers.entrySet()) {
2253            System.out.println(ent.getKey());
2254            System.out.println(ent.getValue());
2255        }
2256    }
2257}