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