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