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