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 */
018package org.apache.oozie.client;
019
020import org.apache.oozie.BuildInfo;
021import org.apache.oozie.client.rest.JsonTags;
022import org.apache.oozie.client.rest.JsonToBean;
023import org.apache.oozie.client.rest.RestConstants;
024import org.apache.oozie.client.retry.ConnectionRetriableClient;
025import org.json.simple.JSONArray;
026import org.json.simple.JSONObject;
027import org.json.simple.JSONValue;
028import org.w3c.dom.Document;
029import org.w3c.dom.Element;
030
031import javax.xml.parsers.DocumentBuilderFactory;
032import javax.xml.transform.Transformer;
033import javax.xml.transform.TransformerFactory;
034import javax.xml.transform.dom.DOMSource;
035import javax.xml.transform.stream.StreamResult;
036import java.io.BufferedReader;
037import java.io.IOException;
038import java.io.InputStream;
039import java.io.InputStreamReader;
040import java.io.OutputStream;
041import java.io.PrintStream;
042import java.io.Reader;
043import java.net.HttpURLConnection;
044import java.net.URL;
045import java.net.URLEncoder;
046import java.util.ArrayList;
047import java.util.Collections;
048import java.util.HashMap;
049import java.util.Iterator;
050import java.util.LinkedHashMap;
051import java.util.List;
052import java.util.Map;
053import java.util.Properties;
054import java.util.concurrent.Callable;
055
056/**
057 * Client API to submit and manage Oozie workflow jobs against an Oozie intance.
058 * <p/>
059 * This class is thread safe.
060 * <p/>
061 * Syntax for filter for the {@link #getJobsInfo(String)} {@link #getJobsInfo(String, int, int)} methods:
062 * <code>[NAME=VALUE][;NAME=VALUE]*</code>.
063 * <p/>
064 * Valid filter names are:
065 * <p/>
066 * <ul/>
067 * <li>name: the workflow application name from the workflow definition.</li>
068 * <li>user: the user that submitted the job.</li>
069 * <li>group: the group for the job.</li>
070 * <li>status: the status of the job.</li>
071 * </ul>
072 * <p/>
073 * The query will do an AND among all the filter names. The query will do an OR among all the filter values for the same
074 * name. Multiple values must be specified as different name value pairs.
075 */
076public class OozieClient {
077
078    public static final long WS_PROTOCOL_VERSION_0 = 0;
079
080    public static final long WS_PROTOCOL_VERSION_1 = 1;
081
082    public static final long WS_PROTOCOL_VERSION = 2; // pointer to current version
083
084    public static final String USER_NAME = "user.name";
085
086    @Deprecated
087    public static final String GROUP_NAME = "group.name";
088
089    public static final String JOB_ACL = "oozie.job.acl";
090
091    public static final String APP_PATH = "oozie.wf.application.path";
092
093    public static final String COORDINATOR_APP_PATH = "oozie.coord.application.path";
094
095    public static final String BUNDLE_APP_PATH = "oozie.bundle.application.path";
096
097    public static final String BUNDLE_ID = "oozie.bundle.id";
098
099    public static final String EXTERNAL_ID = "oozie.wf.external.id";
100
101    public static final String WORKFLOW_NOTIFICATION_URL = "oozie.wf.workflow.notification.url";
102
103    public static final String ACTION_NOTIFICATION_URL = "oozie.wf.action.notification.url";
104
105    public static final String COORD_ACTION_NOTIFICATION_URL = "oozie.coord.action.notification.url";
106
107    public static final String RERUN_SKIP_NODES = "oozie.wf.rerun.skip.nodes";
108
109    public static final String RERUN_FAIL_NODES = "oozie.wf.rerun.failnodes";
110
111    public static final String LOG_TOKEN = "oozie.wf.log.token";
112
113    public static final String ACTION_MAX_RETRIES = "oozie.wf.action.max.retries";
114
115    public static final String ACTION_RETRY_INTERVAL = "oozie.wf.action.retry.interval";
116
117    public static final String FILTER_USER = "user";
118
119    public static final String FILTER_GROUP = "group";
120
121    public static final String FILTER_NAME = "name";
122
123    public static final String FILTER_STATUS = "status";
124
125    public static final String FILTER_NOMINAL_TIME = "nominaltime";
126
127    public static final String FILTER_FREQUENCY = "frequency";
128
129    public static final String FILTER_ID = "id";
130
131    public static final String FILTER_UNIT = "unit";
132
133    public static final String FILTER_JOBID = "jobid";
134
135    public static final String FILTER_APPNAME = "appname";
136
137    public static final String FILTER_SLA_APPNAME = "app_name";
138
139    public static final String FILTER_SLA_ID = "id";
140
141    public static final String FILTER_SLA_PARENT_ID = "parent_id";
142
143    public static final String FILTER_SLA_NOMINAL_START = "nominal_start";
144
145    public static final String FILTER_SLA_NOMINAL_END = "nominal_end";
146
147    public static final String CHANGE_VALUE_ENDTIME = "endtime";
148
149    public static final String CHANGE_VALUE_PAUSETIME = "pausetime";
150
151    public static final String CHANGE_VALUE_CONCURRENCY = "concurrency";
152
153    public static final String CHANGE_VALUE_STATUS = "status";
154
155    public static final String LIBPATH = "oozie.libpath";
156
157    public static final String USE_SYSTEM_LIBPATH = "oozie.use.system.libpath";
158
159    public static final String OOZIE_SUSPEND_ON_NODES = "oozie.suspend.on.nodes";
160
161    public static enum SYSTEM_MODE {
162        NORMAL, NOWEBSERVICE, SAFEMODE
163    }
164
165    /**
166     * debugMode =0 means no debugging. > 0 means debugging on.
167     */
168    public int debugMode = 0;
169
170    private int retryCount = 4;
171
172
173    private String baseUrl;
174    private String protocolUrl;
175    private boolean validatedVersion = false;
176    private JSONArray supportedVersions;
177    private final Map<String, String> headers = new HashMap<String, String>();
178
179    private static final ThreadLocal<String> USER_NAME_TL = new ThreadLocal<String>();
180
181    /**
182     * Allows to impersonate other users in the Oozie server. The current user
183     * must be configured as a proxyuser in Oozie.
184     * <p/>
185     * IMPORTANT: impersonation happens only with Oozie client requests done within
186     * doAs() calls.
187     *
188     * @param userName user to impersonate.
189     * @param callable callable with {@link OozieClient} calls impersonating the specified user.
190     * @return any response returned by the {@link Callable#call()} method.
191     * @throws Exception thrown by the {@link Callable#call()} method.
192     */
193    public static <T> T doAs(String userName, Callable<T> callable) throws Exception {
194        notEmpty(userName, "userName");
195        notNull(callable, "callable");
196        try {
197            USER_NAME_TL.set(userName);
198            return callable.call();
199        }
200        finally {
201            USER_NAME_TL.remove();
202        }
203    }
204
205    protected OozieClient() {
206    }
207
208    /**
209     * Create a Workflow client instance.
210     *
211     * @param oozieUrl URL of the Oozie instance it will interact with.
212     */
213    public OozieClient(String oozieUrl) {
214        this.baseUrl = notEmpty(oozieUrl, "oozieUrl");
215        if (!this.baseUrl.endsWith("/")) {
216            this.baseUrl += "/";
217        }
218    }
219
220    /**
221     * Return the Oozie URL of the workflow client instance.
222     * <p/>
223     * This URL is the base URL fo the Oozie system, with not protocol versioning.
224     *
225     * @return the Oozie URL of the workflow client instance.
226     */
227    public String getOozieUrl() {
228        return baseUrl;
229    }
230
231    /**
232     * Return the Oozie URL used by the client and server for WS communications.
233     * <p/>
234     * This URL is the original URL plus the versioning element path.
235     *
236     * @return the Oozie URL used by the client and server for communication.
237     * @throws OozieClientException thrown in the client and the server are not protocol compatible.
238     */
239    public String getProtocolUrl() throws OozieClientException {
240        validateWSVersion();
241        return protocolUrl;
242    }
243
244    /**
245     * @return current debug Mode
246     */
247    public int getDebugMode() {
248        return debugMode;
249    }
250
251    /**
252     * Set debug mode.
253     *
254     * @param debugMode : 0 means no debugging. > 0 means debugging
255     */
256    public void setDebugMode(int debugMode) {
257        this.debugMode = debugMode;
258    }
259
260    public int getRetryCount() {
261        return retryCount;
262    }
263
264
265    public void setRetryCount(int retryCount) {
266        this.retryCount = retryCount;
267    }
268
269    private String getBaseURLForVersion(long protocolVersion) throws OozieClientException {
270        try {
271            if (supportedVersions == null) {
272                supportedVersions = getSupportedProtocolVersions();
273            }
274            if (supportedVersions == null) {
275                throw new OozieClientException("HTTP error", "no response message");
276            }
277            if (supportedVersions.contains(protocolVersion)) {
278                return baseUrl + "v" + protocolVersion + "/";
279            }
280            else {
281                throw new OozieClientException(OozieClientException.UNSUPPORTED_VERSION, "Protocol version "
282                        + protocolVersion + " is not supported");
283            }
284        }
285        catch (IOException e) {
286            throw new OozieClientException(OozieClientException.IO_ERROR, e);
287        }
288    }
289
290    /**
291     * Validate that the Oozie client and server instances are protocol compatible.
292     *
293     * @throws OozieClientException thrown in the client and the server are not protocol compatible.
294     */
295    public synchronized void validateWSVersion() throws OozieClientException {
296        if (!validatedVersion) {
297            try {
298                supportedVersions = getSupportedProtocolVersions();
299                if (supportedVersions == null) {
300                    throw new OozieClientException("HTTP error", "no response message");
301                }
302                if (!supportedVersions.contains(WS_PROTOCOL_VERSION)
303                        && !supportedVersions.contains(WS_PROTOCOL_VERSION_1)
304                        && !supportedVersions.contains(WS_PROTOCOL_VERSION_0)) {
305                    StringBuilder msg = new StringBuilder();
306                    msg.append("Supported version [").append(WS_PROTOCOL_VERSION)
307                            .append("] or less, Unsupported versions[");
308                    String separator = "";
309                    for (Object version : supportedVersions) {
310                        msg.append(separator).append(version);
311                    }
312                    msg.append("]");
313                    throw new OozieClientException(OozieClientException.UNSUPPORTED_VERSION, msg.toString());
314                }
315                if (supportedVersions.contains(WS_PROTOCOL_VERSION)) {
316                    protocolUrl = baseUrl + "v" + WS_PROTOCOL_VERSION + "/";
317                }
318                else if (supportedVersions.contains(WS_PROTOCOL_VERSION_1)) {
319                    protocolUrl = baseUrl + "v" + WS_PROTOCOL_VERSION_1 + "/";
320                }
321                else {
322                    if (supportedVersions.contains(WS_PROTOCOL_VERSION_0)) {
323                        protocolUrl = baseUrl + "v" + WS_PROTOCOL_VERSION_0 + "/";
324                    }
325                }
326            }
327            catch (IOException ex) {
328                throw new OozieClientException(OozieClientException.IO_ERROR, ex);
329            }
330            validatedVersion = true;
331        }
332    }
333
334    private JSONArray getSupportedProtocolVersions() throws IOException, OozieClientException {
335        JSONArray versions = null;
336        final URL url = new URL(baseUrl + RestConstants.VERSIONS);
337
338        HttpURLConnection conn = createRetryableConnection(url, "GET");
339
340        if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
341            versions = (JSONArray) JSONValue.parse(new InputStreamReader(conn.getInputStream()));
342        }
343        else {
344            handleError(conn);
345        }
346        return versions;
347    }
348
349    /**
350     * Create an empty configuration with just the {@link #USER_NAME} set to the JVM user name.
351     *
352     * @return an empty configuration.
353     */
354    public Properties createConfiguration() {
355        Properties conf = new Properties();
356        String userName = USER_NAME_TL.get();
357        if (userName == null) {
358            userName = System.getProperty("user.name");
359        }
360        conf.setProperty(USER_NAME, userName);
361        return conf;
362    }
363
364    /**
365     * Set a HTTP header to be used in the WS requests by the workflow instance.
366     *
367     * @param name header name.
368     * @param value header value.
369     */
370    public void setHeader(String name, String value) {
371        headers.put(notEmpty(name, "name"), notNull(value, "value"));
372    }
373
374    /**
375     * Get the value of a set HTTP header from the workflow instance.
376     *
377     * @param name header name.
378     * @return header value, <code>null</code> if not set.
379     */
380    public String getHeader(String name) {
381        return headers.get(notEmpty(name, "name"));
382    }
383
384    /**
385     * Get the set HTTP header
386     *
387     * @return map of header key and value
388     */
389    public Map<String, String> getHeaders() {
390        return headers;
391    }
392
393    /**
394     * Remove a HTTP header from the workflow client instance.
395     *
396     * @param name header name.
397     */
398    public void removeHeader(String name) {
399        headers.remove(notEmpty(name, "name"));
400    }
401
402    /**
403     * Return an iterator with all the header names set in the workflow instance.
404     *
405     * @return header names.
406     */
407    public Iterator<String> getHeaderNames() {
408        return Collections.unmodifiableMap(headers).keySet().iterator();
409    }
410
411    private URL createURL(Long protocolVersion, String collection, String resource, Map<String, String> parameters)
412            throws IOException, OozieClientException {
413        validateWSVersion();
414        StringBuilder sb = new StringBuilder();
415        if (protocolVersion == null) {
416            sb.append(protocolUrl);
417        }
418        else {
419            sb.append(getBaseURLForVersion(protocolVersion));
420        }
421        sb.append(collection);
422        if (resource != null && resource.length() > 0) {
423            sb.append("/").append(resource);
424        }
425        if (parameters.size() > 0) {
426            String separator = "?";
427            for (Map.Entry<String, String> param : parameters.entrySet()) {
428                if (param.getValue() != null) {
429                    sb.append(separator).append(URLEncoder.encode(param.getKey(), "UTF-8")).append("=").append(
430                            URLEncoder.encode(param.getValue(), "UTF-8"));
431                    separator = "&";
432                }
433            }
434        }
435        return new URL(sb.toString());
436    }
437
438    private boolean validateCommand(String url) {
439        {
440            if (protocolUrl.contains(baseUrl + "v0")) {
441                if (url.contains("dryrun") || url.contains("jobtype=c") || url.contains("systemmode")) {
442                    return false;
443                }
444            }
445        }
446        return true;
447    }
448    /**
449     * Create retryable http connection to oozie server.
450     *
451     * @param url
452     * @param method
453     * @return connection
454     * @throws IOException
455     * @throws OozieClientException
456     */
457    protected HttpURLConnection createRetryableConnection(final URL url, final String method) throws IOException{
458        return (HttpURLConnection) new ConnectionRetriableClient(getRetryCount()) {
459            @Override
460            public Object doExecute(URL url, String method) throws IOException, OozieClientException {
461                HttpURLConnection conn = createConnection(url, method);
462                return conn;
463            }
464        }.execute(url, method);
465    }
466
467    /**
468     * Create http connection to oozie server.
469     *
470     * @param url
471     * @param method
472     * @return connection
473     * @throws IOException
474     * @throws OozieClientException
475     */
476    protected HttpURLConnection createConnection(URL url, String method) throws IOException, OozieClientException {
477        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
478        conn.setRequestMethod(method);
479        if (method.equals("POST") || method.equals("PUT")) {
480            conn.setDoOutput(true);
481        }
482        for (Map.Entry<String, String> header : headers.entrySet()) {
483            conn.setRequestProperty(header.getKey(), header.getValue());
484        }
485        return conn;
486    }
487
488    protected abstract class ClientCallable<T> implements Callable<T> {
489        private final String method;
490        private final String collection;
491        private final String resource;
492        private final Map<String, String> params;
493        private final Long protocolVersion;
494
495        public ClientCallable(String method, String collection, String resource, Map<String, String> params) {
496            this(method, null, collection, resource, params);
497        }
498
499        public ClientCallable(String method, Long protocolVersion, String collection, String resource, Map<String, String> params) {
500            this.method = method;
501            this.protocolVersion = protocolVersion;
502            this.collection = collection;
503            this.resource = resource;
504            this.params = params;
505        }
506
507        public T call() throws OozieClientException {
508            try {
509                URL url = createURL(protocolVersion, collection, resource, params);
510                if (validateCommand(url.toString())) {
511                    if (getDebugMode() > 0) {
512                        System.out.println(method + " " + url);
513                    }
514                    return call(createRetryableConnection(url, method));
515                }
516                else {
517                    System.out.println("Option not supported in target server. Supported only on Oozie-2.0 or greater."
518                            + " Use 'oozie help' for details");
519                    throw new OozieClientException(OozieClientException.UNSUPPORTED_VERSION, new Exception());
520                }
521            }
522            catch (IOException ex) {
523                throw new OozieClientException(OozieClientException.IO_ERROR, ex);
524            }
525        }
526
527        protected abstract T call(HttpURLConnection conn) throws IOException, OozieClientException;
528    }
529
530    static void handleError(HttpURLConnection conn) throws IOException, OozieClientException {
531        int status = conn.getResponseCode();
532        String error = conn.getHeaderField(RestConstants.OOZIE_ERROR_CODE);
533        String message = conn.getHeaderField(RestConstants.OOZIE_ERROR_MESSAGE);
534
535        if (error == null) {
536            error = "HTTP error code: " + status;
537        }
538
539        if (message == null) {
540            message = conn.getResponseMessage();
541        }
542        throw new OozieClientException(error, message);
543    }
544
545    static Map<String, String> prepareParams(String... params) {
546        Map<String, String> map = new LinkedHashMap<String, String>();
547        for (int i = 0; i < params.length; i = i + 2) {
548            map.put(params[i], params[i + 1]);
549        }
550        String doAsUserName = USER_NAME_TL.get();
551        if (doAsUserName != null) {
552            map.put(RestConstants.DO_AS_PARAM, doAsUserName);
553        }
554        return map;
555    }
556
557    public void writeToXml(Properties props, OutputStream out) throws IOException {
558        try {
559            Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
560            Element conf = doc.createElement("configuration");
561            doc.appendChild(conf);
562            conf.appendChild(doc.createTextNode("\n"));
563            for (String name : props.stringPropertyNames()) { // Properties whose key or value is not of type String are omitted.
564                String value = props.getProperty(name);
565                Element propNode = doc.createElement("property");
566                conf.appendChild(propNode);
567
568                Element nameNode = doc.createElement("name");
569                nameNode.appendChild(doc.createTextNode(name.trim()));
570                propNode.appendChild(nameNode);
571
572                Element valueNode = doc.createElement("value");
573                valueNode.appendChild(doc.createTextNode(value.trim()));
574                propNode.appendChild(valueNode);
575
576                conf.appendChild(doc.createTextNode("\n"));
577            }
578
579            DOMSource source = new DOMSource(doc);
580            StreamResult result = new StreamResult(out);
581            TransformerFactory transFactory = TransformerFactory.newInstance();
582            Transformer transformer = transFactory.newTransformer();
583            transformer.transform(source, result);
584            if (getDebugMode() > 0) {
585                result = new StreamResult(System.out);
586                transformer.transform(source, result);
587                System.out.println();
588            }
589        }
590        catch (Exception e) {
591            throw new IOException(e);
592        }
593    }
594
595    private class JobSubmit extends ClientCallable<String> {
596        private final Properties conf;
597
598        JobSubmit(Properties conf, boolean start) {
599            super("POST", RestConstants.JOBS, "", (start) ? prepareParams(RestConstants.ACTION_PARAM,
600                    RestConstants.JOB_ACTION_START) : prepareParams());
601            this.conf = notNull(conf, "conf");
602        }
603
604        JobSubmit(String jobId, Properties conf) {
605            super("PUT", RestConstants.JOB, notEmpty(jobId, "jobId"), prepareParams(RestConstants.ACTION_PARAM,
606                    RestConstants.JOB_ACTION_RERUN));
607            this.conf = notNull(conf, "conf");
608        }
609
610        public JobSubmit(Properties conf, String jobActionDryrun) {
611            super("POST", RestConstants.JOBS, "", prepareParams(RestConstants.ACTION_PARAM,
612                    RestConstants.JOB_ACTION_DRYRUN));
613            this.conf = notNull(conf, "conf");
614        }
615
616        @Override
617        protected String call(HttpURLConnection conn) throws IOException, OozieClientException {
618            conn.setRequestProperty("content-type", RestConstants.XML_CONTENT_TYPE);
619            writeToXml(conf, conn.getOutputStream());
620            if (conn.getResponseCode() == HttpURLConnection.HTTP_CREATED) {
621                JSONObject json = (JSONObject) JSONValue.parse(new InputStreamReader(conn.getInputStream()));
622                return (String) json.get(JsonTags.JOB_ID);
623            }
624            if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) {
625                handleError(conn);
626            }
627            return null;
628        }
629    }
630
631    /**
632     * Submit a workflow job.
633     *
634     * @param conf job configuration.
635     * @return the job Id.
636     * @throws OozieClientException thrown if the job could not be submitted.
637     */
638    public String submit(Properties conf) throws OozieClientException {
639        return (new JobSubmit(conf, false)).call();
640    }
641
642    private class JobAction extends ClientCallable<Void> {
643
644        JobAction(String jobId, String action) {
645            super("PUT", RestConstants.JOB, notEmpty(jobId, "jobId"), prepareParams(RestConstants.ACTION_PARAM, action));
646        }
647
648        JobAction(String jobId, String action, String params) {
649            super("PUT", RestConstants.JOB, notEmpty(jobId, "jobId"), prepareParams(RestConstants.ACTION_PARAM, action,
650                    RestConstants.JOB_CHANGE_VALUE, params));
651        }
652
653        @Override
654        protected Void call(HttpURLConnection conn) throws IOException, OozieClientException {
655            if (!(conn.getResponseCode() == HttpURLConnection.HTTP_OK)) {
656                handleError(conn);
657            }
658            return null;
659        }
660    }
661
662    /**
663     * Update coord definition.
664     *
665     * @param jobId the job id
666     * @param conf the conf
667     * @param dryrun the dryrun
668     * @param showDiff the show diff
669     * @return the string
670     * @throws OozieClientException the oozie client exception
671     */
672    public String updateCoord(String jobId, Properties conf, String dryrun, String showDiff)
673            throws OozieClientException {
674        return (new UpdateCoord(jobId, conf, dryrun, showDiff)).call();
675    }
676
677    /**
678     * Update coord definition without properties.
679     *
680     * @param jobId the job id
681     * @param dryrun the dryrun
682     * @param showDiff the show diff
683     * @return the string
684     * @throws OozieClientException the oozie client exception
685     */
686    public String updateCoord(String jobId, String dryrun, String showDiff) throws OozieClientException {
687        return (new UpdateCoord(jobId, dryrun, showDiff)).call();
688    }
689
690    /**
691     * The Class UpdateCoord.
692     */
693    private class UpdateCoord extends ClientCallable<String> {
694        private final Properties conf;
695
696        public UpdateCoord(String jobId, Properties conf, String jobActionDryrun, String showDiff) {
697            super("PUT", RestConstants.JOB, notEmpty(jobId, "jobId"), prepareParams(RestConstants.ACTION_PARAM,
698                    RestConstants.JOB_COORD_UPDATE, RestConstants.JOB_ACTION_DRYRUN, jobActionDryrun,
699                    RestConstants.JOB_ACTION_SHOWDIFF, showDiff));
700            this.conf = conf;
701        }
702
703        public UpdateCoord(String jobId, String jobActionDryrun, String showDiff) {
704            this(jobId, new Properties(), jobActionDryrun, showDiff);
705        }
706
707        @Override
708        protected String call(HttpURLConnection conn) throws IOException, OozieClientException {
709            conn.setRequestProperty("content-type", RestConstants.XML_CONTENT_TYPE);
710            writeToXml(conf, conn.getOutputStream());
711
712            if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
713                JSONObject json = (JSONObject) JSONValue.parse(new InputStreamReader(conn.getInputStream()));
714                JSONObject update = (JSONObject) json.get(JsonTags.COORD_UPDATE);
715                if (update != null) {
716                    return (String) update.get(JsonTags.COORD_UPDATE_DIFF);
717                }
718                else {
719                    return "";
720                }
721            }
722            if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) {
723                handleError(conn);
724            }
725            return null;
726        }
727    }
728
729    /**
730     * dryrun for a given job
731     *
732     * @param conf Job configuration.
733     */
734    public String dryrun(Properties conf) throws OozieClientException {
735        return new JobSubmit(conf, RestConstants.JOB_ACTION_DRYRUN).call();
736    }
737
738    /**
739     * Start a workflow job.
740     *
741     * @param jobId job Id.
742     * @throws OozieClientException thrown if the job could not be started.
743     */
744    public void start(String jobId) throws OozieClientException {
745        new JobAction(jobId, RestConstants.JOB_ACTION_START).call();
746    }
747
748    /**
749     * Submit and start a workflow job.
750     *
751     * @param conf job configuration.
752     * @return the job Id.
753     * @throws OozieClientException thrown if the job could not be submitted.
754     */
755    public String run(Properties conf) throws OozieClientException {
756        return (new JobSubmit(conf, true)).call();
757    }
758
759    /**
760     * Rerun a workflow job.
761     *
762     * @param jobId job Id to rerun.
763     * @param conf configuration information for the rerun.
764     * @throws OozieClientException thrown if the job could not be started.
765     */
766    public void reRun(String jobId, Properties conf) throws OozieClientException {
767        new JobSubmit(jobId, conf).call();
768    }
769
770    /**
771     * Suspend a workflow job.
772     *
773     * @param jobId job Id.
774     * @throws OozieClientException thrown if the job could not be suspended.
775     */
776    public void suspend(String jobId) throws OozieClientException {
777        new JobAction(jobId, RestConstants.JOB_ACTION_SUSPEND).call();
778    }
779
780    /**
781     * Resume a workflow job.
782     *
783     * @param jobId job Id.
784     * @throws OozieClientException thrown if the job could not be resume.
785     */
786    public void resume(String jobId) throws OozieClientException {
787        new JobAction(jobId, RestConstants.JOB_ACTION_RESUME).call();
788    }
789
790    /**
791     * Kill a workflow/coord/bundle job.
792     *
793     * @param jobId job Id.
794     * @throws OozieClientException thrown if the job could not be killed.
795     */
796    public void kill(String jobId) throws OozieClientException {
797        new JobAction(jobId, RestConstants.JOB_ACTION_KILL).call();
798    }
799
800    /**
801     * Kill coordinator actions
802     * @param jobId coordinator Job Id
803     * @param rangeType type 'date' if -date is used, 'action-num' if -action is used
804     * @param scope kill scope for date or action nums
805     * @return list of coordinator actions that underwent kill
806     * @throws OozieClientException thrown if some actions could not be killed.
807     */
808    public List<CoordinatorAction> kill(String jobId, String rangeType, String scope) throws OozieClientException {
809        return new CoordActionsKill(jobId, rangeType, scope).call();
810    }
811
812    /**
813     * Change a coordinator job.
814     *
815     * @param jobId job Id.
816     * @param changeValue change value.
817     * @throws OozieClientException thrown if the job could not be changed.
818     */
819    public void change(String jobId, String changeValue) throws OozieClientException {
820        new JobAction(jobId, RestConstants.JOB_ACTION_CHANGE, changeValue).call();
821    }
822
823    /**
824     * Ignore a coordinator job.
825     *
826     * @param jobId coord job Id.
827     * @param scope list of coord actions to be ignored
828     * @throws OozieClientException thrown if the job could not be changed.
829     */
830    public List<CoordinatorAction> ignore(String jobId, String scope) throws OozieClientException {
831        return new CoordIgnore(jobId, RestConstants.JOB_COORD_SCOPE_ACTION, scope).call();
832    }
833
834    private class JobInfo extends ClientCallable<WorkflowJob> {
835
836        JobInfo(String jobId, int start, int len) {
837            super("GET", RestConstants.JOB, notEmpty(jobId, "jobId"), prepareParams(RestConstants.JOB_SHOW_PARAM,
838                    RestConstants.JOB_SHOW_INFO, RestConstants.OFFSET_PARAM, Integer.toString(start),
839                    RestConstants.LEN_PARAM, Integer.toString(len)));
840        }
841
842        @Override
843        protected WorkflowJob call(HttpURLConnection conn) throws IOException, OozieClientException {
844            if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) {
845                Reader reader = new InputStreamReader(conn.getInputStream());
846                JSONObject json = (JSONObject) JSONValue.parse(reader);
847                return JsonToBean.createWorkflowJob(json);
848            }
849            else {
850                handleError(conn);
851            }
852            return null;
853        }
854    }
855
856    private class JMSInfo extends ClientCallable<JMSConnectionInfo> {
857
858        JMSInfo() {
859            super("GET", RestConstants.ADMIN, RestConstants.ADMIN_JMS_INFO, prepareParams());
860        }
861
862        protected JMSConnectionInfo call(HttpURLConnection conn) throws IOException, OozieClientException {
863            if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) {
864                Reader reader = new InputStreamReader(conn.getInputStream());
865                JSONObject json = (JSONObject) JSONValue.parse(reader);
866                return JsonToBean.createJMSConnectionInfo(json);
867            }
868            else {
869                handleError(conn);
870            }
871            return null;
872        }
873    }
874
875    private class WorkflowActionInfo extends ClientCallable<WorkflowAction> {
876        WorkflowActionInfo(String actionId) {
877            super("GET", RestConstants.JOB, notEmpty(actionId, "id"), prepareParams(RestConstants.JOB_SHOW_PARAM,
878                    RestConstants.JOB_SHOW_INFO));
879        }
880
881        @Override
882        protected WorkflowAction call(HttpURLConnection conn) throws IOException, OozieClientException {
883            if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) {
884                Reader reader = new InputStreamReader(conn.getInputStream());
885                JSONObject json = (JSONObject) JSONValue.parse(reader);
886                return JsonToBean.createWorkflowAction(json);
887            }
888            else {
889                handleError(conn);
890            }
891            return null;
892        }
893    }
894
895    /**
896     * Get the info of a workflow job.
897     *
898     * @param jobId job Id.
899     * @return the job info.
900     * @throws OozieClientException thrown if the job info could not be retrieved.
901     */
902    public WorkflowJob getJobInfo(String jobId) throws OozieClientException {
903        return getJobInfo(jobId, 0, 0);
904    }
905
906    /**
907     * Get the JMS Connection info
908     * @return JMSConnectionInfo object
909     * @throws OozieClientException
910     */
911    public JMSConnectionInfo getJMSConnectionInfo() throws OozieClientException {
912        return new JMSInfo().call();
913    }
914
915    /**
916     * Get the info of a workflow job and subset actions.
917     *
918     * @param jobId job Id.
919     * @param start starting index in the list of actions belonging to the job
920     * @param len number of actions to be returned
921     * @return the job info.
922     * @throws OozieClientException thrown if the job info could not be retrieved.
923     */
924    public WorkflowJob getJobInfo(String jobId, int start, int len) throws OozieClientException {
925        return new JobInfo(jobId, start, len).call();
926    }
927
928    /**
929     * Get the info of a workflow action.
930     *
931     * @param actionId Id.
932     * @return the workflow action info.
933     * @throws OozieClientException thrown if the job info could not be retrieved.
934     */
935    public WorkflowAction getWorkflowActionInfo(String actionId) throws OozieClientException {
936        return new WorkflowActionInfo(actionId).call();
937    }
938
939    /**
940     * Get the log of a workflow job.
941     *
942     * @param jobId job Id.
943     * @return the job log.
944     * @throws OozieClientException thrown if the job info could not be retrieved.
945     */
946    public String getJobLog(String jobId) throws OozieClientException {
947        return new JobLog(jobId).call();
948    }
949
950    /**
951     * Get the log of a job.
952     *
953     * @param jobId job Id.
954     * @param logRetrievalType Based on which filter criteria the log is retrieved
955     * @param logRetrievalScope Value for the retrieval type
956     * @param logFilter log filter
957     * @param ps Printstream of command line interface
958     * @throws OozieClientException thrown if the job info could not be retrieved.
959     */
960    public void getJobLog(String jobId, String logRetrievalType, String logRetrievalScope, String logFilter,
961            PrintStream ps) throws OozieClientException {
962        new JobLog(jobId, logRetrievalType, logRetrievalScope, logFilter, ps).call();
963    }
964
965    /**
966     * Get the log of a job.
967     *
968     * @param jobId job Id.
969     * @param logRetrievalType Based on which filter criteria the log is retrieved
970     * @param logRetrievalScope Value for the retrieval type
971     * @param ps Printstream of command line interface
972     * @throws OozieClientException thrown if the job info could not be retrieved.
973     */
974    public void getJobLog(String jobId, String logRetrievalType, String logRetrievalScope, PrintStream ps)
975            throws OozieClientException {
976        getJobLog(jobId, logRetrievalType, logRetrievalScope, null, ps);
977    }
978
979    private class JobLog extends JobMetadata {
980        JobLog(String jobId) {
981            super(jobId, RestConstants.JOB_SHOW_LOG);
982        }
983        JobLog(String jobId, String logRetrievalType, String logRetrievalScope, String logFilter, PrintStream ps) {
984            super(jobId, logRetrievalType, logRetrievalScope, RestConstants.JOB_SHOW_LOG, logFilter, ps);
985        }
986    }
987
988    /**
989     * Gets the JMS topic name for a particular job
990     * @param jobId given jobId
991     * @return the JMS topic name
992     * @throws OozieClientException
993     */
994    public String getJMSTopicName(String jobId) throws OozieClientException {
995        return new JMSTopic(jobId).call();
996    }
997
998    private class JMSTopic extends ClientCallable<String> {
999
1000        JMSTopic(String jobId) {
1001            super("GET", RestConstants.JOB, notEmpty(jobId, "jobId"), prepareParams(RestConstants.JOB_SHOW_PARAM,
1002                    RestConstants.JOB_SHOW_JMS_TOPIC));
1003        }
1004
1005        protected String call(HttpURLConnection conn) throws IOException, OozieClientException {
1006            if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) {
1007                Reader reader = new InputStreamReader(conn.getInputStream());
1008                JSONObject json = (JSONObject) JSONValue.parse(reader);
1009                return (String) json.get(JsonTags.JMS_TOPIC_NAME);
1010            }
1011            else {
1012                handleError(conn);
1013            }
1014            return null;
1015        }
1016    }
1017
1018    /**
1019     * Get the definition of a workflow job.
1020     *
1021     * @param jobId job Id.
1022     * @return the job log.
1023     * @throws OozieClientException thrown if the job info could not be retrieved.
1024     */
1025    public String getJobDefinition(String jobId) throws OozieClientException {
1026        return new JobDefinition(jobId).call();
1027    }
1028
1029    private class JobDefinition extends JobMetadata {
1030
1031        JobDefinition(String jobId) {
1032            super(jobId, RestConstants.JOB_SHOW_DEFINITION);
1033        }
1034    }
1035
1036    private class JobMetadata extends ClientCallable<String> {
1037        PrintStream printStream;
1038
1039        JobMetadata(String jobId, String metaType) {
1040            super("GET", RestConstants.JOB, notEmpty(jobId, "jobId"), prepareParams(RestConstants.JOB_SHOW_PARAM,
1041                    metaType));
1042        }
1043
1044        JobMetadata(String jobId, String logRetrievalType, String logRetrievalScope, String metaType, String logFilter,
1045                PrintStream ps) {
1046            super("GET", RestConstants.JOB, notEmpty(jobId, "jobId"), prepareParams(RestConstants.JOB_SHOW_PARAM,
1047                    metaType, RestConstants.JOB_LOG_TYPE_PARAM, logRetrievalType, RestConstants.JOB_LOG_SCOPE_PARAM,
1048                    logRetrievalScope, RestConstants.LOG_FILTER_OPTION, logFilter));
1049            printStream = ps;
1050        }
1051
1052        @Override
1053        protected String call(HttpURLConnection conn) throws IOException, OozieClientException {
1054            String returnVal = null;
1055            if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) {
1056                InputStream is = conn.getInputStream();
1057                InputStreamReader isr = new InputStreamReader(is);
1058                try {
1059                    if (printStream != null) {
1060                        sendToOutputStream(isr, -1);
1061                    }
1062                    else {
1063                        returnVal = getReaderAsString(isr, -1);
1064                    }
1065                }
1066                finally {
1067                    isr.close();
1068                }
1069            }
1070            else {
1071                handleError(conn);
1072            }
1073            return returnVal;
1074        }
1075
1076        /**
1077         * Output the log to command line interface
1078         *
1079         * @param reader reader to read into a string.
1080         * @param maxLen max content length allowed, if -1 there is no limit.
1081         * @throws IOException
1082         */
1083        private void sendToOutputStream(Reader reader, int maxLen) throws IOException {
1084            if (reader == null) {
1085                throw new IllegalArgumentException("reader cannot be null");
1086            }
1087            StringBuilder sb = new StringBuilder();
1088            char[] buffer = new char[2048];
1089            int read;
1090            int count = 0;
1091            int noOfCharstoFlush = 1024;
1092            while ((read = reader.read(buffer)) > -1) {
1093                count += read;
1094                if ((maxLen > -1) && (count > maxLen)) {
1095                    break;
1096                }
1097                sb.append(buffer, 0, read);
1098                if (sb.length() > noOfCharstoFlush) {
1099                    printStream.print(sb.toString());
1100                    sb = new StringBuilder("");
1101                }
1102            }
1103            printStream.print(sb.toString());
1104        }
1105
1106        /**
1107         * Return a reader as string.
1108         * <p/>
1109         *
1110         * @param reader reader to read into a string.
1111         * @param maxLen max content length allowed, if -1 there is no limit.
1112         * @return the reader content.
1113         * @throws IOException thrown if the resource could not be read.
1114         */
1115        private String getReaderAsString(Reader reader, int maxLen) throws IOException {
1116            if (reader == null) {
1117                throw new IllegalArgumentException("reader cannot be null");
1118            }
1119            StringBuffer sb = new StringBuffer();
1120            char[] buffer = new char[2048];
1121            int read;
1122            int count = 0;
1123            while ((read = reader.read(buffer)) > -1) {
1124                count += read;
1125
1126                // read up to maxLen chars;
1127                if ((maxLen > -1) && (count > maxLen)) {
1128                    break;
1129                }
1130                sb.append(buffer, 0, read);
1131            }
1132            reader.close();
1133            return sb.toString();
1134        }
1135    }
1136
1137    private class CoordJobInfo extends ClientCallable<CoordinatorJob> {
1138
1139        CoordJobInfo(String jobId, String filter, int start, int len, String order) {
1140            super("GET", RestConstants.JOB, notEmpty(jobId, "jobId"), prepareParams(RestConstants.JOB_SHOW_PARAM,
1141                    RestConstants.JOB_SHOW_INFO, RestConstants.JOB_FILTER_PARAM, filter, RestConstants.OFFSET_PARAM,
1142                    Integer.toString(start), RestConstants.LEN_PARAM, Integer.toString(len), RestConstants.ORDER_PARAM,
1143                    order));
1144        }
1145
1146        @Override
1147        protected CoordinatorJob call(HttpURLConnection conn) throws IOException, OozieClientException {
1148            if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) {
1149                Reader reader = new InputStreamReader(conn.getInputStream());
1150                JSONObject json = (JSONObject) JSONValue.parse(reader);
1151                return JsonToBean.createCoordinatorJob(json);
1152            }
1153            else {
1154                handleError(conn);
1155            }
1156            return null;
1157        }
1158    }
1159
1160    private class WfsForCoordAction extends ClientCallable<List<WorkflowJob>> {
1161
1162        WfsForCoordAction(String coordActionId) {
1163            super("GET", RestConstants.JOB, notEmpty(coordActionId, "coordActionId"), prepareParams(
1164                    RestConstants.JOB_SHOW_PARAM, RestConstants.ALL_WORKFLOWS_FOR_COORD_ACTION));
1165        }
1166
1167        @Override
1168        protected List<WorkflowJob> call(HttpURLConnection conn) throws IOException, OozieClientException {
1169            if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) {
1170                Reader reader = new InputStreamReader(conn.getInputStream());
1171                JSONObject json = (JSONObject) JSONValue.parse(reader);
1172                JSONArray workflows = (JSONArray) json.get(JsonTags.WORKFLOWS_JOBS);
1173                if (workflows == null) {
1174                    workflows = new JSONArray();
1175                }
1176                return JsonToBean.createWorkflowJobList(workflows);
1177            }
1178            else {
1179                handleError(conn);
1180            }
1181            return null;
1182        }
1183    }
1184
1185
1186    private class BundleJobInfo extends ClientCallable<BundleJob> {
1187
1188        BundleJobInfo(String jobId) {
1189            super("GET", RestConstants.JOB, notEmpty(jobId, "jobId"), prepareParams(RestConstants.JOB_SHOW_PARAM,
1190                    RestConstants.JOB_SHOW_INFO));
1191        }
1192
1193        @Override
1194        protected BundleJob call(HttpURLConnection conn) throws IOException, OozieClientException {
1195            if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) {
1196                Reader reader = new InputStreamReader(conn.getInputStream());
1197                JSONObject json = (JSONObject) JSONValue.parse(reader);
1198                return JsonToBean.createBundleJob(json);
1199            }
1200            else {
1201                handleError(conn);
1202            }
1203            return null;
1204        }
1205    }
1206
1207    private class CoordActionInfo extends ClientCallable<CoordinatorAction> {
1208        CoordActionInfo(String actionId) {
1209            super("GET", RestConstants.JOB, notEmpty(actionId, "id"), prepareParams(RestConstants.JOB_SHOW_PARAM,
1210                    RestConstants.JOB_SHOW_INFO));
1211        }
1212
1213        @Override
1214        protected CoordinatorAction call(HttpURLConnection conn) throws IOException, OozieClientException {
1215            if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) {
1216                Reader reader = new InputStreamReader(conn.getInputStream());
1217                JSONObject json = (JSONObject) JSONValue.parse(reader);
1218                return JsonToBean.createCoordinatorAction(json);
1219            }
1220            else {
1221                handleError(conn);
1222            }
1223            return null;
1224        }
1225    }
1226
1227    /**
1228     * Get the info of a bundle job.
1229     *
1230     * @param jobId job Id.
1231     * @return the job info.
1232     * @throws OozieClientException thrown if the job info could not be retrieved.
1233     */
1234    public BundleJob getBundleJobInfo(String jobId) throws OozieClientException {
1235        return new BundleJobInfo(jobId).call();
1236    }
1237
1238    /**
1239     * Get the info of a coordinator job.
1240     *
1241     * @param jobId job Id.
1242     * @return the job info.
1243     * @throws OozieClientException thrown if the job info could not be retrieved.
1244     */
1245    public CoordinatorJob getCoordJobInfo(String jobId) throws OozieClientException {
1246        return new CoordJobInfo(jobId, null, -1, -1, "asc").call();
1247    }
1248
1249    /**
1250     * Get the info of a coordinator job and subset actions.
1251     *
1252     * @param jobId job Id.
1253     * @param filter filter the status filter
1254     * @param start starting index in the list of actions belonging to the job
1255     * @param len number of actions to be returned
1256     * @return the job info.
1257     * @throws OozieClientException thrown if the job info could not be retrieved.
1258     */
1259    public CoordinatorJob getCoordJobInfo(String jobId, String filter, int start, int len)
1260            throws OozieClientException {
1261        return new CoordJobInfo(jobId, filter, start, len, "asc").call();
1262    }
1263
1264    /**
1265     * Get the info of a coordinator job and subset actions.
1266     *
1267     * @param jobId job Id.
1268     * @param filter filter the status filter
1269     * @param start starting index in the list of actions belonging to the job
1270     * @param len number of actions to be returned
1271     * @param order order to list coord actions (e.g, desc)
1272     * @return the job info.
1273     * @throws OozieClientException thrown if the job info could not be retrieved.
1274     */
1275    public CoordinatorJob getCoordJobInfo(String jobId, String filter, int start, int len, String order)
1276            throws OozieClientException {
1277        return new CoordJobInfo(jobId, filter, start, len, order).call();
1278    }
1279
1280    public List<WorkflowJob> getWfsForCoordAction(String coordActionId) throws OozieClientException {
1281        return new WfsForCoordAction(coordActionId).call();
1282    }
1283
1284    /**
1285     * Get the info of a coordinator action.
1286     *
1287     * @param actionId Id.
1288     * @return the coordinator action info.
1289     * @throws OozieClientException thrown if the job info could not be retrieved.
1290     */
1291    public CoordinatorAction getCoordActionInfo(String actionId) throws OozieClientException {
1292        return new CoordActionInfo(actionId).call();
1293    }
1294
1295    private class JobsStatus extends ClientCallable<List<WorkflowJob>> {
1296
1297        JobsStatus(String filter, int start, int len) {
1298            super("GET", RestConstants.JOBS, "", prepareParams(RestConstants.JOBS_FILTER_PARAM, filter,
1299                    RestConstants.JOBTYPE_PARAM, "wf", RestConstants.OFFSET_PARAM, Integer.toString(start),
1300                    RestConstants.LEN_PARAM, Integer.toString(len)));
1301        }
1302
1303        @Override
1304        protected List<WorkflowJob> call(HttpURLConnection conn) throws IOException, OozieClientException {
1305            conn.setRequestProperty("content-type", RestConstants.XML_CONTENT_TYPE);
1306            if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) {
1307                Reader reader = new InputStreamReader(conn.getInputStream());
1308                JSONObject json = (JSONObject) JSONValue.parse(reader);
1309                JSONArray workflows = (JSONArray) json.get(JsonTags.WORKFLOWS_JOBS);
1310                if (workflows == null) {
1311                    workflows = new JSONArray();
1312                }
1313                return JsonToBean.createWorkflowJobList(workflows);
1314            }
1315            else {
1316                handleError(conn);
1317            }
1318            return null;
1319        }
1320    }
1321
1322    private class CoordJobsStatus extends ClientCallable<List<CoordinatorJob>> {
1323
1324        CoordJobsStatus(String filter, int start, int len) {
1325            super("GET", RestConstants.JOBS, "", prepareParams(RestConstants.JOBS_FILTER_PARAM, filter,
1326                    RestConstants.JOBTYPE_PARAM, "coord", RestConstants.OFFSET_PARAM, Integer.toString(start),
1327                    RestConstants.LEN_PARAM, Integer.toString(len)));
1328        }
1329
1330        @Override
1331        protected List<CoordinatorJob> call(HttpURLConnection conn) throws IOException, OozieClientException {
1332            conn.setRequestProperty("content-type", RestConstants.XML_CONTENT_TYPE);
1333            if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) {
1334                Reader reader = new InputStreamReader(conn.getInputStream());
1335                JSONObject json = (JSONObject) JSONValue.parse(reader);
1336                JSONArray jobs = (JSONArray) json.get(JsonTags.COORDINATOR_JOBS);
1337                if (jobs == null) {
1338                    jobs = new JSONArray();
1339                }
1340                return JsonToBean.createCoordinatorJobList(jobs);
1341            }
1342            else {
1343                handleError(conn);
1344            }
1345            return null;
1346        }
1347    }
1348
1349    private class BundleJobsStatus extends ClientCallable<List<BundleJob>> {
1350
1351        BundleJobsStatus(String filter, int start, int len) {
1352            super("GET", RestConstants.JOBS, "", prepareParams(RestConstants.JOBS_FILTER_PARAM, filter,
1353                    RestConstants.JOBTYPE_PARAM, "bundle", RestConstants.OFFSET_PARAM, Integer.toString(start),
1354                    RestConstants.LEN_PARAM, Integer.toString(len)));
1355        }
1356
1357        @Override
1358        protected List<BundleJob> call(HttpURLConnection conn) throws IOException, OozieClientException {
1359            conn.setRequestProperty("content-type", RestConstants.XML_CONTENT_TYPE);
1360            if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) {
1361                Reader reader = new InputStreamReader(conn.getInputStream());
1362                JSONObject json = (JSONObject) JSONValue.parse(reader);
1363                JSONArray jobs = (JSONArray) json.get(JsonTags.BUNDLE_JOBS);
1364                if (jobs == null) {
1365                    jobs = new JSONArray();
1366                }
1367                return JsonToBean.createBundleJobList(jobs);
1368            }
1369            else {
1370                handleError(conn);
1371            }
1372            return null;
1373        }
1374    }
1375
1376    private class BulkResponseStatus extends ClientCallable<List<BulkResponse>> {
1377
1378        BulkResponseStatus(String filter, int start, int len) {
1379            super("GET", RestConstants.JOBS, "", prepareParams(RestConstants.JOBS_BULK_PARAM, filter,
1380                    RestConstants.OFFSET_PARAM, Integer.toString(start), RestConstants.LEN_PARAM, Integer.toString(len)));
1381        }
1382
1383        @Override
1384        protected List<BulkResponse> call(HttpURLConnection conn) throws IOException, OozieClientException {
1385            conn.setRequestProperty("content-type", RestConstants.XML_CONTENT_TYPE);
1386            if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) {
1387                Reader reader = new InputStreamReader(conn.getInputStream());
1388                JSONObject json = (JSONObject) JSONValue.parse(reader);
1389                JSONArray results = (JSONArray) json.get(JsonTags.BULK_RESPONSES);
1390                if (results == null) {
1391                    results = new JSONArray();
1392                }
1393                return JsonToBean.createBulkResponseList(results);
1394            }
1395            else {
1396                handleError(conn);
1397            }
1398            return null;
1399        }
1400    }
1401
1402    private class CoordActionsKill extends ClientCallable<List<CoordinatorAction>> {
1403
1404        CoordActionsKill(String jobId, String rangeType, String scope) {
1405            super("PUT", RestConstants.JOB, notEmpty(jobId, "jobId"), prepareParams(RestConstants.ACTION_PARAM,
1406                    RestConstants.JOB_ACTION_KILL, RestConstants.JOB_COORD_RANGE_TYPE_PARAM, rangeType,
1407                    RestConstants.JOB_COORD_SCOPE_PARAM, scope));
1408        }
1409
1410        @Override
1411        protected List<CoordinatorAction> call(HttpURLConnection conn) throws IOException, OozieClientException {
1412            conn.setRequestProperty("content-type", RestConstants.XML_CONTENT_TYPE);
1413            if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) {
1414                Reader reader = new InputStreamReader(conn.getInputStream());
1415                JSONObject json = (JSONObject) JSONValue.parse(reader);
1416                JSONArray coordActions = (JSONArray) json.get(JsonTags.COORDINATOR_ACTIONS);
1417                return JsonToBean.createCoordinatorActionList(coordActions);
1418            }
1419            else {
1420                handleError(conn);
1421            }
1422            return null;
1423        }
1424    }
1425
1426    private class CoordIgnore extends ClientCallable<List<CoordinatorAction>> {
1427        CoordIgnore(String jobId, String rerunType, String scope) {
1428            super("PUT", RestConstants.JOB, notEmpty(jobId, "jobId"), prepareParams(RestConstants.ACTION_PARAM,
1429                    RestConstants.JOB_ACTION_IGNORE, RestConstants.JOB_COORD_RANGE_TYPE_PARAM,
1430                    rerunType, RestConstants.JOB_COORD_SCOPE_PARAM, scope));
1431        }
1432
1433        @Override
1434        protected List<CoordinatorAction> call(HttpURLConnection conn) throws IOException, OozieClientException {
1435            conn.setRequestProperty("content-type", RestConstants.XML_CONTENT_TYPE);
1436            if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) {
1437                Reader reader = new InputStreamReader(conn.getInputStream());
1438                JSONObject json = (JSONObject) JSONValue.parse(reader);
1439                if(json != null) {
1440                    JSONArray coordActions = (JSONArray) json.get(JsonTags.COORDINATOR_ACTIONS);
1441                    return JsonToBean.createCoordinatorActionList(coordActions);
1442                }
1443            }
1444            else {
1445                handleError(conn);
1446            }
1447            return null;
1448        }
1449    }
1450    private class CoordRerun extends ClientCallable<List<CoordinatorAction>> {
1451
1452        CoordRerun(String jobId, String rerunType, String scope, boolean refresh, boolean noCleanup) {
1453            super("PUT", RestConstants.JOB, notEmpty(jobId, "jobId"), prepareParams(RestConstants.ACTION_PARAM,
1454                    RestConstants.JOB_COORD_ACTION_RERUN, RestConstants.JOB_COORD_RANGE_TYPE_PARAM, rerunType,
1455                    RestConstants.JOB_COORD_SCOPE_PARAM, scope, RestConstants.JOB_COORD_RERUN_REFRESH_PARAM,
1456                    Boolean.toString(refresh), RestConstants.JOB_COORD_RERUN_NOCLEANUP_PARAM, Boolean
1457                            .toString(noCleanup)));
1458        }
1459
1460        @Override
1461        protected List<CoordinatorAction> call(HttpURLConnection conn) throws IOException, OozieClientException {
1462            conn.setRequestProperty("content-type", RestConstants.XML_CONTENT_TYPE);
1463            if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) {
1464                Reader reader = new InputStreamReader(conn.getInputStream());
1465                JSONObject json = (JSONObject) JSONValue.parse(reader);
1466                JSONArray coordActions = (JSONArray) json.get(JsonTags.COORDINATOR_ACTIONS);
1467                return JsonToBean.createCoordinatorActionList(coordActions);
1468            }
1469            else {
1470                handleError(conn);
1471            }
1472            return null;
1473        }
1474    }
1475
1476    private class BundleRerun extends ClientCallable<Void> {
1477
1478        BundleRerun(String jobId, String coordScope, String dateScope, boolean refresh, boolean noCleanup) {
1479            super("PUT", RestConstants.JOB, notEmpty(jobId, "jobId"), prepareParams(RestConstants.ACTION_PARAM,
1480                    RestConstants.JOB_BUNDLE_ACTION_RERUN, RestConstants.JOB_BUNDLE_RERUN_COORD_SCOPE_PARAM,
1481                    coordScope, RestConstants.JOB_BUNDLE_RERUN_DATE_SCOPE_PARAM, dateScope,
1482                    RestConstants.JOB_COORD_RERUN_REFRESH_PARAM, Boolean.toString(refresh),
1483                    RestConstants.JOB_COORD_RERUN_NOCLEANUP_PARAM, Boolean.toString(noCleanup)));
1484        }
1485
1486        @Override
1487        protected Void call(HttpURLConnection conn) throws IOException, OozieClientException {
1488            conn.setRequestProperty("content-type", RestConstants.XML_CONTENT_TYPE);
1489            if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) {
1490                return null;
1491            }
1492            else {
1493                handleError(conn);
1494            }
1495            return null;
1496        }
1497    }
1498
1499    /**
1500     * Rerun coordinator actions.
1501     *
1502     * @param jobId coordinator jobId
1503     * @param rerunType rerun type 'date' if -date is used, 'action-id' if -action is used
1504     * @param scope rerun scope for date or actionIds
1505     * @param refresh true if -refresh is given in command option
1506     * @param noCleanup true if -nocleanup is given in command option
1507     * @throws OozieClientException
1508     */
1509    public List<CoordinatorAction> reRunCoord(String jobId, String rerunType, String scope, boolean refresh,
1510            boolean noCleanup) throws OozieClientException {
1511        return new CoordRerun(jobId, rerunType, scope, refresh, noCleanup).call();
1512    }
1513
1514    /**
1515     * Rerun bundle coordinators.
1516     *
1517     * @param jobId bundle jobId
1518     * @param coordScope rerun scope for coordinator jobs
1519     * @param dateScope rerun scope for date
1520     * @param refresh true if -refresh is given in command option
1521     * @param noCleanup true if -nocleanup is given in command option
1522     * @throws OozieClientException
1523     */
1524    public Void reRunBundle(String jobId, String coordScope, String dateScope, boolean refresh, boolean noCleanup)
1525            throws OozieClientException {
1526        return new BundleRerun(jobId, coordScope, dateScope, refresh, noCleanup).call();
1527    }
1528
1529    /**
1530     * Return the info of the workflow jobs that match the filter.
1531     *
1532     * @param filter job filter. Refer to the {@link OozieClient} for the filter syntax.
1533     * @param start jobs offset, base 1.
1534     * @param len number of jobs to return.
1535     * @return a list with the workflow jobs info, without node details.
1536     * @throws OozieClientException thrown if the jobs info could not be retrieved.
1537     */
1538    public List<WorkflowJob> getJobsInfo(String filter, int start, int len) throws OozieClientException {
1539        return new JobsStatus(filter, start, len).call();
1540    }
1541
1542    /**
1543     * Return the info of the workflow jobs that match the filter.
1544     * <p/>
1545     * It returns the first 100 jobs that match the filter.
1546     *
1547     * @param filter job filter. Refer to the {@link OozieClient} for the filter syntax.
1548     * @return a list with the workflow jobs info, without node details.
1549     * @throws OozieClientException thrown if the jobs info could not be retrieved.
1550     */
1551    public List<WorkflowJob> getJobsInfo(String filter) throws OozieClientException {
1552        return getJobsInfo(filter, 1, 50);
1553    }
1554
1555    /**
1556     * Print sla info about coordinator and workflow jobs and actions.
1557     *
1558     * @param start starting offset
1559     * @param len number of results
1560     * @throws OozieClientException
1561     */
1562    public void getSlaInfo(int start, int len, String filter) throws OozieClientException {
1563        new SlaInfo(start, len, filter).call();
1564    }
1565
1566    private class SlaInfo extends ClientCallable<Void> {
1567
1568        SlaInfo(int start, int len, String filter) {
1569            super("GET", WS_PROTOCOL_VERSION_1, RestConstants.SLA, "", prepareParams(RestConstants.SLA_GT_SEQUENCE_ID,
1570                    Integer.toString(start), RestConstants.MAX_EVENTS, Integer.toString(len),
1571                    RestConstants.JOBS_FILTER_PARAM, filter));
1572        }
1573
1574        @Override
1575        protected Void call(HttpURLConnection conn) throws IOException, OozieClientException {
1576            conn.setRequestProperty("content-type", RestConstants.XML_CONTENT_TYPE);
1577            if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) {
1578                BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
1579                String line = null;
1580                while ((line = br.readLine()) != null) {
1581                    System.out.println(line);
1582                }
1583            }
1584            else {
1585                handleError(conn);
1586            }
1587            return null;
1588        }
1589    }
1590
1591    private class JobIdAction extends ClientCallable<String> {
1592
1593        JobIdAction(String externalId) {
1594            super("GET", RestConstants.JOBS, "", prepareParams(RestConstants.JOBTYPE_PARAM, "wf",
1595                    RestConstants.JOBS_EXTERNAL_ID_PARAM, externalId));
1596        }
1597
1598        @Override
1599        protected String call(HttpURLConnection conn) throws IOException, OozieClientException {
1600            if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) {
1601                Reader reader = new InputStreamReader(conn.getInputStream());
1602                JSONObject json = (JSONObject) JSONValue.parse(reader);
1603                return (String) json.get(JsonTags.JOB_ID);
1604            }
1605            else {
1606                handleError(conn);
1607            }
1608            return null;
1609        }
1610    }
1611
1612    /**
1613     * Return the workflow job Id for an external Id.
1614     * <p/>
1615     * The external Id must have provided at job creation time.
1616     *
1617     * @param externalId external Id given at job creation time.
1618     * @return the workflow job Id for an external Id, <code>null</code> if none.
1619     * @throws OozieClientException thrown if the operation could not be done.
1620     */
1621    public String getJobId(String externalId) throws OozieClientException {
1622        return new JobIdAction(externalId).call();
1623    }
1624
1625    private class SetSystemMode extends ClientCallable<Void> {
1626
1627        public SetSystemMode(SYSTEM_MODE status) {
1628            super("PUT", RestConstants.ADMIN, RestConstants.ADMIN_STATUS_RESOURCE, prepareParams(
1629                    RestConstants.ADMIN_SYSTEM_MODE_PARAM, status + ""));
1630        }
1631
1632        @Override
1633        public Void call(HttpURLConnection conn) throws IOException, OozieClientException {
1634            if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) {
1635                handleError(conn);
1636            }
1637            return null;
1638        }
1639    }
1640
1641    /**
1642     * Enable or disable safe mode. Used by OozieCLI. In safe mode, Oozie would not accept any commands except status
1643     * command to change and view the safe mode status.
1644     *
1645     * @param status true to enable safe mode, false to disable safe mode.
1646     * @throws OozieClientException if it fails to set the safe mode status.
1647     */
1648    public void setSystemMode(SYSTEM_MODE status) throws OozieClientException {
1649        new SetSystemMode(status).call();
1650    }
1651
1652    private class GetSystemMode extends ClientCallable<SYSTEM_MODE> {
1653
1654        GetSystemMode() {
1655            super("GET", RestConstants.ADMIN, RestConstants.ADMIN_STATUS_RESOURCE, prepareParams());
1656        }
1657
1658        @Override
1659        protected SYSTEM_MODE call(HttpURLConnection conn) throws IOException, OozieClientException {
1660            if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) {
1661                Reader reader = new InputStreamReader(conn.getInputStream());
1662                JSONObject json = (JSONObject) JSONValue.parse(reader);
1663                return SYSTEM_MODE.valueOf((String) json.get(JsonTags.OOZIE_SYSTEM_MODE));
1664            }
1665            else {
1666                handleError(conn);
1667            }
1668            return SYSTEM_MODE.NORMAL;
1669        }
1670    }
1671
1672    /**
1673     * Returns if Oozie is in safe mode or not.
1674     *
1675     * @return true if safe mode is ON<br>
1676     *         false if safe mode is OFF
1677     * @throws OozieClientException throw if it could not obtain the safe mode status.
1678     */
1679    /*
1680     * public boolean isInSafeMode() throws OozieClientException { return new GetSafeMode().call(); }
1681     */
1682    public SYSTEM_MODE getSystemMode() throws OozieClientException {
1683        return new GetSystemMode().call();
1684    }
1685
1686    public String updateShareLib() throws OozieClientException {
1687        return new UpdateSharelib().call();
1688    }
1689
1690    public String listShareLib(String sharelibKey) throws OozieClientException {
1691        return new ListShareLib(sharelibKey).call();
1692    }
1693
1694    private class GetBuildVersion extends ClientCallable<String> {
1695
1696        GetBuildVersion() {
1697            super("GET", RestConstants.ADMIN, RestConstants.ADMIN_BUILD_VERSION_RESOURCE, prepareParams());
1698        }
1699
1700        @Override
1701        protected String call(HttpURLConnection conn) throws IOException, OozieClientException {
1702            if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) {
1703                Reader reader = new InputStreamReader(conn.getInputStream());
1704                JSONObject json = (JSONObject) JSONValue.parse(reader);
1705                return (String) json.get(JsonTags.BUILD_VERSION);
1706            }
1707            else {
1708                handleError(conn);
1709            }
1710            return null;
1711        }
1712    }
1713
1714
1715    private  class UpdateSharelib extends ClientCallable<String> {
1716
1717        UpdateSharelib() {
1718            super("GET", RestConstants.ADMIN, RestConstants.ADMIN_UPDATE_SHARELIB, prepareParams(
1719                    RestConstants.ALL_SERVER_REQUEST, "true"));
1720        }
1721
1722        @Override
1723        protected String call(HttpURLConnection conn) throws IOException, OozieClientException {
1724            StringBuffer bf = new StringBuffer();
1725            if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) {
1726                Reader reader = new InputStreamReader(conn.getInputStream());
1727                Object sharelib = (Object) JSONValue.parse(reader);
1728                bf.append("[ShareLib update status]").append(System.getProperty("line.separator"));
1729                if (sharelib instanceof JSONArray) {
1730                    for (Object o : ((JSONArray) sharelib)) {
1731                        JSONObject obj = (JSONObject) ((JSONObject) o).get(JsonTags.SHARELIB_LIB_UPDATE);
1732                        for (Object key : obj.keySet()) {
1733                            bf.append("\t").append(key).append(" = ").append(obj.get(key))
1734                                    .append(System.getProperty("line.separator"));
1735                        }
1736                        bf.append(System.getProperty("line.separator"));
1737                    }
1738                    return bf.toString();
1739                }
1740                else{
1741                    JSONObject obj = (JSONObject) ((JSONObject) sharelib).get(JsonTags.SHARELIB_LIB_UPDATE);
1742                    for (Object key : obj.keySet()) {
1743                        bf.append("\t").append(key).append(" = ").append(obj.get(key))
1744                                .append(System.getProperty("line.separator"));
1745                    }
1746                    bf.append(System.getProperty("line.separator"));
1747                }
1748            }
1749            else {
1750                handleError(conn);
1751            }
1752            return null;
1753        }
1754    }
1755
1756    private class ListShareLib extends ClientCallable<String> {
1757
1758        ListShareLib(String sharelibKey) {
1759            super("GET", RestConstants.ADMIN, RestConstants.ADMIN_LIST_SHARELIB, prepareParams(
1760                    RestConstants.SHARE_LIB_REQUEST_KEY, sharelibKey));
1761        }
1762
1763        @Override
1764        protected String call(HttpURLConnection conn) throws IOException, OozieClientException {
1765
1766            if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) {
1767                StringBuffer bf = new StringBuffer();
1768                Reader reader = new InputStreamReader(conn.getInputStream());
1769                JSONObject json = (JSONObject) JSONValue.parse(reader);
1770                Object sharelib = json.get(JsonTags.SHARELIB_LIB);
1771                bf.append("[Available ShareLib]").append(System.getProperty("line.separator"));
1772                if (sharelib instanceof JSONArray) {
1773                    for (Object o : ((JSONArray) sharelib)) {
1774                        JSONObject obj = (JSONObject) o;
1775                        bf.append(obj.get(JsonTags.SHARELIB_LIB_NAME))
1776                                .append(System.getProperty("line.separator"));
1777                        if (obj.get(JsonTags.SHARELIB_LIB_FILES) != null) {
1778                            for (Object file : ((JSONArray) obj.get(JsonTags.SHARELIB_LIB_FILES))) {
1779                                bf.append("\t").append(file).append(System.getProperty("line.separator"));
1780                            }
1781                        }
1782                    }
1783                    return bf.toString();
1784                }
1785            }
1786            else {
1787                handleError(conn);
1788            }
1789            return null;
1790        }
1791
1792    }
1793
1794    /**
1795     * Return the Oozie server build version.
1796     *
1797     * @return the Oozie server build version.
1798     * @throws OozieClientException throw if it the server build version could not be retrieved.
1799     */
1800    public String getServerBuildVersion() throws OozieClientException {
1801        return new GetBuildVersion().call();
1802    }
1803
1804    /**
1805     * Return the Oozie client build version.
1806     *
1807     * @return the Oozie client build version.
1808     */
1809    public String getClientBuildVersion() {
1810        return BuildInfo.getBuildInfo().getProperty(BuildInfo.BUILD_VERSION);
1811    }
1812
1813    /**
1814     * Return the info of the coordinator jobs that match the filter.
1815     *
1816     * @param filter job filter. Refer to the {@link OozieClient} for the filter syntax.
1817     * @param start jobs offset, base 1.
1818     * @param len number of jobs to return.
1819     * @return a list with the coordinator jobs info
1820     * @throws OozieClientException thrown if the jobs info could not be retrieved.
1821     */
1822    public List<CoordinatorJob> getCoordJobsInfo(String filter, int start, int len) throws OozieClientException {
1823        return new CoordJobsStatus(filter, start, len).call();
1824    }
1825
1826    /**
1827     * Return the info of the bundle jobs that match the filter.
1828     *
1829     * @param filter job filter. Refer to the {@link OozieClient} for the filter syntax.
1830     * @param start jobs offset, base 1.
1831     * @param len number of jobs to return.
1832     * @return a list with the bundle jobs info
1833     * @throws OozieClientException thrown if the jobs info could not be retrieved.
1834     */
1835    public List<BundleJob> getBundleJobsInfo(String filter, int start, int len) throws OozieClientException {
1836        return new BundleJobsStatus(filter, start, len).call();
1837    }
1838
1839    public List<BulkResponse> getBulkInfo(String filter, int start, int len) throws OozieClientException {
1840        return new BulkResponseStatus(filter, start, len).call();
1841    }
1842
1843    private class GetQueueDump extends ClientCallable<List<String>> {
1844        GetQueueDump() {
1845            super("GET", RestConstants.ADMIN, RestConstants.ADMIN_QUEUE_DUMP_RESOURCE, prepareParams());
1846        }
1847
1848        @Override
1849        protected List<String> call(HttpURLConnection conn) throws IOException, OozieClientException {
1850            if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) {
1851                Reader reader = new InputStreamReader(conn.getInputStream());
1852                JSONObject json = (JSONObject) JSONValue.parse(reader);
1853                JSONArray queueDumpArray = (JSONArray) json.get(JsonTags.QUEUE_DUMP);
1854
1855                List<String> list = new ArrayList<String>();
1856                list.add("[Server Queue Dump]:");
1857                for (Object o : queueDumpArray) {
1858                    JSONObject entry = (JSONObject) o;
1859                    if (entry.get(JsonTags.CALLABLE_DUMP) != null) {
1860                        String value = (String) entry.get(JsonTags.CALLABLE_DUMP);
1861                        list.add(value);
1862                    }
1863                }
1864                if (queueDumpArray.size() == 0) {
1865                    list.add("Queue dump is null!");
1866                }
1867
1868                list.add("******************************************");
1869                list.add("[Server Uniqueness Map Dump]:");
1870
1871                JSONArray uniqueDumpArray = (JSONArray) json.get(JsonTags.UNIQUE_MAP_DUMP);
1872                for (Object o : uniqueDumpArray) {
1873                    JSONObject entry = (JSONObject) o;
1874                    if (entry.get(JsonTags.UNIQUE_ENTRY_DUMP) != null) {
1875                        String value = (String) entry.get(JsonTags.UNIQUE_ENTRY_DUMP);
1876                        list.add(value);
1877                    }
1878                }
1879                if (uniqueDumpArray.size() == 0) {
1880                    list.add("Uniqueness dump is null!");
1881                }
1882                return list;
1883            }
1884            else {
1885                handleError(conn);
1886            }
1887            return null;
1888        }
1889    }
1890
1891    /**
1892     * Return the Oozie queue's commands' dump
1893     *
1894     * @return the list of strings of callable identification in queue
1895     * @throws OozieClientException throw if it the queue dump could not be retrieved.
1896     */
1897    public List<String> getQueueDump() throws OozieClientException {
1898        return new GetQueueDump().call();
1899    }
1900
1901    private class GetAvailableOozieServers extends ClientCallable<Map<String, String>> {
1902
1903        GetAvailableOozieServers() {
1904            super("GET", RestConstants.ADMIN, RestConstants.ADMIN_AVAILABLE_OOZIE_SERVERS_RESOURCE, prepareParams());
1905        }
1906
1907        @Override
1908        protected Map<String, String> call(HttpURLConnection conn) throws IOException, OozieClientException {
1909            if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) {
1910                Reader reader = new InputStreamReader(conn.getInputStream());
1911                JSONObject json = (JSONObject) JSONValue.parse(reader);
1912                Map<String, String> map = new HashMap<String, String>();
1913                for (Object key : json.keySet()) {
1914                    map.put((String)key, (String)json.get(key));
1915                }
1916                return map;
1917            }
1918            else {
1919                handleError(conn);
1920            }
1921            return null;
1922        }
1923    }
1924
1925    /**
1926     * Return the list of available Oozie servers.
1927     *
1928     * @return the list of available Oozie servers.
1929     * @throws OozieClientException throw if it the list of available Oozie servers could not be retrieved.
1930     */
1931    public Map<String, String> getAvailableOozieServers() throws OozieClientException {
1932        return new GetAvailableOozieServers().call();
1933    }
1934
1935
1936    /**
1937     * Check if the string is not null or not empty.
1938     *
1939     * @param str
1940     * @param name
1941     * @return string
1942     */
1943    public static String notEmpty(String str, String name) {
1944        if (str == null) {
1945            throw new IllegalArgumentException(name + " cannot be null");
1946        }
1947        if (str.length() == 0) {
1948            throw new IllegalArgumentException(name + " cannot be empty");
1949        }
1950        return str;
1951    }
1952
1953    /**
1954     * Check if the object is not null.
1955     *
1956     * @param <T>
1957     * @param obj
1958     * @param name
1959     * @return string
1960     */
1961    public static <T> T notNull(T obj, String name) {
1962        if (obj == null) {
1963            throw new IllegalArgumentException(name + " cannot be null");
1964        }
1965        return obj;
1966    }
1967
1968}