001    /**
002     * Copyright (c) 2010 Yahoo! Inc. All rights reserved.
003     * Licensed under the Apache License, Version 2.0 (the "License");
004     * you may not use this file except in compliance with the License.
005     * You may obtain a copy of the License at
006     *
007     *   http://www.apache.org/licenses/LICENSE-2.0
008     *
009     *  Unless required by applicable law or agreed to in writing, software
010     *  distributed under the License is distributed on an "AS IS" BASIS,
011     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012     *  See the License for the specific language governing permissions and
013     *  limitations under the License. See accompanying LICENSE file.
014     */
015    package org.apache.oozie.cli;
016    
017    import java.io.File;
018    import java.io.FileInputStream;
019    import java.io.FileReader;
020    import java.io.IOException;
021    import java.io.InputStream;
022    import java.text.SimpleDateFormat;
023    import java.util.ArrayList;
024    import java.util.Date;
025    import java.util.List;
026    import java.util.Locale;
027    import java.util.Map;
028    import java.util.Properties;
029    import java.util.TimeZone;
030    
031    import javax.xml.XMLConstants;
032    import javax.xml.parsers.DocumentBuilder;
033    import javax.xml.parsers.DocumentBuilderFactory;
034    import javax.xml.parsers.ParserConfigurationException;
035    import javax.xml.transform.stream.StreamSource;
036    import javax.xml.validation.Schema;
037    import javax.xml.validation.SchemaFactory;
038    import javax.xml.validation.Validator;
039    
040    import org.apache.commons.cli.CommandLine;
041    import org.apache.commons.cli.Option;
042    import org.apache.commons.cli.OptionBuilder;
043    import org.apache.commons.cli.OptionGroup;
044    import org.apache.commons.cli.Options;
045    import org.apache.commons.cli.ParseException;
046    import org.apache.oozie.BuildInfo;
047    import org.apache.oozie.client.BundleJob;
048    import org.apache.oozie.client.CoordinatorAction;
049    import org.apache.oozie.client.CoordinatorJob;
050    import org.apache.oozie.client.OozieClient;
051    import org.apache.oozie.client.OozieClientException;
052    import org.apache.oozie.client.WorkflowAction;
053    import org.apache.oozie.client.WorkflowJob;
054    import org.apache.oozie.client.XOozieClient;
055    import org.apache.oozie.client.OozieClient.SYSTEM_MODE;
056    import org.apache.oozie.client.rest.RestConstants;
057    import org.w3c.dom.DOMException;
058    import org.w3c.dom.Document;
059    import org.w3c.dom.Element;
060    import org.w3c.dom.Node;
061    import org.w3c.dom.NodeList;
062    import org.w3c.dom.Text;
063    import org.xml.sax.SAXException;
064    
065    /**
066     * Oozie command line utility.
067     */
068    public class OozieCLI {
069        public static final String ENV_OOZIE_URL = "OOZIE_URL";
070        public static final String ENV_OOZIE_DEBUG = "OOZIE_DEBUG";
071        public static final String WS_HEADER_PREFIX = "header:";
072    
073        public static final String HELP_CMD = "help";
074        public static final String VERSION_CMD = "version";
075        public static final String JOB_CMD = "job";
076        public static final String JOBS_CMD = "jobs";
077        public static final String ADMIN_CMD = "admin";
078        public static final String VALIDATE_CMD = "validate";
079        public static final String SLA_CMD = "sla";
080        public static final String PIG_CMD = "pig";
081    
082        public static final String OOZIE_OPTION = "oozie";
083        public static final String CONFIG_OPTION = "config";
084        public static final String SUBMIT_OPTION = "submit";
085        public static final String OFFSET_OPTION = "offset";
086        public static final String START_OPTION = "start";
087        public static final String RUN_OPTION = "run";
088        public static final String DRYRUN_OPTION = "dryrun";
089        public static final String SUSPEND_OPTION = "suspend";
090        public static final String RESUME_OPTION = "resume";
091        public static final String KILL_OPTION = "kill";
092        public static final String CHANGE_OPTION = "change";
093        public static final String CHANGE_VALUE_OPTION = "value";
094        public static final String RERUN_OPTION = "rerun";
095        public static final String INFO_OPTION = "info";
096        public static final String LOG_OPTION = "log";
097        public static final String DEFINITION_OPTION = "definition";
098        public static final String CONFIG_CONTENT_OPTION = "configcontent";
099    
100        public static final String LEN_OPTION = "len";
101        public static final String FILTER_OPTION = "filter";
102        public static final String JOBTYPE_OPTION = "jobtype";
103        public static final String SYSTEM_MODE_OPTION = "systemmode";
104        public static final String VERSION_OPTION = "version";
105        public static final String STATUS_OPTION = "status";
106        public static final String LOCAL_TIME_OPTION = "localtime";
107        public static final String QUEUE_DUMP_OPTION = "queuedump";
108        public static final String RERUN_ACTION_OPTION = "action";
109        public static final String RERUN_COORD_OPTION = "coordinator";
110        public static final String RERUN_DATE_OPTION = "date";
111        public static final String RERUN_REFRESH_OPTION = "refresh";
112        public static final String RERUN_NOCLEANUP_OPTION = "nocleanup";
113    
114        public static final String VERBOSE_OPTION = "verbose";
115        public static final String VERBOSE_DELIMITER = "\t";
116    
117        public static final String PIGFILE_OPTION = "file";
118    
119        private static final String[] OOZIE_HELP = {
120                "the env variable '" + ENV_OOZIE_URL + "' is used as default value for the '-" + OOZIE_OPTION + "' option",
121                "custom headers for Oozie web services can be specified using '-D" + WS_HEADER_PREFIX + "NAME=VALUE'" };
122    
123        private static final String RULER;
124        private static final int LINE_WIDTH = 132;
125    
126        private boolean used;
127    
128        static {
129            StringBuilder sb = new StringBuilder();
130            for (int i = 0; i < LINE_WIDTH; i++) {
131                sb.append("-");
132            }
133            RULER = sb.toString();
134        }
135    
136        /**
137         * Entry point for the Oozie CLI when invoked from the command line.
138         * <p/>
139         * Upon completion this method exits the JVM with '0' (success) or '-1' (failure).
140         *
141         * @param args options and arguments for the Oozie CLI.
142         */
143        public static void main(String[] args) {
144            System.exit(new OozieCLI().run(args));
145        }
146    
147        /**
148         * Create an Oozie CLI instance.
149         */
150        public OozieCLI() {
151            used = false;
152        }
153    
154        /**
155         * Return Oozie CLI top help lines.
156         *
157         * @return help lines.
158         */
159        protected String[] getCLIHelp() {
160            return OOZIE_HELP;
161        }
162    
163        protected Options createAdminOptions() {
164            Option oozie = new Option(OOZIE_OPTION, true, "Oozie URL");
165            Option system_mode = new Option(SYSTEM_MODE_OPTION, true,
166                    "Supported in Oozie-2.0 or later versions ONLY. Change oozie system mode [NORMAL|NOWEBSERVICE|SAFEMODE]");
167            Option status = new Option(STATUS_OPTION, false, "show the current system status");
168            Option version = new Option(VERSION_OPTION, false, "show Oozie server build version");
169            Option queuedump = new Option(QUEUE_DUMP_OPTION, false, "show Oozie server queue elements");
170            Options adminOptions = new Options();
171            adminOptions.addOption(oozie);
172            OptionGroup group = new OptionGroup();
173            group.addOption(system_mode);
174            group.addOption(status);
175            group.addOption(version);
176            group.addOption(queuedump);
177            adminOptions.addOptionGroup(group);
178            return adminOptions;
179        }
180    
181        protected Options createJobOptions() {
182            Option oozie = new Option(OOZIE_OPTION, true, "Oozie URL");
183            Option config = new Option(CONFIG_OPTION, true, "job configuration file '.xml' or '.properties'");
184            Option submit = new Option(SUBMIT_OPTION, false, "submit a job");
185            Option run = new Option(RUN_OPTION, false, "run a job");
186            Option rerun = new Option(RERUN_OPTION, true,
187                    "rerun a job  (coordinator requires -action or -date, bundle requires -coordinator or -date)");
188            Option dryrun = new Option(DRYRUN_OPTION, false,
189                    "Supported in Oozie-2.0 or later versions ONLY - dryrun or test run a coordinator job, job is not queued");
190            Option start = new Option(START_OPTION, true, "start a job");
191            Option suspend = new Option(SUSPEND_OPTION, true, "suspend a job");
192            Option resume = new Option(RESUME_OPTION, true, "resume a job");
193            Option kill = new Option(KILL_OPTION, true, "kill a job");
194            Option change = new Option(CHANGE_OPTION, true, "change a coordinator job");
195            Option changeValue = new Option(CHANGE_VALUE_OPTION, true,
196                    "new endtime/concurrency/pausetime value for changing a coordinator job");
197            Option info = new Option(INFO_OPTION, true, "info of a job");
198            Option offset = new Option(OFFSET_OPTION, true, "job info offset of actions (default '1', requires -info)");
199            Option len = new Option(LEN_OPTION, true, "number of actions (default TOTAL ACTIONS, requires -info)");
200            Option localtime = new Option(LOCAL_TIME_OPTION, false, "use local time (default GMT)");
201            Option log = new Option(LOG_OPTION, true, "job log");
202            Option definition = new Option(DEFINITION_OPTION, true, "job definition");
203            Option config_content = new Option(CONFIG_CONTENT_OPTION, true, "job configuration");
204            Option verbose = new Option(VERBOSE_OPTION, false, "verbose mode");
205            Option rerun_action = new Option(RERUN_ACTION_OPTION, true, "coordinator rerun on action ids (requires -rerun)");
206            Option rerun_date = new Option(RERUN_DATE_OPTION, true,
207                    "coordinator/bundle rerun on action dates (requires -rerun)");
208            Option rerun_coord = new Option(RERUN_COORD_OPTION, true, "bundle rerun on coordinator names (requires -rerun)");
209            Option rerun_refresh = new Option(RERUN_REFRESH_OPTION, false,
210                    "re-materialize the coordinator rerun actions (requires -rerun)");
211            Option rerun_nocleanup = new Option(RERUN_NOCLEANUP_OPTION, false,
212                    "do not clean up output-events of the coordiantor rerun actions (requires -rerun)");
213            Option property = OptionBuilder.withArgName("property=value").hasArgs(2).withValueSeparator().withDescription(
214                    "set/override value for given property").create("D");
215    
216            OptionGroup actions = new OptionGroup();
217            actions.addOption(submit);
218            actions.addOption(start);
219            actions.addOption(run);
220            actions.addOption(dryrun);
221            actions.addOption(suspend);
222            actions.addOption(resume);
223            actions.addOption(kill);
224            actions.addOption(change);
225            actions.addOption(info);
226            actions.addOption(rerun);
227            actions.addOption(log);
228            actions.addOption(definition);
229            actions.addOption(config_content);
230            actions.setRequired(true);
231            Options jobOptions = new Options();
232            jobOptions.addOption(oozie);
233            jobOptions.addOption(config);
234            jobOptions.addOption(property);
235            jobOptions.addOption(changeValue);
236            jobOptions.addOption(localtime);
237            jobOptions.addOption(verbose);
238            jobOptions.addOption(offset);
239            jobOptions.addOption(len);
240            jobOptions.addOption(rerun_action);
241            jobOptions.addOption(rerun_date);
242            jobOptions.addOption(rerun_coord);
243            jobOptions.addOption(rerun_refresh);
244            jobOptions.addOption(rerun_nocleanup);
245            jobOptions.addOptionGroup(actions);
246            return jobOptions;
247        }
248    
249        protected Options createJobsOptions() {
250            Option oozie = new Option(OOZIE_OPTION, true, "Oozie URL");
251            Option start = new Option(OFFSET_OPTION, true, "jobs offset (default '1')");
252            Option jobtype = new Option(JOBTYPE_OPTION, true,
253                    "job type ('Supported in Oozie-2.0 or later versions ONLY - 'coordinator' or 'bundle' or 'wf'(default))");
254            Option len = new Option(LEN_OPTION, true, "number of jobs (default '100')");
255            Option filter = new Option(FILTER_OPTION, true, "user=<U>;name=<N>;group=<G>;status=<S>;...");
256            Option localtime = new Option(LOCAL_TIME_OPTION, false, "use local time (default GMT)");
257            Option verbose = new Option(VERBOSE_OPTION, false, "verbose mode");
258            start.setType(Integer.class);
259            len.setType(Integer.class);
260            Options jobsOptions = new Options();
261            jobsOptions.addOption(oozie);
262            jobsOptions.addOption(localtime);
263            jobsOptions.addOption(start);
264            jobsOptions.addOption(len);
265            jobsOptions.addOption(oozie);
266            jobsOptions.addOption(filter);
267            jobsOptions.addOption(jobtype);
268            jobsOptions.addOption(verbose);
269            return jobsOptions;
270        }
271    
272        protected Options createSlaOptions() {
273            Option oozie = new Option(OOZIE_OPTION, true, "Oozie URL");
274            Option start = new Option(OFFSET_OPTION, true, "start offset (default '0')");
275            Option len = new Option(LEN_OPTION, true, "number of results (default '100')");
276            start.setType(Integer.class);
277            len.setType(Integer.class);
278            Options slaOptions = new Options();
279            slaOptions.addOption(start);
280            slaOptions.addOption(len);
281            slaOptions.addOption(oozie);
282            return slaOptions;
283        }
284    
285        protected Options createPigOptions() {
286            Option oozie = new Option(OOZIE_OPTION, true, "Oozie URL");
287            Option config = new Option(CONFIG_OPTION, true, "job configuration file '.properties'");
288            Option pigFile = new Option(PIGFILE_OPTION, true, "Pig script");
289            Option property = OptionBuilder.withArgName("property=value").hasArgs(2).withValueSeparator().withDescription(
290                    "set/override value for given property").create("D");
291            Options pigOptions = new Options();
292            pigOptions.addOption(oozie);
293            pigOptions.addOption(config);
294            pigOptions.addOption(property);
295            pigOptions.addOption(pigFile);
296            return pigOptions;
297        }
298    
299        /**
300         * Run a CLI programmatically.
301         * <p/>
302         * It does not exit the JVM.
303         * <p/>
304         * A CLI instance can be used only once.
305         *
306         * @param args options and arguments for the Oozie CLI.
307         * @return '0' (success), '-1' (failure).
308         */
309        public synchronized int run(String[] args) {
310            if (used) {
311                throw new IllegalStateException("CLI instance already used");
312            }
313            used = true;
314    
315            CLIParser parser = new CLIParser(OOZIE_OPTION, getCLIHelp());
316            parser.addCommand(HELP_CMD, "", "display usage", new Options(), false);
317            parser.addCommand(VERSION_CMD, "", "show client version", new Options(), false);
318            parser.addCommand(JOB_CMD, "", "job operations", createJobOptions(), false);
319            parser.addCommand(JOBS_CMD, "", "jobs status", createJobsOptions(), false);
320            parser.addCommand(ADMIN_CMD, "", "admin operations", createAdminOptions(), false);
321            parser.addCommand(VALIDATE_CMD, "", "validate a workflow XML file", new Options(), true);
322            parser.addCommand(SLA_CMD, "", "sla operations (Supported in Oozie-2.0 or later)", createSlaOptions(), false);
323            parser.addCommand(PIG_CMD, "-X ", "submit a pig job, everything after '-X' are pass-through parameters to pig",
324                    createPigOptions(), true);
325    
326            try {
327                CLIParser.Command command = parser.parse(args);
328                if (command.getName().equals(HELP_CMD)) {
329                    parser.showHelp();
330                }
331                else if (command.getName().equals(JOB_CMD)) {
332                    jobCommand(command.getCommandLine());
333                }
334                else if (command.getName().equals(JOBS_CMD)) {
335                    jobsCommand(command.getCommandLine());
336                }
337                else if (command.getName().equals(ADMIN_CMD)) {
338                    adminCommand(command.getCommandLine());
339                }
340                else if (command.getName().equals(VERSION_CMD)) {
341                    versionCommand();
342                }
343                else if (command.getName().equals(VALIDATE_CMD)) {
344                    validateCommand(command.getCommandLine());
345                }
346                else if (command.getName().equals(SLA_CMD)) {
347                    slaCommand(command.getCommandLine());
348                }
349                else if (command.getName().equals(PIG_CMD)) {
350                    pigCommand(command.getCommandLine());
351                }
352    
353                return 0;
354            }
355            catch (OozieCLIException ex) {
356                System.err.println("Error: " + ex.getMessage());
357                return -1;
358            }
359            catch (ParseException ex) {
360                System.err.println("Invalid sub-command: " + ex.getMessage());
361                System.err.println();
362                System.err.println(parser.shortHelp());
363                return -1;
364            }
365            catch (Exception ex) {
366                ex.printStackTrace();
367                System.err.println(ex.getMessage());
368                return -1;
369            }
370        }
371    
372        protected String getOozieUrl(CommandLine commandLine) {
373            String url = commandLine.getOptionValue(OOZIE_OPTION);
374            if (url == null) {
375                url = System.getenv(ENV_OOZIE_URL);
376                if (url == null) {
377                    throw new IllegalArgumentException(
378                            "Oozie URL is not available neither in command option or in the environment");
379                }
380            }
381            return url;
382        }
383    
384        // Canibalized from Hadoop <code>Configuration.loadResource()</code>.
385        private Properties parse(InputStream is, Properties conf) throws IOException {
386            try {
387                DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
388                // ignore all comments inside the xml file
389                docBuilderFactory.setIgnoringComments(true);
390                DocumentBuilder builder = docBuilderFactory.newDocumentBuilder();
391                Document doc = builder.parse(is);
392                return parseDocument(doc, conf);
393            }
394            catch (SAXException e) {
395                throw new IOException(e);
396            }
397            catch (ParserConfigurationException e) {
398                throw new IOException(e);
399            }
400        }
401    
402        // Canibalized from Hadoop <code>Configuration.loadResource()</code>.
403        private Properties parseDocument(Document doc, Properties conf) throws IOException {
404            try {
405                Element root = doc.getDocumentElement();
406                if (!"configuration".equals(root.getTagName())) {
407                    throw new RuntimeException("bad conf file: top-level element not <configuration>");
408                }
409                NodeList props = root.getChildNodes();
410                for (int i = 0; i < props.getLength(); i++) {
411                    Node propNode = props.item(i);
412                    if (!(propNode instanceof Element)) {
413                        continue;
414                    }
415                    Element prop = (Element) propNode;
416                    if (!"property".equals(prop.getTagName())) {
417                        throw new RuntimeException("bad conf file: element not <property>");
418                    }
419                    NodeList fields = prop.getChildNodes();
420                    String attr = null;
421                    String value = null;
422                    for (int j = 0; j < fields.getLength(); j++) {
423                        Node fieldNode = fields.item(j);
424                        if (!(fieldNode instanceof Element)) {
425                            continue;
426                        }
427                        Element field = (Element) fieldNode;
428                        if ("name".equals(field.getTagName()) && field.hasChildNodes()) {
429                            attr = ((Text) field.getFirstChild()).getData();
430                        }
431                        if ("value".equals(field.getTagName()) && field.hasChildNodes()) {
432                            value = ((Text) field.getFirstChild()).getData();
433                        }
434                    }
435    
436                    if (attr != null && value != null) {
437                        conf.setProperty(attr, value);
438                    }
439                }
440                return conf;
441            }
442            catch (DOMException e) {
443                throw new IOException(e);
444            }
445        }
446    
447        private Properties getConfiguration(CommandLine commandLine) throws IOException {
448            Properties conf = new Properties();
449            conf.setProperty("user.name", System.getProperty("user.name"));
450            String configFile = commandLine.getOptionValue(CONFIG_OPTION);
451            if (configFile == null) {
452                throw new IOException("configuration file not specified");
453            }
454            else {
455                File file = new File(configFile);
456                if (!file.exists()) {
457                    throw new IOException("configuration file [" + configFile + "] not found");
458                }
459                if (configFile.endsWith(".properties")) {
460                    conf.load(new FileReader(file));
461                }
462                else if (configFile.endsWith(".xml")) {
463                    parse(new FileInputStream(configFile), conf);
464                }
465                else {
466                    throw new IllegalArgumentException("configuration must be a '.properties' or a '.xml' file");
467                }
468            }
469            if (commandLine.hasOption("D")) {
470                Properties commandLineProperties = commandLine.getOptionProperties("D");
471                conf.putAll(commandLineProperties);
472            }
473            return conf;
474        }
475    
476        /**
477         * @param commandLine command line string.
478         * @return change value specified by -value.
479         * @throws OozieCLIException
480         */
481        private String getChangeValue(CommandLine commandLine) throws OozieCLIException {
482            String changeValue = commandLine.getOptionValue(CHANGE_VALUE_OPTION);
483    
484            if (changeValue == null) {
485                throw new OozieCLIException("-value option needs to be specified for -change option");
486            }
487    
488            return changeValue;
489        }
490    
491        private void addHeader(OozieClient wc) {
492            for (Map.Entry entry : System.getProperties().entrySet()) {
493                String key = (String) entry.getKey();
494                if (key.startsWith(WS_HEADER_PREFIX)) {
495                    String header = key.substring(WS_HEADER_PREFIX.length());
496                    wc.setHeader(header, (String) entry.getValue());
497                }
498            }
499        }
500    
501        /**
502         * Create a OozieClient.
503         * <p/>
504         * It injects any '-Dheader:' as header to the the {@link org.apache.oozie.client.OozieClient}.
505         *
506         * @param commandLine the parsed command line options.
507         * @return a pre configured eXtended workflow client.
508         * @throws OozieCLIException thrown if the OozieClient could not be configured.
509         */
510        protected OozieClient createOozieClient(CommandLine commandLine) throws OozieCLIException {
511            OozieClient wc = new OozieClient(getOozieUrl(commandLine));
512            addHeader(wc);
513            setDebugMode(wc);
514            return wc;
515        }
516    
517        /**
518         * Create a XOozieClient.
519         * <p/>
520         * It injects any '-Dheader:' as header to the the {@link org.apache.oozie.client.OozieClient}.
521         *
522         * @param commandLine the parsed command line options.
523         * @return a pre configured eXtended workflow client.
524         * @throws OozieCLIException thrown if the XOozieClient could not be configured.
525         */
526        protected XOozieClient createXOozieClient(CommandLine commandLine) throws OozieCLIException {
527            XOozieClient wc = new XOozieClient(getOozieUrl(commandLine));
528            addHeader(wc);
529            setDebugMode(wc);
530            return wc;
531        }
532    
533        protected void setDebugMode(OozieClient wc) {
534            String debug = System.getenv(ENV_OOZIE_DEBUG);
535            if (debug != null && !debug.isEmpty()) {
536                int debugVal = 0;
537                try {
538                    debugVal = Integer.parseInt(debug.trim());
539                }
540                catch (Exception ex) {
541                    System.out.println("Unable to parse the debug settings. May be not an integer [" + debug + "]");
542                    ex.printStackTrace();
543                }
544                wc.setDebugMode(debugVal);
545            }
546        }
547    
548        private static String JOB_ID_PREFIX = "job: ";
549    
550        private void jobCommand(CommandLine commandLine) throws IOException, OozieCLIException {
551            XOozieClient wc = createXOozieClient(commandLine);
552    
553            List<String> options = new ArrayList<String>();
554            for (Option option : commandLine.getOptions()) {
555                options.add(option.getOpt());
556            }
557    
558            try {
559                if (options.contains(SUBMIT_OPTION)) {
560                    System.out.println(JOB_ID_PREFIX + wc.submit(getConfiguration(commandLine)));
561                }
562                else if (options.contains(START_OPTION)) {
563                    wc.start(commandLine.getOptionValue(START_OPTION));
564                }
565                else if (options.contains(DRYRUN_OPTION)) {
566                    String[] dryrunStr = wc.dryrun(getConfiguration(commandLine)).split("action for new instance");
567                    int arraysize = dryrunStr.length;
568                    System.out.println("***coordJob after parsing: ***");
569                    System.out.println(dryrunStr[0]);
570                    int aLen = dryrunStr.length - 1;
571                    if (aLen < 0) {
572                        aLen = 0;
573                    }
574                    System.out.println("***total coord actions is " + aLen + " ***");
575                    for (int i = 1; i <= arraysize - 1; i++) {
576                        System.out.println(RULER);
577                        System.out.println("coordAction instance: " + i + ":");
578                        System.out.println(dryrunStr[i]);
579                    }
580                }
581                else if (options.contains(SUSPEND_OPTION)) {
582                    wc.suspend(commandLine.getOptionValue(SUSPEND_OPTION));
583                }
584                else if (options.contains(RESUME_OPTION)) {
585                    wc.resume(commandLine.getOptionValue(RESUME_OPTION));
586                }
587                else if (options.contains(KILL_OPTION)) {
588                    wc.kill(commandLine.getOptionValue(KILL_OPTION));
589                }
590                else if (options.contains(CHANGE_OPTION)) {
591                    wc.change(commandLine.getOptionValue(CHANGE_OPTION), getChangeValue(commandLine));
592                }
593                else if (options.contains(RUN_OPTION)) {
594                    System.out.println(JOB_ID_PREFIX + wc.run(getConfiguration(commandLine)));
595                }
596                else if (options.contains(RERUN_OPTION)) {
597                    if (commandLine.getOptionValue(RERUN_OPTION).contains("-W")) {
598                        wc.reRun(commandLine.getOptionValue(RERUN_OPTION), getConfiguration(commandLine));
599                    }
600                    else if (commandLine.getOptionValue(RERUN_OPTION).contains("-B")) {
601                        String bundleJobId = commandLine.getOptionValue(RERUN_OPTION);
602                        String coordScope = null;
603                        String dateScope = null;
604                        boolean refresh = false;
605                        boolean noCleanup = false;
606                        if (options.contains(RERUN_ACTION_OPTION)) {
607                            throw new OozieCLIException("Invalid options provided for bundle rerun. " + RERUN_ACTION_OPTION
608                                    + " is not valid for bundle rerun");
609                        }
610                        if (options.contains(RERUN_DATE_OPTION)) {
611                            dateScope = commandLine.getOptionValue(RERUN_DATE_OPTION);
612                        }
613    
614                        if (options.contains(RERUN_COORD_OPTION)) {
615                            coordScope = commandLine.getOptionValue(RERUN_COORD_OPTION);
616                        }
617    
618                        if (options.contains(RERUN_REFRESH_OPTION)) {
619                            refresh = true;
620                        }
621                        if (options.contains(RERUN_NOCLEANUP_OPTION)) {
622                            noCleanup = true;
623                        }
624                        wc.reRunBundle(bundleJobId, coordScope, dateScope, refresh, noCleanup);
625                        if (coordScope != null && !coordScope.isEmpty()) {
626                            System.out.println("Coordinators [" + coordScope + "] of bundle " + bundleJobId
627                                    + " are scheduled to rerun on date ranges [" + dateScope + "].");
628                        }
629                        else {
630                            System.out.println("All coordinators of bundle " + bundleJobId
631                                    + " are scheduled to rerun on the date ranges [" + dateScope + "].");
632                        }
633                    }
634                    else {
635                        String coordJobId = commandLine.getOptionValue(RERUN_OPTION);
636                        String scope = null;
637                        String rerunType = null;
638                        boolean refresh = false;
639                        boolean noCleanup = false;
640                        if (options.contains(RERUN_DATE_OPTION) && options.contains(RERUN_ACTION_OPTION)) {
641                            throw new OozieCLIException("Invalid options provided for rerun: either" + RERUN_DATE_OPTION
642                                    + " or " + RERUN_ACTION_OPTION + " expected. Don't use both at the same time.");
643                        }
644                        if (options.contains(RERUN_DATE_OPTION)) {
645                            rerunType = RestConstants.JOB_COORD_RERUN_DATE;
646                            scope = commandLine.getOptionValue(RERUN_DATE_OPTION);
647                        }
648                        else if (options.contains(RERUN_ACTION_OPTION)) {
649                            rerunType = RestConstants.JOB_COORD_RERUN_ACTION;
650                            scope = commandLine.getOptionValue(RERUN_ACTION_OPTION);
651                        }
652                        else {
653                            throw new OozieCLIException("Invalid options provided for rerun: " + RERUN_DATE_OPTION + " or "
654                                    + RERUN_ACTION_OPTION + " expected.");
655                        }
656                        if (options.contains(RERUN_REFRESH_OPTION)) {
657                            refresh = true;
658                        }
659                        if (options.contains(RERUN_NOCLEANUP_OPTION)) {
660                            noCleanup = true;
661                        }
662                        printRerunCoordActions(wc.reRunCoord(coordJobId, rerunType, scope, refresh, noCleanup));
663                    }
664                }
665                else if (options.contains(INFO_OPTION)) {
666                    if (commandLine.getOptionValue(INFO_OPTION).endsWith("-B")) {
667                        printBundleJob(wc.getBundleJobInfo(commandLine.getOptionValue(INFO_OPTION)), options
668                                .contains(LOCAL_TIME_OPTION), options.contains(VERBOSE_OPTION));
669                    }
670                    else if (commandLine.getOptionValue(INFO_OPTION).endsWith("-C")) {
671                        String s = commandLine.getOptionValue(OFFSET_OPTION);
672                        int start = Integer.parseInt((s != null) ? s : "0");
673                        s = commandLine.getOptionValue(LEN_OPTION);
674                        int len = Integer.parseInt((s != null) ? s : "0");
675                        printCoordJob(wc.getCoordJobInfo(commandLine.getOptionValue(INFO_OPTION), start, len), options
676                                .contains(LOCAL_TIME_OPTION), options.contains(VERBOSE_OPTION));
677                    }
678                    else if (commandLine.getOptionValue(INFO_OPTION).contains("-C@")) {
679                        printCoordAction(wc.getCoordActionInfo(commandLine.getOptionValue(INFO_OPTION)), options
680                                .contains(LOCAL_TIME_OPTION));
681                    }
682                    else if (commandLine.getOptionValue(INFO_OPTION).contains("-W@")) {
683                        printWorkflowAction(wc.getWorkflowActionInfo(commandLine.getOptionValue(INFO_OPTION)), options
684                                .contains(LOCAL_TIME_OPTION));
685                    }
686                    else {
687                        String s = commandLine.getOptionValue(OFFSET_OPTION);
688                        int start = Integer.parseInt((s != null) ? s : "0");
689                        s = commandLine.getOptionValue(LEN_OPTION);
690                        String jobtype = commandLine.getOptionValue(JOBTYPE_OPTION);
691                        jobtype = (jobtype != null) ? jobtype : "wf";
692                        int len = Integer.parseInt((s != null) ? s : "0");
693                        printJob(wc.getJobInfo(commandLine.getOptionValue(INFO_OPTION), start, len), options
694                                .contains(LOCAL_TIME_OPTION), options.contains(VERBOSE_OPTION));
695                    }
696                }
697                else if (options.contains(LOG_OPTION)) {
698                    System.out.println(wc.getJobLog(commandLine.getOptionValue(LOG_OPTION)));
699                }
700                else if (options.contains(DEFINITION_OPTION)) {
701                    System.out.println(wc.getJobDefinition(commandLine.getOptionValue(DEFINITION_OPTION)));
702                }
703                else if (options.contains(CONFIG_CONTENT_OPTION)) {
704                    if (commandLine.getOptionValue(CONFIG_CONTENT_OPTION).endsWith("-C")) {
705                        System.out.println(wc.getCoordJobInfo(commandLine.getOptionValue(CONFIG_CONTENT_OPTION)).getConf());
706                    }
707                    else if (commandLine.getOptionValue(CONFIG_CONTENT_OPTION).endsWith("-W")) {
708                        System.out.println(wc.getJobInfo(commandLine.getOptionValue(CONFIG_CONTENT_OPTION)).getConf());
709                    }
710                    else if (commandLine.getOptionValue(CONFIG_CONTENT_OPTION).endsWith("-B")) {
711                        System.out
712                                .println(wc.getBundleJobInfo(commandLine.getOptionValue(CONFIG_CONTENT_OPTION)).getConf());
713                    }
714                    else {
715                        System.out.println("ERROR:  job id [" + commandLine.getOptionValue(CONFIG_CONTENT_OPTION)
716                                + "] doesn't end with either C or W or B");
717                    }
718                }
719            }
720            catch (OozieClientException ex) {
721                throw new OozieCLIException(ex.toString(), ex);
722            }
723        }
724    
725        private void printCoordJob(CoordinatorJob coordJob, boolean localtime, boolean verbose) {
726            System.out.println("Job ID : " + coordJob.getId());
727    
728            System.out.println(RULER);
729    
730            List<CoordinatorAction> actions = coordJob.getActions();
731            System.out.println("Job Name : " + maskIfNull(coordJob.getAppName()));
732            System.out.println("App Path : " + maskIfNull(coordJob.getAppPath()));
733            System.out.println("Status   : " + coordJob.getStatus());
734            System.out.println(RULER);
735    
736            if (verbose) {
737                System.out.println("ID" + VERBOSE_DELIMITER + "Action Number" + VERBOSE_DELIMITER + "Console URL"
738                        + VERBOSE_DELIMITER + "Error Code" + VERBOSE_DELIMITER + "Error Message" + VERBOSE_DELIMITER
739                        + "External ID" + VERBOSE_DELIMITER + "External Status" + VERBOSE_DELIMITER + "Job ID"
740                        + VERBOSE_DELIMITER + "Tracker URI" + VERBOSE_DELIMITER + "Created" + VERBOSE_DELIMITER
741                        + "Nominal Time" + VERBOSE_DELIMITER + "Status" + VERBOSE_DELIMITER + "Last Modified"
742                        + VERBOSE_DELIMITER + "Missing Dependencies");
743                System.out.println(RULER);
744    
745                for (CoordinatorAction action : actions) {
746                    System.out.println(maskIfNull(action.getId()) + VERBOSE_DELIMITER + action.getActionNumber()
747                            + VERBOSE_DELIMITER + maskIfNull(action.getConsoleUrl()) + VERBOSE_DELIMITER
748                            + maskIfNull(action.getErrorCode()) + VERBOSE_DELIMITER + maskIfNull(action.getErrorMessage())
749                            + VERBOSE_DELIMITER + maskIfNull(action.getExternalId()) + VERBOSE_DELIMITER
750                            + maskIfNull(action.getExternalStatus()) + VERBOSE_DELIMITER + maskIfNull(action.getJobId())
751                            + VERBOSE_DELIMITER + maskIfNull(action.getTrackerUri()) + VERBOSE_DELIMITER
752                            + maskDate(action.getCreatedTime(), localtime) + VERBOSE_DELIMITER
753                            + maskDate(action.getNominalTime(), localtime) + action.getStatus() + VERBOSE_DELIMITER
754                            + maskDate(action.getLastModifiedTime(), localtime) + VERBOSE_DELIMITER
755                            + maskIfNull(action.getMissingDependencies()));
756    
757                    System.out.println(RULER);
758                }
759            }
760            else {
761                System.out.println(String.format(COORD_ACTION_FORMATTER, "ID", "Status", "Ext ID", "Err Code", "Created",
762                        "Nominal Time", "Last Mod"));
763    
764                for (CoordinatorAction action : actions) {
765                    System.out.println(String.format(COORD_ACTION_FORMATTER, maskIfNull(action.getId()),
766                            action.getStatus(), maskIfNull(action.getExternalId()), maskIfNull(action.getErrorCode()),
767                            maskDate(action.getCreatedTime(), localtime), maskDate(action.getNominalTime(), localtime),
768                            maskDate(action.getLastModifiedTime(), localtime)));
769    
770                    System.out.println(RULER);
771                }
772            }
773        }
774    
775        private void printBundleJob(BundleJob bundleJob, boolean localtime, boolean verbose) {
776            System.out.println("Job ID : " + bundleJob.getId());
777    
778            System.out.println(RULER);
779    
780            List<CoordinatorJob> coordinators = bundleJob.getCoordinators();
781            System.out.println("Job Name : " + maskIfNull(bundleJob.getAppName()));
782            System.out.println("App Path : " + maskIfNull(bundleJob.getAppPath()));
783            System.out.println("Status   : " + bundleJob.getStatus());
784            System.out.println("Kickoff time   : " + bundleJob.getKickoffTime());
785            System.out.println(RULER);
786    
787            System.out.println(String.format(BUNDLE_COORD_JOBS_FORMATTER, "Job ID", "Status", "Freq", "Unit", "Started",
788                    "Next Materialized"));
789            System.out.println(RULER);
790    
791            for (CoordinatorJob job : coordinators) {
792                System.out.println(String.format(BUNDLE_COORD_JOBS_FORMATTER, maskIfNull(job.getId()), job.getStatus(), job
793                        .getFrequency(), job.getTimeUnit(), maskDate(job.getStartTime(), localtime), maskDate(job
794                        .getNextMaterializedTime(), localtime)));
795    
796                System.out.println(RULER);
797            }
798        }
799    
800        private void printCoordAction(CoordinatorAction coordAction, boolean contains) {
801            System.out.println("ID : " + maskIfNull(coordAction.getId()));
802    
803            System.out.println(RULER);
804    
805            System.out.println("Action Number        : " + coordAction.getActionNumber());
806            System.out.println("Console URL          : " + maskIfNull(coordAction.getConsoleUrl()));
807            System.out.println("Error Code           : " + maskIfNull(coordAction.getErrorCode()));
808            System.out.println("Error Message        : " + maskIfNull(coordAction.getErrorMessage()));
809            System.out.println("External ID          : " + maskIfNull(coordAction.getExternalId()));
810            System.out.println("External Status      : " + maskIfNull(coordAction.getExternalStatus()));
811            System.out.println("Job ID               : " + maskIfNull(coordAction.getJobId()));
812            System.out.println("Tracker URI          : " + maskIfNull(coordAction.getTrackerUri()));
813            System.out.println("Created              : " + maskDate(coordAction.getCreatedTime(), contains));
814            System.out.println("Nominal Time         : " + maskDate(coordAction.getNominalTime(), contains));
815            System.out.println("Status               : " + coordAction.getStatus());
816            System.out.println("Last Modified        : " + maskDate(coordAction.getLastModifiedTime(), contains));
817            System.out.println("Missing Dependencies : " + maskIfNull(coordAction.getMissingDependencies()));
818    
819            System.out.println(RULER);
820        }
821    
822        private void printRerunCoordActions(List<CoordinatorAction> actions) {
823            if (actions != null && actions.size() > 0) {
824                System.out.println("Action ID" + VERBOSE_DELIMITER + "Nominal Time");
825                System.out.println(RULER);
826                for (CoordinatorAction action : actions) {
827                    System.out.println(maskIfNull(action.getId()) + VERBOSE_DELIMITER
828                            + maskDate(action.getNominalTime(), false));
829                }
830            }
831            else {
832                System.out.println("No Actions match your rerun criteria!");
833            }
834        }
835    
836        private void printWorkflowAction(WorkflowAction action, boolean contains) {
837            System.out.println("ID : " + maskIfNull(action.getId()));
838    
839            System.out.println(RULER);
840    
841            System.out.println("Console URL     : " + maskIfNull(action.getConsoleUrl()));
842            System.out.println("Error Code      : " + maskIfNull(action.getErrorCode()));
843            System.out.println("Error Message   : " + maskIfNull(action.getErrorMessage()));
844            System.out.println("External ID     : " + maskIfNull(action.getExternalId()));
845            System.out.println("External Status : " + maskIfNull(action.getExternalStatus()));
846            System.out.println("Name            : " + maskIfNull(action.getName()));
847            System.out.println("Retries         : " + action.getRetries());
848            System.out.println("Tracker URI     : " + maskIfNull(action.getTrackerUri()));
849            System.out.println("Type            : " + maskIfNull(action.getType()));
850            System.out.println("Started         : " + maskDate(action.getStartTime(), contains));
851            System.out.println("Status          : " + action.getStatus());
852            System.out.println("Ended           : " + maskDate(action.getEndTime(), contains));
853    
854            System.out.println(RULER);
855        }
856    
857        private static final String WORKFLOW_JOBS_FORMATTER = "%-41s%-13s%-10s%-10s%-10s%-24s%-24s";
858        private static final String COORD_JOBS_FORMATTER = "%-41s%-15s%-10s%-5s%-13s%-24s%-24s";
859        private static final String BUNDLE_JOBS_FORMATTER = "%-41s%-15s%-10s%-20s%-20s%-13s%-13s";
860        private static final String BUNDLE_COORD_JOBS_FORMATTER = "%-41s%-10s%-5s%-13s%-24s%-24s";
861    
862        private static final String WORKFLOW_ACTION_FORMATTER = "%-78s%-10s%-23s%-11s%-10s";
863        private static final String COORD_ACTION_FORMATTER = "%-41s%-10s%-37s%-10s%-17s%-17s";
864    
865        private void printJob(WorkflowJob job, boolean localtime, boolean verbose) throws IOException {
866            System.out.println("Job ID : " + maskIfNull(job.getId()));
867    
868            System.out.println(RULER);
869    
870            System.out.println("Workflow Name : " + maskIfNull(job.getAppName()));
871            System.out.println("App Path      : " + maskIfNull(job.getAppPath()));
872            System.out.println("Status        : " + job.getStatus());
873            System.out.println("Run           : " + job.getRun());
874            System.out.println("User          : " + maskIfNull(job.getUser()));
875            System.out.println("Group         : " + maskIfNull(job.getGroup()));
876            System.out.println("Created       : " + maskDate(job.getCreatedTime(), localtime));
877            System.out.println("Started       : " + maskDate(job.getStartTime(), localtime));
878            System.out.println("Last Modified : " + maskDate(job.getLastModifiedTime(), localtime));
879            System.out.println("Ended         : " + maskDate(job.getEndTime(), localtime));
880            System.out.println("CoordAction ID: " + maskIfNull(job.getParentId()));
881    
882            List<WorkflowAction> actions = job.getActions();
883    
884            if (actions != null && actions.size() > 0) {
885                System.out.println();
886                System.out.println("Actions");
887                System.out.println(RULER);
888    
889                if (verbose) {
890                    System.out.println("ID" + VERBOSE_DELIMITER + "Console URL" + VERBOSE_DELIMITER + "Error Code"
891                            + VERBOSE_DELIMITER + "Error Message" + VERBOSE_DELIMITER + "External ID" + VERBOSE_DELIMITER
892                            + "External Status" + VERBOSE_DELIMITER + "Name" + VERBOSE_DELIMITER + "Retries"
893                            + VERBOSE_DELIMITER + "Tracker URI" + VERBOSE_DELIMITER + "Type" + VERBOSE_DELIMITER
894                            + "Started" + VERBOSE_DELIMITER + "Status" + VERBOSE_DELIMITER + "Ended");
895                    System.out.println(RULER);
896    
897                    for (WorkflowAction action : job.getActions()) {
898                        System.out.println(maskIfNull(action.getId()) + VERBOSE_DELIMITER
899                                + maskIfNull(action.getConsoleUrl()) + VERBOSE_DELIMITER
900                                + maskIfNull(action.getErrorCode()) + VERBOSE_DELIMITER
901                                + maskIfNull(action.getErrorMessage()) + VERBOSE_DELIMITER
902                                + maskIfNull(action.getExternalId()) + VERBOSE_DELIMITER
903                                + maskIfNull(action.getExternalStatus()) + VERBOSE_DELIMITER + maskIfNull(action.getName())
904                                + VERBOSE_DELIMITER + action.getRetries() + VERBOSE_DELIMITER
905                                + maskIfNull(action.getTrackerUri()) + VERBOSE_DELIMITER + maskIfNull(action.getType())
906                                + VERBOSE_DELIMITER + maskDate(action.getStartTime(), localtime) + VERBOSE_DELIMITER
907                                + action.getStatus() + VERBOSE_DELIMITER + maskDate(action.getEndTime(), localtime));
908    
909                        System.out.println(RULER);
910                    }
911                }
912                else {
913                    System.out.println(String.format(WORKFLOW_ACTION_FORMATTER, "ID", "Status", "Ext ID", "Ext Status",
914                            "Err Code"));
915    
916                    System.out.println(RULER);
917    
918                    for (WorkflowAction action : job.getActions()) {
919                        System.out.println(String.format(WORKFLOW_ACTION_FORMATTER, maskIfNull(action.getId()), action
920                                .getStatus(), maskIfNull(action.getExternalId()), maskIfNull(action.getExternalStatus()),
921                                maskIfNull(action.getErrorCode())));
922    
923                        System.out.println(RULER);
924                    }
925                }
926            }
927            else {
928                System.out.println(RULER);
929            }
930    
931            System.out.println();
932        }
933    
934        private void jobsCommand(CommandLine commandLine) throws IOException, OozieCLIException {
935            XOozieClient wc = createXOozieClient(commandLine);
936    
937            String filter = commandLine.getOptionValue(FILTER_OPTION);
938            String s = commandLine.getOptionValue(OFFSET_OPTION);
939            int start = Integer.parseInt((s != null) ? s : "0");
940            s = commandLine.getOptionValue(LEN_OPTION);
941            String jobtype = commandLine.getOptionValue(JOBTYPE_OPTION);
942            jobtype = (jobtype != null) ? jobtype : "wf";
943            int len = Integer.parseInt((s != null) ? s : "0");
944            try {
945                if (jobtype.toLowerCase().contains("wf")) {
946                    printJobs(wc.getJobsInfo(filter, start, len), commandLine.hasOption(LOCAL_TIME_OPTION), commandLine
947                            .hasOption(VERBOSE_OPTION));
948                }
949                else if (jobtype.toLowerCase().startsWith("coord")) {
950                    printCoordJobs(wc.getCoordJobsInfo(filter, start, len), commandLine.hasOption(LOCAL_TIME_OPTION),
951                            commandLine.hasOption(VERBOSE_OPTION));
952                }
953                else if (jobtype.toLowerCase().startsWith("bundle")) {
954                    printBundleJobs(wc.getBundleJobsInfo(filter, start, len), commandLine.hasOption(LOCAL_TIME_OPTION),
955                            commandLine.hasOption(VERBOSE_OPTION));
956                }
957    
958            }
959            catch (OozieClientException ex) {
960                throw new OozieCLIException(ex.toString(), ex);
961            }
962        }
963    
964        private void printCoordJobs(List<CoordinatorJob> jobs, boolean localtime, boolean verbose) throws IOException {
965            if (jobs != null && jobs.size() > 0) {
966                if (verbose) {
967                    System.out.println("Job ID" + VERBOSE_DELIMITER + "App Name" + VERBOSE_DELIMITER + "App Path"
968                            + VERBOSE_DELIMITER + "Console URL" + VERBOSE_DELIMITER + "User" + VERBOSE_DELIMITER + "Group"
969                            + VERBOSE_DELIMITER + "Concurrency" + VERBOSE_DELIMITER + "Frequency" + VERBOSE_DELIMITER
970                            + "Time Unit" + VERBOSE_DELIMITER + "Time Zone" + VERBOSE_DELIMITER + "Time Out"
971                            + VERBOSE_DELIMITER + "Started" + VERBOSE_DELIMITER + "Next Materialize" + VERBOSE_DELIMITER
972                            + "Status" + VERBOSE_DELIMITER + "Last Action" + VERBOSE_DELIMITER + "Ended");
973                    System.out.println(RULER);
974    
975                    for (CoordinatorJob job : jobs) {
976                        System.out.println(maskIfNull(job.getId()) + VERBOSE_DELIMITER + maskIfNull(job.getAppName())
977                                + VERBOSE_DELIMITER + maskIfNull(job.getAppPath()) + VERBOSE_DELIMITER
978                                + maskIfNull(job.getConsoleUrl()) + VERBOSE_DELIMITER + maskIfNull(job.getUser())
979                                + VERBOSE_DELIMITER + maskIfNull(job.getGroup()) + VERBOSE_DELIMITER + job.getConcurrency()
980                                + VERBOSE_DELIMITER + job.getFrequency() + VERBOSE_DELIMITER + job.getTimeUnit()
981                                + VERBOSE_DELIMITER + maskIfNull(job.getTimeZone()) + VERBOSE_DELIMITER + job.getTimeout()
982                                + VERBOSE_DELIMITER + maskDate(job.getStartTime(), localtime) + VERBOSE_DELIMITER
983                                + maskDate(job.getNextMaterializedTime(), localtime) + VERBOSE_DELIMITER + job.getStatus()
984                                + VERBOSE_DELIMITER + maskDate(job.getLastActionTime(), localtime) + VERBOSE_DELIMITER
985                                + maskDate(job.getEndTime(), localtime));
986    
987                        System.out.println(RULER);
988                    }
989                }
990                else {
991                    System.out.println(String.format(COORD_JOBS_FORMATTER, "Job ID", "App Name", "Status", "Freq", "Unit",
992                            "Started", "Next Materialized"));
993                    System.out.println(RULER);
994    
995                    for (CoordinatorJob job : jobs) {
996                        System.out.println(String.format(COORD_JOBS_FORMATTER, maskIfNull(job.getId()), maskIfNull(job
997                                .getAppName()), job.getStatus(), job.getFrequency(), job.getTimeUnit(), maskDate(job
998                                .getStartTime(), localtime), maskDate(job.getNextMaterializedTime(), localtime)));
999    
1000                        System.out.println(RULER);
1001                    }
1002                }
1003            }
1004            else {
1005                System.out.println("No Jobs match your criteria!");
1006            }
1007        }
1008    
1009        private void printBundleJobs(List<BundleJob> jobs, boolean localtime, boolean verbose) throws IOException {
1010            if (jobs != null && jobs.size() > 0) {
1011                if (verbose) {
1012                    System.out.println("Job ID" + VERBOSE_DELIMITER + "Bundle Name" + VERBOSE_DELIMITER + "Bundle Path"
1013                            + VERBOSE_DELIMITER + "User" + VERBOSE_DELIMITER + "Group" + VERBOSE_DELIMITER + "Status"
1014                            + VERBOSE_DELIMITER + "Kickoff" + VERBOSE_DELIMITER + "Pause" + VERBOSE_DELIMITER + "Created"
1015                            + VERBOSE_DELIMITER + "Console URL");
1016                    System.out.println(RULER);
1017    
1018                    for (BundleJob job : jobs) {
1019                        System.out.println(maskIfNull(job.getId()) + VERBOSE_DELIMITER + maskIfNull(job.getAppName())
1020                                + VERBOSE_DELIMITER + maskIfNull(job.getAppPath()) + VERBOSE_DELIMITER
1021                                + maskIfNull(job.getUser()) + VERBOSE_DELIMITER + maskIfNull(job.getGroup())
1022                                + VERBOSE_DELIMITER + job.getStatus() + VERBOSE_DELIMITER
1023                                + maskDate(job.getKickoffTime(), localtime) + VERBOSE_DELIMITER
1024                                + maskDate(job.getPauseTime(), localtime) + VERBOSE_DELIMITER
1025                                + maskDate(job.getCreatedTime(), localtime) + VERBOSE_DELIMITER
1026                                + maskIfNull(job.getConsoleUrl()));
1027    
1028                        System.out.println(RULER);
1029                    }
1030                }
1031                else {
1032                    System.out.println(String.format(BUNDLE_JOBS_FORMATTER, "Job ID", "Bundle Name", "Status", "Kickoff",
1033                            "Created", "User", "Group"));
1034                    System.out.println(RULER);
1035    
1036                    for (BundleJob job : jobs) {
1037                        System.out.println(String.format(BUNDLE_JOBS_FORMATTER, maskIfNull(job.getId()), maskIfNull(job
1038                                .getAppName()), job.getStatus(), maskDate(job.getKickoffTime(), localtime),
1039                                maskDate(job.getCreatedTime(), localtime), maskIfNull(job.getUser()), maskIfNull(job.getGroup())));
1040                        System.out.println(RULER);
1041                    }
1042                }
1043            }
1044            else {
1045                System.out.println("No Jobs match your criteria!");
1046            }
1047        }
1048    
1049        private void slaCommand(CommandLine commandLine) throws IOException, OozieCLIException {
1050            XOozieClient wc = createXOozieClient(commandLine);
1051            String s = commandLine.getOptionValue(OFFSET_OPTION);
1052            int start = Integer.parseInt((s != null) ? s : "0");
1053            s = commandLine.getOptionValue(LEN_OPTION);
1054            int len = Integer.parseInt((s != null) ? s : "100");
1055            try {
1056                wc.getSlaInfo(start, len);
1057            }
1058            catch (OozieClientException ex) {
1059                throw new OozieCLIException(ex.toString(), ex);
1060            }
1061        }
1062    
1063        private void adminCommand(CommandLine commandLine) throws OozieCLIException {
1064            XOozieClient wc = createXOozieClient(commandLine);
1065    
1066            List<String> options = new ArrayList<String>();
1067            for (Option option : commandLine.getOptions()) {
1068                options.add(option.getOpt());
1069            }
1070    
1071            try {
1072                SYSTEM_MODE status = SYSTEM_MODE.NORMAL;
1073                if (options.contains(VERSION_OPTION)) {
1074                    System.out.println("Oozie server build version: " + wc.getServerBuildVersion());
1075                }
1076                else if (options.contains(SYSTEM_MODE_OPTION)) {
1077                    String systemModeOption = commandLine.getOptionValue(SYSTEM_MODE_OPTION).toUpperCase();
1078                    try {
1079                        status = SYSTEM_MODE.valueOf(systemModeOption);
1080                    }
1081                    catch (Exception e) {
1082                        throw new OozieCLIException("Invalid input provided for option: " + SYSTEM_MODE_OPTION
1083                                + " value given :" + systemModeOption
1084                                + " Expected values are: NORMAL/NOWEBSERVICE/SAFEMODE ");
1085                    }
1086                    wc.setSystemMode(status);
1087                    System.out.println("System mode: " + status);
1088                }
1089                else if (options.contains(STATUS_OPTION)) {
1090                    status = wc.getSystemMode();
1091                    System.out.println("System mode: " + status);
1092                }
1093                else if (options.contains(QUEUE_DUMP_OPTION)) {
1094    
1095                    List<String> list = wc.getQueueDump();
1096                    if (list != null && list.size() != 0) {
1097                        for (String str : list) {
1098                            System.out.println(str);
1099                        }
1100                    }
1101                    else {
1102                        System.out.println("QueueDump is null!");
1103                    }
1104                }
1105            }
1106            catch (OozieClientException ex) {
1107                throw new OozieCLIException(ex.toString(), ex);
1108            }
1109        }
1110    
1111        private void versionCommand() throws OozieCLIException {
1112            System.out.println("Oozie client build version: "
1113                    + BuildInfo.getBuildInfo().getProperty(BuildInfo.BUILD_VERSION));
1114        }
1115    
1116        private void printJobs(List<WorkflowJob> jobs, boolean localtime, boolean verbose) throws IOException {
1117            if (jobs != null && jobs.size() > 0) {
1118                if (verbose) {
1119                    System.out.println("Job ID" + VERBOSE_DELIMITER + "App Name" + VERBOSE_DELIMITER + "App Path"
1120                            + VERBOSE_DELIMITER + "Console URL" + VERBOSE_DELIMITER + "User" + VERBOSE_DELIMITER + "Group"
1121                            + VERBOSE_DELIMITER + "Run" + VERBOSE_DELIMITER + "Created" + VERBOSE_DELIMITER + "Started"
1122                            + VERBOSE_DELIMITER + "Status" + VERBOSE_DELIMITER + "Last Modified" + VERBOSE_DELIMITER
1123                            + "Ended");
1124                    System.out.println(RULER);
1125    
1126                    for (WorkflowJob job : jobs) {
1127                        System.out.println(maskIfNull(job.getId()) + VERBOSE_DELIMITER + maskIfNull(job.getAppName())
1128                                + VERBOSE_DELIMITER + maskIfNull(job.getAppPath()) + VERBOSE_DELIMITER
1129                                + maskIfNull(job.getConsoleUrl()) + VERBOSE_DELIMITER + maskIfNull(job.getUser())
1130                                + VERBOSE_DELIMITER + maskIfNull(job.getGroup()) + VERBOSE_DELIMITER + job.getRun()
1131                                + VERBOSE_DELIMITER + maskDate(job.getCreatedTime(), localtime) + VERBOSE_DELIMITER
1132                                + maskDate(job.getStartTime(), localtime) + VERBOSE_DELIMITER + job.getStatus()
1133                                + VERBOSE_DELIMITER + maskDate(job.getLastModifiedTime(), localtime) + VERBOSE_DELIMITER
1134                                + maskDate(job.getEndTime(), localtime));
1135    
1136                        System.out.println(RULER);
1137                    }
1138                }
1139                else {
1140                    System.out.println(String.format(WORKFLOW_JOBS_FORMATTER, "Job ID", "App Name", "Status", "User",
1141                            "Group", "Started", "Ended"));
1142                    System.out.println(RULER);
1143    
1144                    for (WorkflowJob job : jobs) {
1145                        System.out.println(String.format(WORKFLOW_JOBS_FORMATTER, maskIfNull(job.getId()), maskIfNull(job
1146                                .getAppName()), job.getStatus(), maskIfNull(job.getUser()), maskIfNull(job.getGroup()),
1147                                maskDate(job.getStartTime(), localtime), maskDate(job.getEndTime(), localtime)));
1148    
1149                        System.out.println(RULER);
1150                    }
1151                }
1152            }
1153            else {
1154                System.out.println("No Jobs match your criteria!");
1155            }
1156        }
1157    
1158        private String maskIfNull(String value) {
1159            if (value != null && value.length() > 0) {
1160                return value;
1161            }
1162            return "-";
1163        }
1164    
1165        private String maskDate(Date date, boolean isLocalTimeZone) {
1166            if (date == null) {
1167                return "-";
1168            }
1169    
1170            // SimpleDateFormat dateFormater = new SimpleDateFormat("yyyy-MM-dd
1171            // HH:mm Z", Locale.US);
1172            SimpleDateFormat dateFormater = new SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.US);
1173            if (!isLocalTimeZone) {
1174                dateFormater.setTimeZone(TimeZone.getTimeZone("GMT"));
1175            }
1176            return dateFormater.format(date);
1177        }
1178    
1179        private void validateCommand(CommandLine commandLine) throws OozieCLIException {
1180            String[] args = commandLine.getArgs();
1181            if (args.length != 1) {
1182                throw new OozieCLIException("One file must be specified");
1183            }
1184            File file = new File(args[0]);
1185            if (file.exists()) {
1186                try {
1187                    List<StreamSource> sources = new ArrayList<StreamSource>();
1188                    sources.add(new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream(
1189                            "oozie-workflow-0.1.xsd")));
1190                    SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
1191                    Schema schema = factory.newSchema(sources.toArray(new StreamSource[sources.size()]));
1192                    Validator validator = schema.newValidator();
1193                    validator.validate(new StreamSource(new FileReader(file)));
1194                    System.out.println("Valid worflow-app");
1195                }
1196                catch (Exception ex) {
1197                    throw new OozieCLIException("Invalid workflow-app, " + ex.toString(), ex);
1198                }
1199            }
1200            else {
1201                throw new OozieCLIException("File does not exists");
1202            }
1203        }
1204    
1205        private void pigCommand(CommandLine commandLine) throws IOException, OozieCLIException {
1206            List<String> pigArgs = commandLine.getArgList();
1207            if (pigArgs.size() > 0) {
1208                // checking is a pigArgs starts with -X (because CLIParser cannot check this)
1209                if (!pigArgs.get(0).equals("-X")) {
1210                    throw new OozieCLIException("Unrecognized option: " + pigArgs.get(0) + " Expecting -X");
1211                }
1212                pigArgs.remove(0);
1213            }
1214    
1215            List<String> options = new ArrayList<String>();
1216            for (Option option : commandLine.getOptions()) {
1217                options.add(option.getOpt());
1218            }
1219    
1220            if (!options.contains(PIGFILE_OPTION)) {
1221                throw new OozieCLIException("Need to specify -file <scriptfile>");
1222            }
1223    
1224            if (!options.contains(CONFIG_OPTION)) {
1225                throw new OozieCLIException("Need to specify -config <configfile>");
1226            }
1227    
1228            Properties conf = getConfiguration(commandLine);
1229            String script = commandLine.getOptionValue(PIGFILE_OPTION);
1230    
1231            try {
1232                XOozieClient wc = createXOozieClient(commandLine);
1233                System.out.println(JOB_ID_PREFIX + wc.submitPig(conf, script, pigArgs.toArray(new String[pigArgs.size()])));
1234            }
1235            catch (OozieClientException ex) {
1236                throw new OozieCLIException(ex.toString(), ex);
1237            }
1238        }
1239    }