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