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