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.client;
019    
020    import java.io.BufferedReader;
021    import java.io.IOException;
022    import java.io.InputStream;
023    import java.io.InputStreamReader;
024    import java.io.OutputStream;
025    import java.io.PrintStream;
026    import java.io.Reader;
027    import java.net.HttpURLConnection;
028    import java.net.URL;
029    import java.net.URLEncoder;
030    import java.util.ArrayList;
031    import java.util.Collections;
032    import java.util.HashMap;
033    import java.util.Iterator;
034    import java.util.LinkedHashMap;
035    import java.util.List;
036    import java.util.Map;
037    import java.util.Properties;
038    import java.util.concurrent.Callable;
039    
040    import javax.xml.parsers.DocumentBuilderFactory;
041    import javax.xml.transform.Transformer;
042    import javax.xml.transform.TransformerFactory;
043    import javax.xml.transform.dom.DOMSource;
044    import javax.xml.transform.stream.StreamResult;
045    
046    import org.apache.oozie.BuildInfo;
047    import org.apache.oozie.client.rest.JsonTags;
048    import org.apache.oozie.client.rest.JsonToBean;
049    import org.apache.oozie.client.rest.RestConstants;
050    import org.json.simple.JSONArray;
051    import org.json.simple.JSONObject;
052    import org.json.simple.JSONValue;
053    import org.w3c.dom.Document;
054    import org.w3c.dom.Element;
055    
056    /**
057     * Client API to submit and manage Oozie workflow jobs against an Oozie intance.
058     * <p/>
059     * This class is thread safe.
060     * <p/>
061     * Syntax for filter for the {@link #getJobsInfo(String)} {@link #getJobsInfo(String, int, int)} methods:
062     * <code>[NAME=VALUE][;NAME=VALUE]*</code>.
063     * <p/>
064     * Valid filter names are:
065     * <p/>
066     * <ul/>
067     * <li>name: the workflow application name from the workflow definition.</li>
068     * <li>user: the user that submitted the job.</li>
069     * <li>group: the group for the job.</li>
070     * <li>status: the status of the job.</li>
071     * </ul>
072     * <p/>
073     * The query will do an AND among all the filter names. The query will do an OR among all the filter values for the same
074     * name. Multiple values must be specified as different name value pairs.
075     */
076    public class OozieClient {
077    
078        public static final long WS_PROTOCOL_VERSION_0 = 0;
079    
080        public static final long WS_PROTOCOL_VERSION = 1;
081    
082        public static final String USER_NAME = "user.name";
083    
084        @Deprecated
085        public static final String GROUP_NAME = "group.name";
086    
087        public static final String JOB_ACL = "oozie.job.acl";
088    
089        public static final String APP_PATH = "oozie.wf.application.path";
090    
091        public static final String COORDINATOR_APP_PATH = "oozie.coord.application.path";
092    
093        public static final String BUNDLE_APP_PATH = "oozie.bundle.application.path";
094    
095        public static final String BUNDLE_ID = "oozie.bundle.id";
096    
097        public static final String EXTERNAL_ID = "oozie.wf.external.id";
098    
099        public static final String WORKFLOW_NOTIFICATION_URL = "oozie.wf.workflow.notification.url";
100    
101        public static final String ACTION_NOTIFICATION_URL = "oozie.wf.action.notification.url";
102    
103        public static final String COORD_ACTION_NOTIFICATION_URL = "oozie.coord.action.notification.url";
104    
105        public static final String RERUN_SKIP_NODES = "oozie.wf.rerun.skip.nodes";
106    
107        public static final String RERUN_FAIL_NODES = "oozie.wf.rerun.failnodes";
108    
109        public static final String LOG_TOKEN = "oozie.wf.log.token";
110    
111        public static final String ACTION_MAX_RETRIES = "oozie.wf.action.max.retries";
112    
113        public static final String ACTION_RETRY_INTERVAL = "oozie.wf.action.retry.interval";
114    
115        public static final String FILTER_USER = "user";
116    
117        public static final String FILTER_GROUP = "group";
118    
119        public static final String FILTER_NAME = "name";
120    
121        public static final String FILTER_STATUS = "status";
122    
123        public static final String FILTER_FREQUENCY = "frequency";
124    
125        public static final String FILTER_ID = "id";
126    
127        public static final String FILTER_UNIT = "unit";
128    
129        public static final String FILTER_JOBID = "jobid";
130    
131        public static final String FILTER_APPNAME = "appname";
132    
133        public static final String CHANGE_VALUE_ENDTIME = "endtime";
134    
135        public static final String CHANGE_VALUE_PAUSETIME = "pausetime";
136    
137        public static final String CHANGE_VALUE_CONCURRENCY = "concurrency";
138    
139        public static final String LIBPATH = "oozie.libpath";
140    
141        public static final String USE_SYSTEM_LIBPATH = "oozie.use.system.libpath";
142    
143        public static enum SYSTEM_MODE {
144            NORMAL, NOWEBSERVICE, SAFEMODE
145        };
146    
147        /**
148         * debugMode =0 means no debugging. > 0 means debugging on.
149         */
150        public int debugMode = 0;
151    
152        private String baseUrl;
153        private String protocolUrl;
154        private boolean validatedVersion = false;
155        private final Map<String, String> headers = new HashMap<String, String>();
156    
157        private static ThreadLocal<String> USER_NAME_TL = new ThreadLocal<String>();
158    
159        /**
160         * Allows to impersonate other users in the Oozie server. The current user
161         * must be configured as a proxyuser in Oozie.
162         * <p/>
163         * IMPORTANT: impersonation happens only with Oozie client requests done within
164         * doAs() calls.
165         *
166         * @param userName user to impersonate.
167         * @param callable callable with {@link OozieClient} calls impersonating the specified user.
168         * @return any response returned by the {@link Callable#call()} method.
169         * @throws Exception thrown by the {@link Callable#call()} method.
170         */
171        public static <T> T doAs(String userName, Callable<T> callable) throws Exception {
172            notEmpty(userName, "userName");
173            notNull(callable, "callable");
174            try {
175                USER_NAME_TL.set(userName);
176                return callable.call();
177            }
178            finally {
179                USER_NAME_TL.remove();
180            }
181        }
182    
183        protected OozieClient() {
184        }
185    
186        /**
187         * Create a Workflow client instance.
188         *
189         * @param oozieUrl URL of the Oozie instance it will interact with.
190         */
191        public OozieClient(String oozieUrl) {
192            this.baseUrl = notEmpty(oozieUrl, "oozieUrl");
193            if (!this.baseUrl.endsWith("/")) {
194                this.baseUrl += "/";
195            }
196        }
197    
198        /**
199         * Return the Oozie URL of the workflow client instance.
200         * <p/>
201         * This URL is the base URL fo the Oozie system, with not protocol versioning.
202         *
203         * @return the Oozie URL of the workflow client instance.
204         */
205        public String getOozieUrl() {
206            return baseUrl;
207        }
208    
209        /**
210         * Return the Oozie URL used by the client and server for WS communications.
211         * <p/>
212         * This URL is the original URL plus the versioning element path.
213         *
214         * @return the Oozie URL used by the client and server for communication.
215         * @throws OozieClientException thrown in the client and the server are not protocol compatible.
216         */
217        public String getProtocolUrl() throws OozieClientException {
218            validateWSVersion();
219            return protocolUrl;
220        }
221    
222        /**
223         * @return current debug Mode
224         */
225        public int getDebugMode() {
226            return debugMode;
227        }
228    
229        /**
230         * Set debug mode.
231         *
232         * @param debugMode : 0 means no debugging. > 0 means debugging
233         */
234        public void setDebugMode(int debugMode) {
235            this.debugMode = debugMode;
236        }
237    
238        /**
239         * Validate that the Oozie client and server instances are protocol compatible.
240         *
241         * @throws OozieClientException thrown in the client and the server are not protocol compatible.
242         */
243        public synchronized void validateWSVersion() throws OozieClientException {
244            if (!validatedVersion) {
245                try {
246                    URL url = new URL(baseUrl + RestConstants.VERSIONS);
247                    HttpURLConnection conn = createConnection(url, "GET");
248                    if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
249                        JSONArray array = (JSONArray) JSONValue.parse(new InputStreamReader(conn.getInputStream()));
250                        if (array == null) {
251                            throw new OozieClientException("HTTP error", "no response message");
252                        }
253                        if (!array.contains(WS_PROTOCOL_VERSION) && !array.contains(WS_PROTOCOL_VERSION_0)) {
254                            StringBuilder msg = new StringBuilder();
255                            msg.append("Supported version [").append(WS_PROTOCOL_VERSION).append(
256                                    "] or less, Unsupported versions[");
257                            String separator = "";
258                            for (Object version : array) {
259                                msg.append(separator).append(version);
260                            }
261                            msg.append("]");
262                            throw new OozieClientException(OozieClientException.UNSUPPORTED_VERSION, msg.toString());
263                        }
264                        if (array.contains(WS_PROTOCOL_VERSION)) {
265                            protocolUrl = baseUrl + "v" + WS_PROTOCOL_VERSION + "/";
266                        }
267                        else {
268                            if (array.contains(WS_PROTOCOL_VERSION_0)) {
269                                protocolUrl = baseUrl + "v" + WS_PROTOCOL_VERSION_0 + "/";
270                            }
271                        }
272                    }
273                    else {
274                        handleError(conn);
275                    }
276                }
277                catch (IOException ex) {
278                    throw new OozieClientException(OozieClientException.IO_ERROR, ex);
279                }
280                validatedVersion = true;
281            }
282        }
283    
284        /**
285         * Create an empty configuration with just the {@link #USER_NAME} set to the JVM user name.
286         *
287         * @return an empty configuration.
288         */
289        public Properties createConfiguration() {
290            Properties conf = new Properties();
291            String userName = USER_NAME_TL.get();
292            if (userName == null) {
293                userName = System.getProperty("user.name");
294            }
295            conf.setProperty(USER_NAME, userName);
296            return conf;
297        }
298    
299        /**
300         * Set a HTTP header to be used in the WS requests by the workflow instance.
301         *
302         * @param name header name.
303         * @param value header value.
304         */
305        public void setHeader(String name, String value) {
306            headers.put(notEmpty(name, "name"), notNull(value, "value"));
307        }
308    
309        /**
310         * Get the value of a set HTTP header from the workflow instance.
311         *
312         * @param name header name.
313         * @return header value, <code>null</code> if not set.
314         */
315        public String getHeader(String name) {
316            return headers.get(notEmpty(name, "name"));
317        }
318    
319        /**
320         * Get the set HTTP header
321         *
322         * @return map of header key and value
323         */
324        public Map<String, String> getHeaders() {
325            return headers;
326        }
327    
328        /**
329         * Remove a HTTP header from the workflow client instance.
330         *
331         * @param name header name.
332         */
333        public void removeHeader(String name) {
334            headers.remove(notEmpty(name, "name"));
335        }
336    
337        /**
338         * Return an iterator with all the header names set in the workflow instance.
339         *
340         * @return header names.
341         */
342        public Iterator<String> getHeaderNames() {
343            return Collections.unmodifiableMap(headers).keySet().iterator();
344        }
345    
346        private URL createURL(String collection, String resource, Map<String, String> parameters) throws IOException,
347                OozieClientException {
348            validateWSVersion();
349            StringBuilder sb = new StringBuilder();
350            sb.append(protocolUrl).append(collection);
351            if (resource != null && resource.length() > 0) {
352                sb.append("/").append(resource);
353            }
354            if (parameters.size() > 0) {
355                String separator = "?";
356                for (Map.Entry<String, String> param : parameters.entrySet()) {
357                    if (param.getValue() != null) {
358                        sb.append(separator).append(URLEncoder.encode(param.getKey(), "UTF-8")).append("=").append(
359                                URLEncoder.encode(param.getValue(), "UTF-8"));
360                        separator = "&";
361                    }
362                }
363            }
364            return new URL(sb.toString());
365        }
366    
367        private boolean validateCommand(String url) {
368            {
369                if (protocolUrl.contains(baseUrl + "v0")) {
370                    if (url.contains("dryrun") || url.contains("jobtype=c") || url.contains("systemmode")) {
371                        return false;
372                    }
373                }
374            }
375            return true;
376        }
377    
378        /**
379         * Create http connection to oozie server.
380         *
381         * @param url
382         * @param method
383         * @return connection
384         * @throws IOException
385         * @throws OozieClientException
386         */
387        protected HttpURLConnection createConnection(URL url, String method) throws IOException, OozieClientException {
388            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
389            conn.setRequestMethod(method);
390            if (method.equals("POST") || method.equals("PUT")) {
391                conn.setDoOutput(true);
392            }
393            for (Map.Entry<String, String> header : headers.entrySet()) {
394                conn.setRequestProperty(header.getKey(), header.getValue());
395            }
396            return conn;
397        }
398    
399        protected abstract class ClientCallable<T> implements Callable<T> {
400            private final String method;
401            private final String collection;
402            private final String resource;
403            private final Map<String, String> params;
404    
405            public ClientCallable(String method, String collection, String resource, Map<String, String> params) {
406                this.method = method;
407                this.collection = collection;
408                this.resource = resource;
409                this.params = params;
410            }
411    
412            public T call() throws OozieClientException {
413                try {
414                    URL url = createURL(collection, resource, params);
415                    if (validateCommand(url.toString())) {
416                        if (getDebugMode() > 0) {
417                            System.out.println("Connection URL:[" + url + "]");
418                        }
419                        HttpURLConnection conn = createConnection(url, method);
420                        return call(conn);
421                    }
422                    else {
423                        System.out.println("Option not supported in target server. Supported only on Oozie-2.0 or greater."
424                                + " Use 'oozie help' for details");
425                        throw new OozieClientException(OozieClientException.UNSUPPORTED_VERSION, new Exception());
426                    }
427                }
428                catch (IOException ex) {
429                    throw new OozieClientException(OozieClientException.IO_ERROR, ex);
430                }
431    
432            }
433    
434            protected abstract T call(HttpURLConnection conn) throws IOException, OozieClientException;
435        }
436    
437        static void handleError(HttpURLConnection conn) throws IOException, OozieClientException {
438            int status = conn.getResponseCode();
439            String error = conn.getHeaderField(RestConstants.OOZIE_ERROR_CODE);
440            String message = conn.getHeaderField(RestConstants.OOZIE_ERROR_MESSAGE);
441    
442            if (error == null) {
443                error = "HTTP error code: " + status;
444            }
445    
446            if (message == null) {
447                message = conn.getResponseMessage();
448            }
449            throw new OozieClientException(error, message);
450        }
451    
452        static Map<String, String> prepareParams(String... params) {
453            Map<String, String> map = new LinkedHashMap<String, String>();
454            for (int i = 0; i < params.length; i = i + 2) {
455                map.put(params[i], params[i + 1]);
456            }
457            String doAsUserName = USER_NAME_TL.get();
458            if (doAsUserName != null) {
459                map.put(RestConstants.DO_AS_PARAM, doAsUserName);
460            }
461            return map;
462        }
463    
464        public void writeToXml(Properties props, OutputStream out) throws IOException {
465            try {
466                Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
467                Element conf = doc.createElement("configuration");
468                doc.appendChild(conf);
469                conf.appendChild(doc.createTextNode("\n"));
470                for (String name : props.stringPropertyNames()) { // Properties whose key or value is not of type String are omitted.
471                    String value = props.getProperty(name);
472                    Element propNode = doc.createElement("property");
473                    conf.appendChild(propNode);
474    
475                    Element nameNode = doc.createElement("name");
476                    nameNode.appendChild(doc.createTextNode(name.trim()));
477                    propNode.appendChild(nameNode);
478    
479                    Element valueNode = doc.createElement("value");
480                    valueNode.appendChild(doc.createTextNode(value.trim()));
481                    propNode.appendChild(valueNode);
482    
483                    conf.appendChild(doc.createTextNode("\n"));
484                }
485    
486                DOMSource source = new DOMSource(doc);
487                StreamResult result = new StreamResult(out);
488                TransformerFactory transFactory = TransformerFactory.newInstance();
489                Transformer transformer = transFactory.newTransformer();
490                transformer.transform(source, result);
491            }
492            catch (Exception e) {
493                throw new IOException(e);
494            }
495        }
496    
497        private class JobSubmit extends ClientCallable<String> {
498            private final Properties conf;
499    
500            JobSubmit(Properties conf, boolean start) {
501                super("POST", RestConstants.JOBS, "", (start) ? prepareParams(RestConstants.ACTION_PARAM,
502                        RestConstants.JOB_ACTION_START) : prepareParams());
503                this.conf = notNull(conf, "conf");
504            }
505    
506            JobSubmit(String jobId, Properties conf) {
507                super("PUT", RestConstants.JOB, notEmpty(jobId, "jobId"), prepareParams(RestConstants.ACTION_PARAM,
508                        RestConstants.JOB_ACTION_RERUN));
509                this.conf = notNull(conf, "conf");
510            }
511    
512            public JobSubmit(Properties conf, String jobActionDryrun) {
513                super("POST", RestConstants.JOBS, "", prepareParams(RestConstants.ACTION_PARAM,
514                        RestConstants.JOB_ACTION_DRYRUN));
515                this.conf = notNull(conf, "conf");
516            }
517    
518            @Override
519            protected String call(HttpURLConnection conn) throws IOException, OozieClientException {
520                conn.setRequestProperty("content-type", RestConstants.XML_CONTENT_TYPE);
521                writeToXml(conf, conn.getOutputStream());
522                if (conn.getResponseCode() == HttpURLConnection.HTTP_CREATED) {
523                    JSONObject json = (JSONObject) JSONValue.parse(new InputStreamReader(conn.getInputStream()));
524                    return (String) json.get(JsonTags.JOB_ID);
525                }
526                if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) {
527                    handleError(conn);
528                }
529                return null;
530            }
531        }
532    
533        /**
534         * Submit a workflow job.
535         *
536         * @param conf job configuration.
537         * @return the job Id.
538         * @throws OozieClientException thrown if the job could not be submitted.
539         */
540        public String submit(Properties conf) throws OozieClientException {
541            return (new JobSubmit(conf, false)).call();
542        }
543    
544        private class JobAction extends ClientCallable<Void> {
545    
546            JobAction(String jobId, String action) {
547                super("PUT", RestConstants.JOB, notEmpty(jobId, "jobId"), prepareParams(RestConstants.ACTION_PARAM, action));
548            }
549    
550            JobAction(String jobId, String action, String params) {
551                super("PUT", RestConstants.JOB, notEmpty(jobId, "jobId"), prepareParams(RestConstants.ACTION_PARAM, action,
552                        RestConstants.JOB_CHANGE_VALUE, params));
553            }
554    
555            @Override
556            protected Void call(HttpURLConnection conn) throws IOException, OozieClientException {
557                if (!(conn.getResponseCode() == HttpURLConnection.HTTP_OK)) {
558                    handleError(conn);
559                }
560                return null;
561            }
562        }
563    
564        /**
565         * dryrun for a given job
566         *
567         * @param conf Job configuration.
568         */
569        public String dryrun(Properties conf) throws OozieClientException {
570            return new JobSubmit(conf, RestConstants.JOB_ACTION_DRYRUN).call();
571        }
572    
573        /**
574         * Start a workflow job.
575         *
576         * @param jobId job Id.
577         * @throws OozieClientException thrown if the job could not be started.
578         */
579        public void start(String jobId) throws OozieClientException {
580            new JobAction(jobId, RestConstants.JOB_ACTION_START).call();
581        }
582    
583        /**
584         * Submit and start a workflow job.
585         *
586         * @param conf job configuration.
587         * @return the job Id.
588         * @throws OozieClientException thrown if the job could not be submitted.
589         */
590        public String run(Properties conf) throws OozieClientException {
591            return (new JobSubmit(conf, true)).call();
592        }
593    
594        /**
595         * Rerun a workflow job.
596         *
597         * @param jobId job Id to rerun.
598         * @param conf configuration information for the rerun.
599         * @throws OozieClientException thrown if the job could not be started.
600         */
601        public void reRun(String jobId, Properties conf) throws OozieClientException {
602            new JobSubmit(jobId, conf).call();
603        }
604    
605        /**
606         * Suspend a workflow job.
607         *
608         * @param jobId job Id.
609         * @throws OozieClientException thrown if the job could not be suspended.
610         */
611        public void suspend(String jobId) throws OozieClientException {
612            new JobAction(jobId, RestConstants.JOB_ACTION_SUSPEND).call();
613        }
614    
615        /**
616         * Resume a workflow job.
617         *
618         * @param jobId job Id.
619         * @throws OozieClientException thrown if the job could not be resume.
620         */
621        public void resume(String jobId) throws OozieClientException {
622            new JobAction(jobId, RestConstants.JOB_ACTION_RESUME).call();
623        }
624    
625        /**
626         * Kill a workflow job.
627         *
628         * @param jobId job Id.
629         * @throws OozieClientException thrown if the job could not be killed.
630         */
631        public void kill(String jobId) throws OozieClientException {
632            new JobAction(jobId, RestConstants.JOB_ACTION_KILL).call();
633        }
634    
635        /**
636         * Change a coordinator job.
637         *
638         * @param jobId job Id.
639         * @param changeValue change value.
640         * @throws OozieClientException thrown if the job could not be changed.
641         */
642        public void change(String jobId, String changeValue) throws OozieClientException {
643            new JobAction(jobId, RestConstants.JOB_ACTION_CHANGE, changeValue).call();
644        }
645    
646        private class JobInfo extends ClientCallable<WorkflowJob> {
647    
648            JobInfo(String jobId, int start, int len) {
649                super("GET", RestConstants.JOB, notEmpty(jobId, "jobId"), prepareParams(RestConstants.JOB_SHOW_PARAM,
650                        RestConstants.JOB_SHOW_INFO, RestConstants.OFFSET_PARAM, Integer.toString(start),
651                        RestConstants.LEN_PARAM, Integer.toString(len)));
652            }
653    
654            @Override
655            protected WorkflowJob call(HttpURLConnection conn) throws IOException, OozieClientException {
656                if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) {
657                    Reader reader = new InputStreamReader(conn.getInputStream());
658                    JSONObject json = (JSONObject) JSONValue.parse(reader);
659                    return JsonToBean.createWorkflowJob(json);
660                }
661                else {
662                    handleError(conn);
663                }
664                return null;
665            }
666        }
667    
668        private class WorkflowActionInfo extends ClientCallable<WorkflowAction> {
669            WorkflowActionInfo(String actionId) {
670                super("GET", RestConstants.JOB, notEmpty(actionId, "id"), prepareParams(RestConstants.JOB_SHOW_PARAM,
671                        RestConstants.JOB_SHOW_INFO));
672            }
673    
674            @Override
675            protected WorkflowAction call(HttpURLConnection conn) throws IOException, OozieClientException {
676                if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) {
677                    Reader reader = new InputStreamReader(conn.getInputStream());
678                    JSONObject json = (JSONObject) JSONValue.parse(reader);
679                    return JsonToBean.createWorkflowAction(json);
680                }
681                else {
682                    handleError(conn);
683                }
684                return null;
685            }
686        }
687    
688        /**
689         * Get the info of a workflow job.
690         *
691         * @param jobId job Id.
692         * @return the job info.
693         * @throws OozieClientException thrown if the job info could not be retrieved.
694         */
695        public WorkflowJob getJobInfo(String jobId) throws OozieClientException {
696            return getJobInfo(jobId, 0, 0);
697        }
698    
699        /**
700         * Get the info of a workflow job and subset actions.
701         *
702         * @param jobId job Id.
703         * @param start starting index in the list of actions belonging to the job
704         * @param len number of actions to be returned
705         * @return the job info.
706         * @throws OozieClientException thrown if the job info could not be retrieved.
707         */
708        public WorkflowJob getJobInfo(String jobId, int start, int len) throws OozieClientException {
709            return new JobInfo(jobId, start, len).call();
710        }
711    
712        /**
713         * Get the info of a workflow action.
714         *
715         * @param actionId Id.
716         * @return the workflow action info.
717         * @throws OozieClientException thrown if the job info could not be retrieved.
718         */
719        public WorkflowAction getWorkflowActionInfo(String actionId) throws OozieClientException {
720            return new WorkflowActionInfo(actionId).call();
721        }
722    
723        /**
724         * Get the log of a workflow job.
725         *
726         * @param jobId job Id.
727         * @return the job log.
728         * @throws OozieClientException thrown if the job info could not be retrieved.
729         */
730        public String getJobLog(String jobId) throws OozieClientException {
731            return new JobLog(jobId).call();
732        }
733    
734        /**
735         * Get the log of a job.
736         *
737         * @param jobId job Id.
738         * @param logRetrievalType Based on which filter criteria the log is retrieved
739         * @param logRetrievalScope Value for the retrieval type
740         * @param ps Printstream of command line interface
741         * @throws OozieClientException thrown if the job info could not be retrieved.
742         */
743        public void getJobLog(String jobId, String logRetrievalType, String logRetrievalScope, PrintStream ps)
744                throws OozieClientException {
745            new JobLog(jobId, logRetrievalType, logRetrievalScope, ps).call();
746        }
747    
748        private class JobLog extends JobMetadata {
749            JobLog(String jobId) {
750                super(jobId, RestConstants.JOB_SHOW_LOG);
751            }
752    
753            JobLog(String jobId, String logRetrievalType, String logRetrievalScope, PrintStream ps) {
754                super(jobId, logRetrievalType, logRetrievalScope, RestConstants.JOB_SHOW_LOG, ps);
755            }
756        }
757    
758        /**
759         * Get the definition of a workflow job.
760         *
761         * @param jobId job Id.
762         * @return the job log.
763         * @throws OozieClientException thrown if the job info could not be retrieved.
764         */
765        public String getJobDefinition(String jobId) throws OozieClientException {
766            return new JobDefinition(jobId).call();
767        }
768    
769        private class JobDefinition extends JobMetadata {
770    
771            JobDefinition(String jobId) {
772                super(jobId, RestConstants.JOB_SHOW_DEFINITION);
773            }
774        }
775    
776        private class JobMetadata extends ClientCallable<String> {
777            PrintStream printStream;
778    
779            JobMetadata(String jobId, String metaType) {
780                super("GET", RestConstants.JOB, notEmpty(jobId, "jobId"), prepareParams(RestConstants.JOB_SHOW_PARAM,
781                        metaType));
782            }
783    
784            JobMetadata(String jobId, String logRetrievalType, String logRetrievalScope, String metaType, PrintStream ps) {
785                super("GET", RestConstants.JOB, notEmpty(jobId, "jobId"), prepareParams(RestConstants.JOB_SHOW_PARAM,
786                        metaType, RestConstants.JOB_LOG_TYPE_PARAM, logRetrievalType, RestConstants.JOB_LOG_SCOPE_PARAM,
787                        logRetrievalScope));
788                printStream = ps;
789            }
790    
791            @Override
792            protected String call(HttpURLConnection conn) throws IOException, OozieClientException {
793                String returnVal = null;
794                if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) {
795                    InputStream is = conn.getInputStream();
796                    InputStreamReader isr = new InputStreamReader(is);
797                    try {
798                        if (printStream != null) {
799                            sendToOutputStream(isr, -1);
800                        }
801                        else {
802                            returnVal = getReaderAsString(isr, -1);
803                        }
804                    }
805                    finally {
806                        isr.close();
807                    }
808                }
809                else {
810                    handleError(conn);
811                }
812                return returnVal;
813            }
814    
815            /**
816             * Output the log to command line interface
817             *
818             * @param reader reader to read into a string.
819             * @param maxLen max content length allowed, if -1 there is no limit.
820             * @param ps Printstream of command line interface
821             * @throws IOException
822             */
823            private void sendToOutputStream(Reader reader, int maxLen) throws IOException {
824                if (reader == null) {
825                    throw new IllegalArgumentException("reader cannot be null");
826                }
827                StringBuilder sb = new StringBuilder();
828                char[] buffer = new char[2048];
829                int read;
830                int count = 0;
831                int noOfCharstoFlush = 1024;
832                while ((read = reader.read(buffer)) > -1) {
833                    count += read;
834                    if ((maxLen > -1) && (count > maxLen)) {
835                        break;
836                    }
837                    sb.append(buffer, 0, read);
838                    if (sb.length() > noOfCharstoFlush) {
839                        printStream.print(sb.toString());
840                        sb = new StringBuilder("");
841                    }
842                }
843                printStream.print(sb.toString());
844            }
845    
846            /**
847             * Return a reader as string.
848             * <p/>
849             *
850             * @param reader reader to read into a string.
851             * @param maxLen max content length allowed, if -1 there is no limit.
852             * @return the reader content.
853             * @throws IOException thrown if the resource could not be read.
854             */
855            private String getReaderAsString(Reader reader, int maxLen) throws IOException {
856                if (reader == null) {
857                    throw new IllegalArgumentException("reader cannot be null");
858                }
859                StringBuffer sb = new StringBuffer();
860                char[] buffer = new char[2048];
861                int read;
862                int count = 0;
863                while ((read = reader.read(buffer)) > -1) {
864                    count += read;
865    
866                    // read up to maxLen chars;
867                    if ((maxLen > -1) && (count > maxLen)) {
868                        break;
869                    }
870                    sb.append(buffer, 0, read);
871                }
872                reader.close();
873                return sb.toString();
874            }
875        }
876    
877        private class CoordJobInfo extends ClientCallable<CoordinatorJob> {
878    
879            CoordJobInfo(String jobId, String filter, int start, int len) {
880                super("GET", RestConstants.JOB, notEmpty(jobId, "jobId"), prepareParams(RestConstants.JOB_SHOW_PARAM,
881                        RestConstants.JOB_SHOW_INFO, RestConstants.JOB_FILTER_PARAM, filter, RestConstants.OFFSET_PARAM, Integer.toString(start),
882                        RestConstants.LEN_PARAM, Integer.toString(len)));
883            }
884    
885            @Override
886            protected CoordinatorJob call(HttpURLConnection conn) throws IOException, OozieClientException {
887                if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) {
888                    Reader reader = new InputStreamReader(conn.getInputStream());
889                    JSONObject json = (JSONObject) JSONValue.parse(reader);
890                    return JsonToBean.createCoordinatorJob(json);
891                }
892                else {
893                    handleError(conn);
894                }
895                return null;
896            }
897        }
898    
899        private class BundleJobInfo extends ClientCallable<BundleJob> {
900    
901            BundleJobInfo(String jobId) {
902                super("GET", RestConstants.JOB, notEmpty(jobId, "jobId"), prepareParams(RestConstants.JOB_SHOW_PARAM,
903                        RestConstants.JOB_SHOW_INFO));
904            }
905    
906            @Override
907            protected BundleJob call(HttpURLConnection conn) throws IOException, OozieClientException {
908                if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) {
909                    Reader reader = new InputStreamReader(conn.getInputStream());
910                    JSONObject json = (JSONObject) JSONValue.parse(reader);
911                    return JsonToBean.createBundleJob(json);
912                }
913                else {
914                    handleError(conn);
915                }
916                return null;
917            }
918        }
919    
920        private class CoordActionInfo extends ClientCallable<CoordinatorAction> {
921            CoordActionInfo(String actionId) {
922                super("GET", RestConstants.JOB, notEmpty(actionId, "id"), prepareParams(RestConstants.JOB_SHOW_PARAM,
923                        RestConstants.JOB_SHOW_INFO));
924            }
925    
926            @Override
927            protected CoordinatorAction call(HttpURLConnection conn) throws IOException, OozieClientException {
928                if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) {
929                    Reader reader = new InputStreamReader(conn.getInputStream());
930                    JSONObject json = (JSONObject) JSONValue.parse(reader);
931                    return JsonToBean.createCoordinatorAction(json);
932                }
933                else {
934                    handleError(conn);
935                }
936                return null;
937            }
938        }
939    
940        /**
941         * Get the info of a bundle job.
942         *
943         * @param jobId job Id.
944         * @return the job info.
945         * @throws OozieClientException thrown if the job info could not be retrieved.
946         */
947        public BundleJob getBundleJobInfo(String jobId) throws OozieClientException {
948            return new BundleJobInfo(jobId).call();
949        }
950    
951        /**
952         * Get the info of a coordinator job.
953         *
954         * @param jobId job Id.
955         * @return the job info.
956         * @throws OozieClientException thrown if the job info could not be retrieved.
957         */
958        public CoordinatorJob getCoordJobInfo(String jobId) throws OozieClientException {
959            return new CoordJobInfo(jobId, null, 0, 0).call();
960        }
961    
962        /**
963         * Get the info of a coordinator job and subset actions.
964         *
965         * @param jobId job Id.
966         * @param filter filter the status filter
967         * @param start starting index in the list of actions belonging to the job
968         * @param len number of actions to be returned
969         * @return the job info.
970         * @throws OozieClientException thrown if the job info could not be retrieved.
971         */
972        public CoordinatorJob getCoordJobInfo(String jobId, String filter, int start, int len) throws OozieClientException {
973            return new CoordJobInfo(jobId, filter, start, len).call();
974        }
975    
976        /**
977         * Get the info of a coordinator action.
978         *
979         * @param actionId Id.
980         * @return the coordinator action info.
981         * @throws OozieClientException thrown if the job info could not be retrieved.
982         */
983        public CoordinatorAction getCoordActionInfo(String actionId) throws OozieClientException {
984            return new CoordActionInfo(actionId).call();
985        }
986    
987        private class JobsStatus extends ClientCallable<List<WorkflowJob>> {
988    
989            JobsStatus(String filter, int start, int len) {
990                super("GET", RestConstants.JOBS, "", prepareParams(RestConstants.JOBS_FILTER_PARAM, filter,
991                        RestConstants.JOBTYPE_PARAM, "wf", RestConstants.OFFSET_PARAM, Integer.toString(start),
992                        RestConstants.LEN_PARAM, Integer.toString(len)));
993            }
994    
995            @Override
996            @SuppressWarnings("unchecked")
997            protected List<WorkflowJob> call(HttpURLConnection conn) throws IOException, OozieClientException {
998                conn.setRequestProperty("content-type", RestConstants.XML_CONTENT_TYPE);
999                if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) {
1000                    Reader reader = new InputStreamReader(conn.getInputStream());
1001                    JSONObject json = (JSONObject) JSONValue.parse(reader);
1002                    JSONArray workflows = (JSONArray) json.get(JsonTags.WORKFLOWS_JOBS);
1003                    if (workflows == null) {
1004                        workflows = new JSONArray();
1005                    }
1006                    return JsonToBean.createWorkflowJobList(workflows);
1007                }
1008                else {
1009                    handleError(conn);
1010                }
1011                return null;
1012            }
1013        }
1014    
1015        private class CoordJobsStatus extends ClientCallable<List<CoordinatorJob>> {
1016    
1017            CoordJobsStatus(String filter, int start, int len) {
1018                super("GET", RestConstants.JOBS, "", prepareParams(RestConstants.JOBS_FILTER_PARAM, filter,
1019                        RestConstants.JOBTYPE_PARAM, "coord", RestConstants.OFFSET_PARAM, Integer.toString(start),
1020                        RestConstants.LEN_PARAM, Integer.toString(len)));
1021            }
1022    
1023            @Override
1024            protected List<CoordinatorJob> call(HttpURLConnection conn) throws IOException, OozieClientException {
1025                conn.setRequestProperty("content-type", RestConstants.XML_CONTENT_TYPE);
1026                if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) {
1027                    Reader reader = new InputStreamReader(conn.getInputStream());
1028                    JSONObject json = (JSONObject) JSONValue.parse(reader);
1029                    JSONArray jobs = (JSONArray) json.get(JsonTags.COORDINATOR_JOBS);
1030                    if (jobs == null) {
1031                        jobs = new JSONArray();
1032                    }
1033                    return JsonToBean.createCoordinatorJobList(jobs);
1034                }
1035                else {
1036                    handleError(conn);
1037                }
1038                return null;
1039            }
1040        }
1041    
1042        private class BundleJobsStatus extends ClientCallable<List<BundleJob>> {
1043    
1044            BundleJobsStatus(String filter, int start, int len) {
1045                super("GET", RestConstants.JOBS, "", prepareParams(RestConstants.JOBS_FILTER_PARAM, filter,
1046                        RestConstants.JOBTYPE_PARAM, "bundle", RestConstants.OFFSET_PARAM, Integer.toString(start),
1047                        RestConstants.LEN_PARAM, Integer.toString(len)));
1048            }
1049    
1050            @Override
1051            protected List<BundleJob> call(HttpURLConnection conn) throws IOException, OozieClientException {
1052                conn.setRequestProperty("content-type", RestConstants.XML_CONTENT_TYPE);
1053                if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) {
1054                    Reader reader = new InputStreamReader(conn.getInputStream());
1055                    JSONObject json = (JSONObject) JSONValue.parse(reader);
1056                    JSONArray jobs = (JSONArray) json.get(JsonTags.BUNDLE_JOBS);
1057                    if (jobs == null) {
1058                        jobs = new JSONArray();
1059                    }
1060                    return JsonToBean.createBundleJobList(jobs);
1061                }
1062                else {
1063                    handleError(conn);
1064                }
1065                return null;
1066            }
1067        }
1068    
1069        private class BulkResponseStatus extends ClientCallable<List<BulkResponse>> {
1070    
1071            BulkResponseStatus(String filter, int start, int len) {
1072                super("GET", RestConstants.JOBS, "", prepareParams(RestConstants.JOBS_BULK_PARAM, filter,
1073                        RestConstants.OFFSET_PARAM, Integer.toString(start), RestConstants.LEN_PARAM, Integer.toString(len)));
1074            }
1075    
1076            @Override
1077            protected List<BulkResponse> call(HttpURLConnection conn) throws IOException, OozieClientException {
1078                conn.setRequestProperty("content-type", RestConstants.XML_CONTENT_TYPE);
1079                if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) {
1080                    Reader reader = new InputStreamReader(conn.getInputStream());
1081                    JSONObject json = (JSONObject) JSONValue.parse(reader);
1082                    JSONArray results = (JSONArray) json.get(JsonTags.BULK_RESPONSES);
1083                    if (results == null) {
1084                        results = new JSONArray();
1085                    }
1086                    return JsonToBean.createBulkResponseList(results);
1087                }
1088                else {
1089                    handleError(conn);
1090                }
1091                return null;
1092            }
1093        }
1094    
1095        private class CoordRerun extends ClientCallable<List<CoordinatorAction>> {
1096    
1097            CoordRerun(String jobId, String rerunType, String scope, boolean refresh, boolean noCleanup) {
1098                super("PUT", RestConstants.JOB, notEmpty(jobId, "jobId"), prepareParams(RestConstants.ACTION_PARAM,
1099                        RestConstants.JOB_COORD_ACTION_RERUN, RestConstants.JOB_COORD_RERUN_TYPE_PARAM, rerunType,
1100                        RestConstants.JOB_COORD_RERUN_SCOPE_PARAM, scope, RestConstants.JOB_COORD_RERUN_REFRESH_PARAM,
1101                        Boolean.toString(refresh), RestConstants.JOB_COORD_RERUN_NOCLEANUP_PARAM, Boolean
1102                                .toString(noCleanup)));
1103            }
1104    
1105            @Override
1106            protected List<CoordinatorAction> call(HttpURLConnection conn) throws IOException, OozieClientException {
1107                conn.setRequestProperty("content-type", RestConstants.XML_CONTENT_TYPE);
1108                if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) {
1109                    Reader reader = new InputStreamReader(conn.getInputStream());
1110                    JSONObject json = (JSONObject) JSONValue.parse(reader);
1111                    JSONArray coordActions = (JSONArray) json.get(JsonTags.COORDINATOR_ACTIONS);
1112                    return JsonToBean.createCoordinatorActionList(coordActions);
1113                }
1114                else {
1115                    handleError(conn);
1116                }
1117                return null;
1118            }
1119        }
1120    
1121        private class BundleRerun extends ClientCallable<Void> {
1122    
1123            BundleRerun(String jobId, String coordScope, String dateScope, boolean refresh, boolean noCleanup) {
1124                super("PUT", RestConstants.JOB, notEmpty(jobId, "jobId"), prepareParams(RestConstants.ACTION_PARAM,
1125                        RestConstants.JOB_BUNDLE_ACTION_RERUN, RestConstants.JOB_BUNDLE_RERUN_COORD_SCOPE_PARAM,
1126                        coordScope, RestConstants.JOB_BUNDLE_RERUN_DATE_SCOPE_PARAM, dateScope,
1127                        RestConstants.JOB_COORD_RERUN_REFRESH_PARAM, Boolean.toString(refresh),
1128                        RestConstants.JOB_COORD_RERUN_NOCLEANUP_PARAM, Boolean.toString(noCleanup)));
1129            }
1130    
1131            @Override
1132            protected Void call(HttpURLConnection conn) throws IOException, OozieClientException {
1133                conn.setRequestProperty("content-type", RestConstants.XML_CONTENT_TYPE);
1134                if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) {
1135                    return null;
1136                }
1137                else {
1138                    handleError(conn);
1139                }
1140                return null;
1141            }
1142        }
1143    
1144        /**
1145         * Rerun coordinator actions.
1146         *
1147         * @param jobId coordinator jobId
1148         * @param rerunType rerun type 'date' if -date is used, 'action-id' if -action is used
1149         * @param scope rerun scope for date or actionIds
1150         * @param refresh true if -refresh is given in command option
1151         * @param noCleanup true if -nocleanup is given in command option
1152         * @throws OozieClientException
1153         */
1154        public List<CoordinatorAction> reRunCoord(String jobId, String rerunType, String scope, boolean refresh,
1155                boolean noCleanup) throws OozieClientException {
1156            return new CoordRerun(jobId, rerunType, scope, refresh, noCleanup).call();
1157        }
1158    
1159        /**
1160         * Rerun bundle coordinators.
1161         *
1162         * @param jobId bundle jobId
1163         * @param coordScope rerun scope for coordinator jobs
1164         * @param dateScope rerun scope for date
1165         * @param refresh true if -refresh is given in command option
1166         * @param noCleanup true if -nocleanup is given in command option
1167         * @throws OozieClientException
1168         */
1169        public Void reRunBundle(String jobId, String coordScope, String dateScope, boolean refresh, boolean noCleanup)
1170                throws OozieClientException {
1171            return new BundleRerun(jobId, coordScope, dateScope, refresh, noCleanup).call();
1172        }
1173    
1174        /**
1175         * Return the info of the workflow jobs that match the filter.
1176         *
1177         * @param filter job filter. Refer to the {@link OozieClient} for the filter syntax.
1178         * @param start jobs offset, base 1.
1179         * @param len number of jobs to return.
1180         * @return a list with the workflow jobs info, without node details.
1181         * @throws OozieClientException thrown if the jobs info could not be retrieved.
1182         */
1183        public List<WorkflowJob> getJobsInfo(String filter, int start, int len) throws OozieClientException {
1184            return new JobsStatus(filter, start, len).call();
1185        }
1186    
1187        /**
1188         * Return the info of the workflow jobs that match the filter.
1189         * <p/>
1190         * It returns the first 100 jobs that match the filter.
1191         *
1192         * @param filter job filter. Refer to the {@link OozieClient} for the filter syntax.
1193         * @return a list with the workflow jobs info, without node details.
1194         * @throws OozieClientException thrown if the jobs info could not be retrieved.
1195         */
1196        public List<WorkflowJob> getJobsInfo(String filter) throws OozieClientException {
1197            return getJobsInfo(filter, 1, 50);
1198        }
1199    
1200        /**
1201         * Print sla info about coordinator and workflow jobs and actions.
1202         *
1203         * @param start starting offset
1204         * @param len number of results
1205         * @throws OozieClientException
1206         */
1207        public void getSlaInfo(int start, int len, String filter) throws OozieClientException {
1208            new SlaInfo(start, len, filter).call();
1209        }
1210    
1211        private class SlaInfo extends ClientCallable<Void> {
1212    
1213            SlaInfo(int start, int len, String filter) {
1214                super("GET", RestConstants.SLA, "", prepareParams(RestConstants.SLA_GT_SEQUENCE_ID,
1215                        Integer.toString(start), RestConstants.MAX_EVENTS, Integer.toString(len),
1216                        RestConstants.JOBS_FILTER_PARAM, filter));
1217            }
1218    
1219            @Override
1220            @SuppressWarnings("unchecked")
1221            protected Void call(HttpURLConnection conn) throws IOException, OozieClientException {
1222                conn.setRequestProperty("content-type", RestConstants.XML_CONTENT_TYPE);
1223                if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) {
1224                    BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
1225                    String line = null;
1226                    while ((line = br.readLine()) != null) {
1227                        System.out.println(line);
1228                    }
1229                }
1230                else {
1231                    handleError(conn);
1232                }
1233                return null;
1234            }
1235        }
1236    
1237        private class JobIdAction extends ClientCallable<String> {
1238    
1239            JobIdAction(String externalId) {
1240                super("GET", RestConstants.JOBS, "", prepareParams(RestConstants.JOBTYPE_PARAM, "wf",
1241                        RestConstants.JOBS_EXTERNAL_ID_PARAM, externalId));
1242            }
1243    
1244            @Override
1245            @SuppressWarnings("unchecked")
1246            protected String call(HttpURLConnection conn) throws IOException, OozieClientException {
1247                if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) {
1248                    Reader reader = new InputStreamReader(conn.getInputStream());
1249                    JSONObject json = (JSONObject) JSONValue.parse(reader);
1250                    return (String) json.get(JsonTags.JOB_ID);
1251                }
1252                else {
1253                    handleError(conn);
1254                }
1255                return null;
1256            }
1257        }
1258    
1259        /**
1260         * Return the workflow job Id for an external Id.
1261         * <p/>
1262         * The external Id must have provided at job creation time.
1263         *
1264         * @param externalId external Id given at job creation time.
1265         * @return the workflow job Id for an external Id, <code>null</code> if none.
1266         * @throws OozieClientException thrown if the operation could not be done.
1267         */
1268        public String getJobId(String externalId) throws OozieClientException {
1269            return new JobIdAction(externalId).call();
1270        }
1271    
1272        private class SetSystemMode extends ClientCallable<Void> {
1273    
1274            public SetSystemMode(SYSTEM_MODE status) {
1275                super("PUT", RestConstants.ADMIN, RestConstants.ADMIN_STATUS_RESOURCE, prepareParams(
1276                        RestConstants.ADMIN_SYSTEM_MODE_PARAM, status + ""));
1277            }
1278    
1279            @Override
1280            public Void call(HttpURLConnection conn) throws IOException, OozieClientException {
1281                if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) {
1282                    handleError(conn);
1283                }
1284                return null;
1285            }
1286        }
1287    
1288        /**
1289         * Enable or disable safe mode. Used by OozieCLI. In safe mode, Oozie would not accept any commands except status
1290         * command to change and view the safe mode status.
1291         *
1292         * @param status true to enable safe mode, false to disable safe mode.
1293         * @throws OozieClientException if it fails to set the safe mode status.
1294         */
1295        public void setSystemMode(SYSTEM_MODE status) throws OozieClientException {
1296            new SetSystemMode(status).call();
1297        }
1298    
1299        private class GetSystemMode extends ClientCallable<SYSTEM_MODE> {
1300    
1301            GetSystemMode() {
1302                super("GET", RestConstants.ADMIN, RestConstants.ADMIN_STATUS_RESOURCE, prepareParams());
1303            }
1304    
1305            @Override
1306            protected SYSTEM_MODE call(HttpURLConnection conn) throws IOException, OozieClientException {
1307                if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) {
1308                    Reader reader = new InputStreamReader(conn.getInputStream());
1309                    JSONObject json = (JSONObject) JSONValue.parse(reader);
1310                    return SYSTEM_MODE.valueOf((String) json.get(JsonTags.OOZIE_SYSTEM_MODE));
1311                }
1312                else {
1313                    handleError(conn);
1314                }
1315                return SYSTEM_MODE.NORMAL;
1316            }
1317        }
1318    
1319        /**
1320         * Returns if Oozie is in safe mode or not.
1321         *
1322         * @return true if safe mode is ON<br>
1323         *         false if safe mode is OFF
1324         * @throws OozieClientException throw if it could not obtain the safe mode status.
1325         */
1326        /*
1327         * public boolean isInSafeMode() throws OozieClientException { return new GetSafeMode().call(); }
1328         */
1329        public SYSTEM_MODE getSystemMode() throws OozieClientException {
1330            return new GetSystemMode().call();
1331        }
1332    
1333        private class GetBuildVersion extends ClientCallable<String> {
1334    
1335            GetBuildVersion() {
1336                super("GET", RestConstants.ADMIN, RestConstants.ADMIN_BUILD_VERSION_RESOURCE, prepareParams());
1337            }
1338    
1339            @Override
1340            protected String call(HttpURLConnection conn) throws IOException, OozieClientException {
1341                if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) {
1342                    Reader reader = new InputStreamReader(conn.getInputStream());
1343                    JSONObject json = (JSONObject) JSONValue.parse(reader);
1344                    return (String) json.get(JsonTags.BUILD_VERSION);
1345                }
1346                else {
1347                    handleError(conn);
1348                }
1349                return null;
1350            }
1351        }
1352    
1353        /**
1354         * Return the Oozie server build version.
1355         *
1356         * @return the Oozie server build version.
1357         * @throws OozieClientException throw if it the server build version could not be retrieved.
1358         */
1359        public String getServerBuildVersion() throws OozieClientException {
1360            return new GetBuildVersion().call();
1361        }
1362    
1363        /**
1364         * Return the Oozie client build version.
1365         *
1366         * @return the Oozie client build version.
1367         */
1368        public String getClientBuildVersion() {
1369            return BuildInfo.getBuildInfo().getProperty(BuildInfo.BUILD_VERSION);
1370        }
1371    
1372        /**
1373         * Return the info of the coordinator jobs that match the filter.
1374         *
1375         * @param filter job filter. Refer to the {@link OozieClient} for the filter syntax.
1376         * @param start jobs offset, base 1.
1377         * @param len number of jobs to return.
1378         * @return a list with the coordinator jobs info
1379         * @throws OozieClientException thrown if the jobs info could not be retrieved.
1380         */
1381        public List<CoordinatorJob> getCoordJobsInfo(String filter, int start, int len) throws OozieClientException {
1382            return new CoordJobsStatus(filter, start, len).call();
1383        }
1384    
1385        /**
1386         * Return the info of the bundle jobs that match the filter.
1387         *
1388         * @param filter job filter. Refer to the {@link OozieClient} for the filter syntax.
1389         * @param start jobs offset, base 1.
1390         * @param len number of jobs to return.
1391         * @return a list with the bundle jobs info
1392         * @throws OozieClientException thrown if the jobs info could not be retrieved.
1393         */
1394        public List<BundleJob> getBundleJobsInfo(String filter, int start, int len) throws OozieClientException {
1395            return new BundleJobsStatus(filter, start, len).call();
1396        }
1397    
1398        public List<BulkResponse> getBulkInfo(String filter, int start, int len) throws OozieClientException {
1399            return new BulkResponseStatus(filter, start, len).call();
1400        }
1401    
1402        private class GetQueueDump extends ClientCallable<List<String>> {
1403            GetQueueDump() {
1404                super("GET", RestConstants.ADMIN, RestConstants.ADMIN_QUEUE_DUMP_RESOURCE, prepareParams());
1405            }
1406    
1407            @Override
1408            protected List<String> call(HttpURLConnection conn) throws IOException, OozieClientException {
1409                if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) {
1410                    Reader reader = new InputStreamReader(conn.getInputStream());
1411                    JSONObject json = (JSONObject) JSONValue.parse(reader);
1412                    JSONArray queueDumpArray = (JSONArray) json.get(JsonTags.QUEUE_DUMP);
1413    
1414                    List<String> list = new ArrayList<String>();
1415                    list.add("[Server Queue Dump]:");
1416                    for (Object o : queueDumpArray) {
1417                        JSONObject entry = (JSONObject) o;
1418                        if (entry.get(JsonTags.CALLABLE_DUMP) != null) {
1419                            String value = (String) entry.get(JsonTags.CALLABLE_DUMP);
1420                            list.add(value);
1421                        }
1422                    }
1423                    if (queueDumpArray.size() == 0) {
1424                        list.add("Queue dump is null!");
1425                    }
1426    
1427                    list.add("******************************************");
1428                    list.add("[Server Uniqueness Map Dump]:");
1429    
1430                    JSONArray uniqueDumpArray = (JSONArray) json.get(JsonTags.UNIQUE_MAP_DUMP);
1431                    for (Object o : uniqueDumpArray) {
1432                        JSONObject entry = (JSONObject) o;
1433                        if (entry.get(JsonTags.UNIQUE_ENTRY_DUMP) != null) {
1434                            String value = (String) entry.get(JsonTags.UNIQUE_ENTRY_DUMP);
1435                            list.add(value);
1436                        }
1437                    }
1438                    if (uniqueDumpArray.size() == 0) {
1439                        list.add("Uniqueness dump is null!");
1440                    }
1441                    return list;
1442                }
1443                else {
1444                    handleError(conn);
1445                }
1446                return null;
1447            }
1448        }
1449    
1450        /**
1451         * Return the Oozie queue's commands' dump
1452         *
1453         * @return the list of strings of callable identification in queue
1454         * @throws OozieClientException throw if it the queue dump could not be retrieved.
1455         */
1456        public List<String> getQueueDump() throws OozieClientException {
1457            return new GetQueueDump().call();
1458        }
1459    
1460        /**
1461         * Check if the string is not null or not empty.
1462         *
1463         * @param str
1464         * @param name
1465         * @return string
1466         */
1467        public static String notEmpty(String str, String name) {
1468            if (str == null) {
1469                throw new IllegalArgumentException(name + " cannot be null");
1470            }
1471            if (str.length() == 0) {
1472                throw new IllegalArgumentException(name + " cannot be empty");
1473            }
1474            return str;
1475        }
1476    
1477        /**
1478         * Check if the object is not null.
1479         *
1480         * @param <T>
1481         * @param obj
1482         * @param name
1483         * @return string
1484         */
1485        public static <T> T notNull(T obj, String name) {
1486            if (obj == null) {
1487                throw new IllegalArgumentException(name + " cannot be null");
1488            }
1489            return obj;
1490        }
1491    
1492    }