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