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