001/** 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 019package org.apache.oozie.client; 020 021import com.fasterxml.jackson.core.type.TypeReference; 022import com.fasterxml.jackson.databind.ObjectMapper; 023import com.google.common.collect.Lists; 024import org.apache.oozie.BuildInfo; 025import org.apache.oozie.cli.ValidationUtil; 026import org.apache.oozie.client.rest.JsonTags; 027import org.apache.oozie.client.rest.JsonToBean; 028import org.apache.oozie.client.rest.RestConstants; 029import org.apache.oozie.client.retry.ConnectionRetriableClient; 030import org.json.simple.JSONArray; 031import org.json.simple.JSONObject; 032import org.json.simple.JSONValue; 033import org.w3c.dom.Document; 034import org.w3c.dom.Element; 035 036import javax.xml.parsers.DocumentBuilderFactory; 037import javax.xml.transform.Transformer; 038import javax.xml.transform.TransformerFactory; 039import javax.xml.transform.dom.DOMSource; 040import javax.xml.transform.stream.StreamResult; 041 042import java.io.BufferedReader; 043import java.io.File; 044import java.io.FileInputStream; 045import java.io.IOException; 046import java.io.InputStream; 047import java.io.InputStreamReader; 048import java.io.OutputStream; 049import java.io.PrintStream; 050import java.io.Reader; 051import java.net.HttpURLConnection; 052import java.net.URL; 053import java.net.URLEncoder; 054import java.nio.charset.StandardCharsets; 055import java.util.Collections; 056import java.util.HashMap; 057import java.util.HashSet; 058import java.util.Iterator; 059import java.util.LinkedHashMap; 060import java.util.List; 061import java.util.Map; 062import java.util.Map.Entry; 063import java.util.Properties; 064import java.util.Set; 065import java.util.concurrent.Callable; 066 067 068/** 069 * Client API to submit and manage Oozie workflow jobs against an Oozie instance. 070 * <p> 071 * This class is thread safe. 072 * <p> 073 * Syntax for filter for the {@link #getJobsInfo(String)} {@link #getJobsInfo(String, int, int)} methods: 074 * <code>[NAME=VALUE][;NAME=VALUE]*</code>. 075 * <p> 076 * Valid filter names are: 077 * <ul> 078 * <li>name: the workflow application name from the workflow definition.</li> 079 * <li>user: the user that submitted the job.</li> 080 * <li>group: the group for the job.</li> 081 * <li>status: the status of the job.</li> 082 * </ul> 083 * <p> 084 * 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 085 * name. Multiple values must be specified as different name value pairs. 086 */ 087public class OozieClient { 088 089 public static final long WS_PROTOCOL_VERSION_0 = 0; 090 091 public static final long WS_PROTOCOL_VERSION_1 = 1; 092 093 public static final long WS_PROTOCOL_VERSION = 2; // pointer to current version 094 095 public static final String USER_NAME = "user.name"; 096 097 @Deprecated 098 public static final String GROUP_NAME = "group.name"; 099 100 public static final String JOB_ACL = "oozie.job.acl"; 101 102 public static final String APP_PATH = "oozie.wf.application.path"; 103 104 public static final String COORDINATOR_APP_PATH = "oozie.coord.application.path"; 105 106 public static final String BUNDLE_APP_PATH = "oozie.bundle.application.path"; 107 108 public static final String BUNDLE_ID = "oozie.bundle.id"; 109 110 public static final String EXTERNAL_ID = "oozie.wf.external.id"; 111 112 public static final String WORKFLOW_NOTIFICATION_PROXY = "oozie.wf.workflow.notification.proxy"; 113 114 public static final String WORKFLOW_NOTIFICATION_URL = "oozie.wf.workflow.notification.url"; 115 116 public static final String ACTION_NOTIFICATION_URL = "oozie.wf.action.notification.url"; 117 118 public static final String COORD_ACTION_NOTIFICATION_URL = "oozie.coord.action.notification.url"; 119 120 public static final String COORD_ACTION_NOTIFICATION_PROXY = "oozie.coord.action.notification.proxy"; 121 122 public static final String RERUN_SKIP_NODES = "oozie.wf.rerun.skip.nodes"; 123 124 public static final String RERUN_FAIL_NODES = "oozie.wf.rerun.failnodes"; 125 126 public static final String LOG_TOKEN = "oozie.wf.log.token"; 127 128 public static final String ACTION_MAX_RETRIES = "oozie.wf.action.max.retries"; 129 130 public static final String ACTION_RETRY_INTERVAL = "oozie.wf.action.retry.interval"; 131 132 public static final String FILTER_USER = "user"; 133 134 public static final String FILTER_TEXT = "text"; 135 136 public static final String FILTER_GROUP = "group"; 137 138 public static final String FILTER_NAME = "name"; 139 140 public static final String FILTER_STATUS = "status"; 141 142 public static final String FILTER_NOMINAL_TIME = "nominaltime"; 143 144 public static final String FILTER_FREQUENCY = "frequency"; 145 146 public static final String FILTER_ID = "id"; 147 148 public static final String FILTER_UNIT = "unit"; 149 150 public static final String FILTER_JOBID = "jobid"; 151 152 public static final String FILTER_APPNAME = "appname"; 153 154 public static final String FILTER_SLA_APPNAME = "app_name"; 155 156 public static final String FILTER_SLA_ID = "id"; 157 158 public static final String FILTER_SLA_PARENT_ID = "parent_id"; 159 160 public static final String FILTER_BUNDLE = "bundle"; 161 162 public static final String FILTER_SLA_EVENT_STATUS = "event_status"; 163 164 public static final String FILTER_SLA_STATUS = "sla_status"; 165 166 public static final String FILTER_SLA_NOMINAL_START = "nominal_start"; 167 168 public static final String FILTER_SLA_NOMINAL_END = "nominal_end"; 169 170 public static final String FILTER_CREATED_TIME_START = "startcreatedtime"; 171 172 public static final String FILTER_CREATED_TIME_END = "endcreatedtime"; 173 174 public static final String SLA_DISABLE_ALERT = "oozie.sla.disable.alerts"; 175 176 public static final String SLA_ENABLE_ALERT = "oozie.sla.enable.alerts"; 177 178 public static final String SLA_DISABLE_ALERT_OLDER_THAN = SLA_DISABLE_ALERT + ".older.than"; 179 180 public static final String SLA_DISABLE_ALERT_COORD = SLA_DISABLE_ALERT + ".coord"; 181 182 public static final String CHANGE_VALUE_ENDTIME = "endtime"; 183 184 public static final String CHANGE_VALUE_PAUSETIME = "pausetime"; 185 186 public static final String CHANGE_VALUE_CONCURRENCY = "concurrency"; 187 188 public static final String CHANGE_VALUE_STATUS = "status"; 189 190 public static final String LIBPATH = "oozie.libpath"; 191 192 public static final String USE_SYSTEM_LIBPATH = "oozie.use.system.libpath"; 193 194 public static final String OOZIE_SUSPEND_ON_NODES = "oozie.suspend.on.nodes"; 195 196 public static final String FILTER_SORT_BY = "sortby"; 197 198 public enum SORT_BY { 199 createdTime("createdTimestamp"), lastModifiedTime("lastModifiedTimestamp"); 200 private final String fullname; 201 202 SORT_BY(String fullname) { 203 this.fullname = fullname; 204 } 205 206 public String getFullname() { 207 return fullname; 208 } 209 } 210 211 public static enum SYSTEM_MODE { 212 NORMAL, NOWEBSERVICE, SAFEMODE 213 } 214 215 private static final Set<String> COMPLETED_WF_STATUSES = new HashSet<>(); 216 private static final Set<String> COMPLETED_COORD_AND_BUNDLE_STATUSES = new HashSet<>(); 217 private static final Set<String> COMPLETED_COORD_ACTION_STATUSES = new HashSet<>(); 218 static { 219 COMPLETED_WF_STATUSES.add(WorkflowJob.Status.FAILED.toString()); 220 COMPLETED_WF_STATUSES.add(WorkflowJob.Status.KILLED.toString()); 221 COMPLETED_WF_STATUSES.add(WorkflowJob.Status.SUCCEEDED.toString()); 222 COMPLETED_COORD_AND_BUNDLE_STATUSES.add(Job.Status.FAILED.toString()); 223 COMPLETED_COORD_AND_BUNDLE_STATUSES.add(Job.Status.KILLED.toString()); 224 COMPLETED_COORD_AND_BUNDLE_STATUSES.add(Job.Status.SUCCEEDED.toString()); 225 COMPLETED_COORD_AND_BUNDLE_STATUSES.add(Job.Status.DONEWITHERROR.toString()); 226 COMPLETED_COORD_AND_BUNDLE_STATUSES.add(Job.Status.IGNORED.toString()); 227 COMPLETED_COORD_ACTION_STATUSES.add(CoordinatorAction.Status.FAILED.toString()); 228 COMPLETED_COORD_ACTION_STATUSES.add(CoordinatorAction.Status.IGNORED.toString()); 229 COMPLETED_COORD_ACTION_STATUSES.add(CoordinatorAction.Status.KILLED.toString()); 230 COMPLETED_COORD_ACTION_STATUSES.add(CoordinatorAction.Status.SKIPPED.toString()); 231 COMPLETED_COORD_ACTION_STATUSES.add(CoordinatorAction.Status.SUCCEEDED.toString()); 232 COMPLETED_COORD_ACTION_STATUSES.add(CoordinatorAction.Status.TIMEDOUT.toString()); 233 } 234 235 /** 236 * debugMode =0 means no debugging. 1 means debugging on. 237 */ 238 public int debugMode = 0; 239 240 private int retryCount = 4; 241 242 243 private String baseUrl; 244 private String protocolUrl; 245 private boolean validatedVersion = false; 246 private JSONArray supportedVersions; 247 private final Map<String, String> headers = new HashMap<>(); 248 249 private static final ThreadLocal<String> USER_NAME_TL = new ThreadLocal<>(); 250 251 /** 252 * Allows to impersonate other users in the Oozie server. The current user 253 * must be configured as a proxyuser in Oozie. 254 * <p> 255 * IMPORTANT: impersonation happens only with Oozie client requests done within 256 * doAs() calls. 257 * 258 * @param <T> the type of the callable 259 * @param userName user to impersonate. 260 * @param callable callable with {@link OozieClient} calls impersonating the specified user. 261 * @return any response returned by the {@link Callable#call()} method. 262 * @throws Exception thrown by the {@link Callable#call()} method. 263 */ 264 public static <T> T doAs(String userName, Callable<T> callable) throws Exception { 265 notEmpty(userName, "userName"); 266 notNull(callable, "callable"); 267 try { 268 USER_NAME_TL.set(userName); 269 return callable.call(); 270 } 271 finally { 272 USER_NAME_TL.remove(); 273 } 274 } 275 276 protected OozieClient() { 277 } 278 279 /** 280 * Create a Workflow client instance. 281 * 282 * @param oozieUrl URL of the Oozie instance it will interact with. 283 */ 284 public OozieClient(String oozieUrl) { 285 this.baseUrl = notEmpty(oozieUrl, "oozieUrl"); 286 if (!this.baseUrl.endsWith("/")) { 287 this.baseUrl += "/"; 288 } 289 } 290 291 /** 292 * Return the Oozie URL of the workflow client instance. 293 * <p> 294 * This URL is the base URL fo the Oozie system, with not protocol versioning. 295 * 296 * @return the Oozie URL of the workflow client instance. 297 */ 298 public String getOozieUrl() { 299 return baseUrl; 300 } 301 302 /** 303 * Return the Oozie URL used by the client and server for WS communications. 304 * <p> 305 * This URL is the original URL plus the versioning element path. 306 * 307 * @return the Oozie URL used by the client and server for communication. 308 * @throws OozieClientException thrown in the client and the server are not protocol compatible. 309 */ 310 public String getProtocolUrl() throws OozieClientException { 311 validateWSVersion(); 312 return protocolUrl; 313 } 314 315 /** 316 * @return current debug Mode 317 */ 318 public int getDebugMode() { 319 return debugMode; 320 } 321 322 /** 323 * Set debug mode. 324 * 325 * @param debugMode : 0 means no debugging. 1 means debugging 326 */ 327 public void setDebugMode(int debugMode) { 328 this.debugMode = debugMode; 329 } 330 331 public int getRetryCount() { 332 return retryCount; 333 } 334 335 336 public void setRetryCount(int retryCount) { 337 this.retryCount = retryCount; 338 } 339 340 private String getBaseURLForVersion(long protocolVersion) throws OozieClientException { 341 try { 342 if (supportedVersions == null) { 343 supportedVersions = getSupportedProtocolVersions(); 344 } 345 if (supportedVersions == null) { 346 throw new OozieClientException("HTTP error", "no response message"); 347 } 348 if (supportedVersions.contains(protocolVersion)) { 349 return baseUrl + "v" + protocolVersion + "/"; 350 } 351 else { 352 throw new OozieClientException(OozieClientException.UNSUPPORTED_VERSION, "Protocol version " 353 + protocolVersion + " is not supported"); 354 } 355 } 356 catch (IOException e) { 357 throw new OozieClientException(OozieClientException.IO_ERROR, e); 358 } 359 } 360 361 /** 362 * Validate that the Oozie client and server instances are protocol compatible. 363 * 364 * @throws OozieClientException thrown in the client and the server are not protocol compatible. 365 */ 366 public synchronized void validateWSVersion() throws OozieClientException { 367 if (!validatedVersion) { 368 try { 369 supportedVersions = getSupportedProtocolVersions(); 370 if (supportedVersions == null) { 371 throw new OozieClientException("HTTP error", "no response message"); 372 } 373 if (!supportedVersions.contains(WS_PROTOCOL_VERSION) 374 && !supportedVersions.contains(WS_PROTOCOL_VERSION_1) 375 && !supportedVersions.contains(WS_PROTOCOL_VERSION_0)) { 376 StringBuilder msg = new StringBuilder(); 377 msg.append("Supported version [").append(WS_PROTOCOL_VERSION) 378 .append("] or less, Unsupported versions["); 379 String separator = ""; 380 for (Object version : supportedVersions) { 381 msg.append(separator).append(version); 382 } 383 msg.append("]"); 384 throw new OozieClientException(OozieClientException.UNSUPPORTED_VERSION, msg.toString()); 385 } 386 if (supportedVersions.contains(WS_PROTOCOL_VERSION)) { 387 protocolUrl = baseUrl + "v" + WS_PROTOCOL_VERSION + "/"; 388 } 389 else if (supportedVersions.contains(WS_PROTOCOL_VERSION_1)) { 390 protocolUrl = baseUrl + "v" + WS_PROTOCOL_VERSION_1 + "/"; 391 } 392 else { 393 if (supportedVersions.contains(WS_PROTOCOL_VERSION_0)) { 394 protocolUrl = baseUrl + "v" + WS_PROTOCOL_VERSION_0 + "/"; 395 } 396 } 397 } 398 catch (IOException ex) { 399 throw new OozieClientException(OozieClientException.IO_ERROR, ex); 400 } 401 validatedVersion = true; 402 } 403 } 404 405 private JSONArray getSupportedProtocolVersions() throws IOException, OozieClientException { 406 JSONArray versions = null; 407 final URL url = new URL(baseUrl + RestConstants.VERSIONS); 408 409 HttpURLConnection conn = createRetryableConnection(url, "GET"); 410 411 if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) { 412 versions = (JSONArray) JSONValue.parse(new InputStreamReader(conn.getInputStream())); 413 } 414 else { 415 handleError(conn); 416 } 417 return versions; 418 } 419 420 /** 421 * Create an empty configuration with just the {@link #USER_NAME} set to the JVM user name. 422 * 423 * @return an empty configuration. 424 */ 425 public Properties createConfiguration() { 426 Properties conf = new Properties(); 427 String userName = USER_NAME_TL.get(); 428 if (userName == null) { 429 userName = System.getProperty("user.name"); 430 } 431 conf.setProperty(USER_NAME, userName); 432 return conf; 433 } 434 435 /** 436 * Set a HTTP header to be used in the WS requests by the workflow instance. 437 * 438 * @param name header name. 439 * @param value header value. 440 */ 441 public void setHeader(String name, String value) { 442 headers.put(notEmpty(name, "name"), notNull(value, "value")); 443 } 444 445 /** 446 * Get the value of a set HTTP header from the workflow instance. 447 * 448 * @param name header name. 449 * @return header value, <code>null</code> if not set. 450 */ 451 public String getHeader(String name) { 452 return headers.get(notEmpty(name, "name")); 453 } 454 455 /** 456 * Get the set HTTP header 457 * 458 * @return map of header key and value 459 */ 460 public Map<String, String> getHeaders() { 461 return headers; 462 } 463 464 /** 465 * Remove a HTTP header from the workflow client instance. 466 * 467 * @param name header name. 468 */ 469 public void removeHeader(String name) { 470 headers.remove(notEmpty(name, "name")); 471 } 472 473 /** 474 * Return an iterator with all the header names set in the workflow instance. 475 * 476 * @return header names. 477 */ 478 public Iterator<String> getHeaderNames() { 479 return Collections.unmodifiableMap(headers).keySet().iterator(); 480 } 481 482 private URL createURL(Long protocolVersion, String collection, String resource, Map<String, String> parameters) 483 throws IOException, OozieClientException { 484 validateWSVersion(); 485 StringBuilder sb = new StringBuilder(); 486 if (protocolVersion == null) { 487 sb.append(protocolUrl); 488 } 489 else { 490 sb.append(getBaseURLForVersion(protocolVersion)); 491 } 492 sb.append(collection); 493 if (resource != null && resource.length() > 0) { 494 sb.append("/").append(resource); 495 } 496 if (parameters.size() > 0) { 497 String separator = "?"; 498 for (Map.Entry<String, String> param : parameters.entrySet()) { 499 if (param.getValue() != null) { 500 sb.append(separator).append(URLEncoder.encode(param.getKey(), "UTF-8")).append("=").append( 501 URLEncoder.encode(param.getValue(), "UTF-8")); 502 separator = "&"; 503 } 504 } 505 } 506 return new URL(sb.toString()); 507 } 508 509 private boolean validateCommand(String url) { 510 { 511 if (protocolUrl.contains(baseUrl + "v0")) { 512 if (url.contains("dryrun") || url.contains("jobtype=c") || url.contains("systemmode")) { 513 return false; 514 } 515 } 516 } 517 return true; 518 } 519 /** 520 * Create retryable http connection to oozie server. 521 * 522 * @param url URL to create connection to 523 * @param method method to use - GET, POST, PUT 524 * @return connection 525 * @throws IOException in case of an exception during connection setup 526 */ 527 protected HttpURLConnection createRetryableConnection(final URL url, final String method) throws IOException { 528 return (HttpURLConnection) new ConnectionRetriableClient(getRetryCount()) { 529 @Override 530 public Object doExecute(URL url, String method) throws IOException, OozieClientException { 531 HttpURLConnection conn = createConnection(url, method); 532 return conn; 533 } 534 }.execute(url, method); 535 } 536 537 /** 538 * Create http connection to oozie server. 539 * 540 * @param url URL to create connection to 541 * @param method method to use - GET, POST, PUT 542 * @return connection 543 * @throws IOException in case of an exception during connection setup 544 * @throws OozieClientException if the connection can't be set up 545 */ 546 protected HttpURLConnection createConnection(URL url, String method) throws IOException, OozieClientException { 547 HttpURLConnection conn = (HttpURLConnection) url.openConnection(); 548 conn.setRequestMethod(method); 549 if (method.equals("POST") || method.equals("PUT")) { 550 conn.setDoOutput(true); 551 } 552 for (Map.Entry<String, String> header : headers.entrySet()) { 553 conn.setRequestProperty(header.getKey(), header.getValue()); 554 } 555 return conn; 556 } 557 558 protected abstract class ClientCallable<T> implements Callable<T> { 559 private final String method; 560 private final String collection; 561 private final String resource; 562 private final Map<String, String> params; 563 private final Long protocolVersion; 564 565 public ClientCallable(String method, String collection, String resource, Map<String, String> params) { 566 this(method, null, collection, resource, params); 567 } 568 569 public ClientCallable(String method, Long protocolVersion, String collection, String resource, Map<String, String> params) { 570 this.method = method; 571 this.protocolVersion = protocolVersion; 572 this.collection = collection; 573 this.resource = resource; 574 this.params = params; 575 } 576 577 public T call() throws OozieClientException { 578 try { 579 URL url = createURL(protocolVersion, collection, resource, params); 580 if (validateCommand(url.toString())) { 581 if (getDebugMode() > 0) { 582 System.out.println(method + " " + url); 583 } 584 return call(createRetryableConnection(url, method)); 585 } 586 else { 587 System.out.println("Option not supported in target server. Supported only on Oozie-2.0 or greater." 588 + " Use 'oozie help' for details"); 589 throw new OozieClientException(OozieClientException.UNSUPPORTED_VERSION, new Exception()); 590 } 591 } 592 catch (IOException ex) { 593 throw new OozieClientException(OozieClientException.IO_ERROR, ex); 594 } 595 } 596 597 protected abstract T call(HttpURLConnection conn) throws IOException, OozieClientException; 598 599 } 600 601 protected abstract class MapClientCallable extends ClientCallable<Map<String, String>> { 602 603 MapClientCallable(String method, String collection, String resource, Map<String, String> params) { 604 super(method, collection, resource, params); 605 } 606 607 @Override 608 protected Map<String, String> call(HttpURLConnection conn) throws IOException, OozieClientException { 609 if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) { 610 Reader reader = new InputStreamReader(conn.getInputStream()); 611 JSONObject json = (JSONObject) JSONValue.parse(reader); 612 Map<String, String> map = new HashMap<>(); 613 for (Object key : json.keySet()) { 614 map.put((String)key, (String)json.get(key)); 615 } 616 return map; 617 } 618 else { 619 handleError(conn); 620 } 621 return null; 622 } 623 } 624 625 static void handleError(HttpURLConnection conn) throws IOException, OozieClientException { 626 int status = conn.getResponseCode(); 627 String error = conn.getHeaderField(RestConstants.OOZIE_ERROR_CODE); 628 String message = conn.getHeaderField(RestConstants.OOZIE_ERROR_MESSAGE); 629 630 if (error == null) { 631 error = "HTTP error code: " + status; 632 } 633 634 if (message == null) { 635 message = conn.getResponseMessage(); 636 } 637 throw new OozieClientException(error, message); 638 } 639 640 static Map<String, String> prepareParams(String... params) { 641 Map<String, String> map = new LinkedHashMap<>(); 642 for (int i = 0; i < params.length; i = i + 2) { 643 map.put(params[i], params[i + 1]); 644 } 645 String doAsUserName = USER_NAME_TL.get(); 646 if (doAsUserName != null) { 647 map.put(RestConstants.DO_AS_PARAM, doAsUserName); 648 } 649 return map; 650 } 651 652 public void writeToXml(Properties props, OutputStream out) throws IOException { 653 try { 654 Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); 655 Element conf = doc.createElement("configuration"); 656 doc.appendChild(conf); 657 conf.appendChild(doc.createTextNode("\n")); 658 for (String name : props.stringPropertyNames()) { // Properties whose key or value is not of type String are omitted. 659 String value = props.getProperty(name); 660 Element propNode = doc.createElement("property"); 661 conf.appendChild(propNode); 662 663 Element nameNode = doc.createElement("name"); 664 nameNode.appendChild(doc.createTextNode(name.trim())); 665 propNode.appendChild(nameNode); 666 667 Element valueNode = doc.createElement("value"); 668 valueNode.appendChild(doc.createTextNode(value.trim())); 669 propNode.appendChild(valueNode); 670 671 conf.appendChild(doc.createTextNode("\n")); 672 } 673 674 DOMSource source = new DOMSource(doc); 675 StreamResult result = new StreamResult(out); 676 TransformerFactory transFactory = TransformerFactory.newInstance(); 677 transFactory.setFeature("http://javax.xml.XMLConstants/feature/secure-processing", true); 678 Transformer transformer = transFactory.newTransformer(); 679 transformer.transform(source, result); 680 if (getDebugMode() > 0) { 681 result = new StreamResult(System.out); 682 transformer.transform(source, result); 683 System.out.println(); 684 } 685 } 686 catch (Exception e) { 687 throw new IOException(e); 688 } 689 } 690 691 private class JobSubmit extends ClientCallable<String> { 692 private final Properties conf; 693 694 JobSubmit(Properties conf, boolean start) { 695 super("POST", RestConstants.JOBS, "", (start) ? prepareParams(RestConstants.ACTION_PARAM, 696 RestConstants.JOB_ACTION_START) : prepareParams()); 697 this.conf = notNull(conf, "conf"); 698 } 699 700 JobSubmit(String jobId, Properties conf) { 701 super("PUT", RestConstants.JOB, notEmpty(jobId, "jobId"), prepareParams(RestConstants.ACTION_PARAM, 702 RestConstants.JOB_ACTION_RERUN)); 703 this.conf = notNull(conf, "conf"); 704 } 705 706 public JobSubmit(Properties conf, String jobActionDryrun) { 707 super("POST", RestConstants.JOBS, "", prepareParams(RestConstants.ACTION_PARAM, 708 RestConstants.JOB_ACTION_DRYRUN)); 709 this.conf = notNull(conf, "conf"); 710 } 711 712 @Override 713 protected String call(HttpURLConnection conn) throws IOException, OozieClientException { 714 conn.setRequestProperty("content-type", RestConstants.XML_CONTENT_TYPE); 715 writeToXml(conf, conn.getOutputStream()); 716 if (conn.getResponseCode() == HttpURLConnection.HTTP_CREATED) { 717 JSONObject json = (JSONObject) JSONValue.parse(new InputStreamReader(conn.getInputStream())); 718 return (String) json.get(JsonTags.JOB_ID); 719 } 720 if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) { 721 handleError(conn); 722 } 723 return null; 724 } 725 } 726 727 /** 728 * Submit a workflow job. 729 * 730 * @param conf job configuration. 731 * @return the job Id. 732 * @throws OozieClientException thrown if the job could not be submitted. 733 */ 734 public String submit(Properties conf) throws OozieClientException { 735 return (new JobSubmit(conf, false)).call(); 736 } 737 738 private class JobAction extends ClientCallable<Void> { 739 740 JobAction(String jobId, String action) { 741 super("PUT", RestConstants.JOB, notEmpty(jobId, "jobId"), prepareParams(RestConstants.ACTION_PARAM, action)); 742 } 743 744 JobAction(String jobId, String action, String params) { 745 super("PUT", RestConstants.JOB, notEmpty(jobId, "jobId"), prepareParams(RestConstants.ACTION_PARAM, action, 746 RestConstants.JOB_CHANGE_VALUE, params)); 747 } 748 749 @Override 750 protected Void call(HttpURLConnection conn) throws IOException, OozieClientException { 751 if (!(conn.getResponseCode() == HttpURLConnection.HTTP_OK)) { 752 handleError(conn); 753 } 754 return null; 755 } 756 } 757 758 private class JobsAction extends ClientCallable<JSONObject> { 759 760 JobsAction(String action, String filter, String jobType, int start, int len) { 761 super("PUT", RestConstants.JOBS, "", 762 prepareParams(RestConstants.ACTION_PARAM, action, 763 RestConstants.JOB_FILTER_PARAM, filter, RestConstants.JOBTYPE_PARAM, jobType, 764 RestConstants.OFFSET_PARAM, Integer.toString(start), 765 RestConstants.LEN_PARAM, Integer.toString(len))); 766 } 767 768 @Override 769 protected JSONObject call(HttpURLConnection conn) throws IOException, OozieClientException { 770 conn.setRequestProperty("content-type", RestConstants.XML_CONTENT_TYPE); 771 if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) { 772 Reader reader = new InputStreamReader(conn.getInputStream()); 773 JSONObject json = (JSONObject) JSONValue.parse(reader); 774 return json; 775 } 776 else { 777 handleError(conn); 778 } 779 return null; 780 } 781 } 782 /** 783 * Update coord definition. 784 * 785 * @param jobId the job id 786 * @param conf the conf 787 * @param dryrun the dryrun 788 * @param showDiff the show diff 789 * @return the string 790 * @throws OozieClientException the oozie client exception 791 */ 792 public String updateCoord(String jobId, Properties conf, String dryrun, String showDiff) 793 throws OozieClientException { 794 return (new UpdateCoord(jobId, conf, dryrun, showDiff)).call(); 795 } 796 797 /** 798 * Update coord definition without properties. 799 * 800 * @param jobId the job id 801 * @param dryrun the dryrun 802 * @param showDiff the show diff 803 * @return the string 804 * @throws OozieClientException the oozie client exception 805 */ 806 public String updateCoord(String jobId, String dryrun, String showDiff) throws OozieClientException { 807 return (new UpdateCoord(jobId, dryrun, showDiff)).call(); 808 } 809 810 /** 811 * The Class UpdateCoord. 812 */ 813 private class UpdateCoord extends ClientCallable<String> { 814 private final Properties conf; 815 816 public UpdateCoord(String jobId, Properties conf, String jobActionDryrun, String showDiff) { 817 super("PUT", RestConstants.JOB, notEmpty(jobId, "jobId"), prepareParams(RestConstants.ACTION_PARAM, 818 RestConstants.JOB_COORD_UPDATE, RestConstants.JOB_ACTION_DRYRUN, jobActionDryrun, 819 RestConstants.JOB_ACTION_SHOWDIFF, showDiff)); 820 this.conf = conf; 821 } 822 823 public UpdateCoord(String jobId, String jobActionDryrun, String showDiff) { 824 this(jobId, new Properties(), jobActionDryrun, showDiff); 825 } 826 827 @Override 828 protected String call(HttpURLConnection conn) throws IOException, OozieClientException { 829 conn.setRequestProperty("content-type", RestConstants.XML_CONTENT_TYPE); 830 writeToXml(conf, conn.getOutputStream()); 831 832 if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) { 833 JSONObject json = (JSONObject) JSONValue.parse(new InputStreamReader(conn.getInputStream())); 834 JSONObject update = (JSONObject) json.get(JsonTags.COORD_UPDATE); 835 if (update != null) { 836 return (String) update.get(JsonTags.COORD_UPDATE_DIFF); 837 } 838 else { 839 return ""; 840 } 841 } 842 if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) { 843 handleError(conn); 844 } 845 return null; 846 } 847 } 848 849 /** 850 * dryrun for a given job 851 * 852 * @param conf Job configuration. 853 * @return new job. 854 * @throws org.apache.oozie.client.OozieClientException thrown if the job could not be submitted. 855 */ 856 public String dryrun(Properties conf) throws OozieClientException { 857 return new JobSubmit(conf, RestConstants.JOB_ACTION_DRYRUN).call(); 858 } 859 860 /** 861 * Start a workflow job. 862 * 863 * @param jobId job Id. 864 * @throws OozieClientException thrown if the job could not be started. 865 */ 866 public void start(String jobId) throws OozieClientException { 867 new JobAction(jobId, RestConstants.JOB_ACTION_START).call(); 868 } 869 870 /** 871 * Submit and start a workflow job. 872 * 873 * @param conf job configuration. 874 * @return the job Id. 875 * @throws OozieClientException thrown if the job could not be submitted. 876 */ 877 public String run(Properties conf) throws OozieClientException { 878 return (new JobSubmit(conf, true)).call(); 879 } 880 881 /** 882 * Rerun a workflow job. 883 * 884 * @param jobId job Id to rerun. 885 * @param conf configuration information for the rerun. 886 * @throws OozieClientException thrown if the job could not be started. 887 */ 888 public void reRun(String jobId, Properties conf) throws OozieClientException { 889 new JobSubmit(jobId, conf).call(); 890 } 891 892 /** 893 * Suspend a workflow job. 894 * 895 * @param jobId job Id. 896 * @throws OozieClientException thrown if the job could not be suspended. 897 */ 898 public void suspend(String jobId) throws OozieClientException { 899 new JobAction(jobId, RestConstants.JOB_ACTION_SUSPEND).call(); 900 } 901 902 /** 903 * Resume a workflow job. 904 * 905 * @param jobId job Id. 906 * @throws OozieClientException thrown if the job could not be resume. 907 */ 908 public void resume(String jobId) throws OozieClientException { 909 new JobAction(jobId, RestConstants.JOB_ACTION_RESUME).call(); 910 } 911 912 /** 913 * Kill a workflow/coord/bundle job. 914 * 915 * @param jobId job Id. 916 * @throws OozieClientException thrown if the job could not be killed. 917 */ 918 public void kill(String jobId) throws OozieClientException { 919 new JobAction(jobId, RestConstants.JOB_ACTION_KILL).call(); 920 } 921 922 /** 923 * Kill coordinator actions 924 * @param jobId coordinator Job Id 925 * @param rangeType type 'date' if -date is used, 'action-num' if -action is used 926 * @param scope kill scope for date or action nums 927 * @return list of coordinator actions that underwent kill 928 * @throws OozieClientException thrown if some actions could not be killed. 929 */ 930 public List<CoordinatorAction> kill(String jobId, String rangeType, String scope) throws OozieClientException { 931 return new CoordActionsKill(jobId, rangeType, scope).call(); 932 } 933 934 public JSONObject bulkModifyJobs(String actionType, String filter, String jobType, int start, int len) 935 throws OozieClientException { 936 return new JobsAction(actionType, filter, jobType, start, len).call(); 937 } 938 939 public JSONObject killJobs(String filter, String jobType, int start, int len) 940 throws OozieClientException { 941 return bulkModifyJobs("kill", filter, jobType, start, len); 942 } 943 944 public JSONObject suspendJobs(String filter, String jobType, int start, int len) 945 throws OozieClientException { 946 return bulkModifyJobs("suspend", filter, jobType, start, len); 947 } 948 949 public JSONObject resumeJobs(String filter, String jobType, int start, int len) 950 throws OozieClientException { 951 return bulkModifyJobs("resume", filter, jobType, start, len); 952 } 953 /** 954 * Change a coordinator job. 955 * 956 * @param jobId job Id. 957 * @param changeValue change value. 958 * @throws OozieClientException thrown if the job could not be changed. 959 */ 960 public void change(String jobId, String changeValue) throws OozieClientException { 961 new JobAction(jobId, RestConstants.JOB_ACTION_CHANGE, changeValue).call(); 962 } 963 964 /** 965 * Ignore a coordinator job. 966 * 967 * @param jobId coord job Id. 968 * @param scope list of coord actions to be ignored 969 * @return the list ignored actions or null if there are none 970 * @throws OozieClientException thrown if the job could not be changed. 971 * @return list of ignored coordinator jobs 972 */ 973 public List<CoordinatorAction> ignore(String jobId, String scope) throws OozieClientException { 974 return new CoordIgnore(jobId, RestConstants.JOB_COORD_SCOPE_ACTION, scope).call(); 975 } 976 977 private class JobInfo extends ClientCallable<WorkflowJob> { 978 979 JobInfo(String jobId, int start, int len) { 980 super("GET", RestConstants.JOB, notEmpty(jobId, "jobId"), prepareParams(RestConstants.JOB_SHOW_PARAM, 981 RestConstants.JOB_SHOW_INFO, RestConstants.OFFSET_PARAM, Integer.toString(start), 982 RestConstants.LEN_PARAM, Integer.toString(len))); 983 } 984 985 @Override 986 protected WorkflowJob call(HttpURLConnection conn) throws IOException, OozieClientException { 987 if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) { 988 Reader reader = new InputStreamReader(conn.getInputStream()); 989 JSONObject json = (JSONObject) JSONValue.parse(reader); 990 return JsonToBean.createWorkflowJob(json); 991 } 992 else { 993 handleError(conn); 994 } 995 return null; 996 } 997 } 998 999 private class JMSInfo extends ClientCallable<JMSConnectionInfo> { 1000 1001 JMSInfo() { 1002 super("GET", RestConstants.ADMIN, RestConstants.ADMIN_JMS_INFO, prepareParams()); 1003 } 1004 1005 protected JMSConnectionInfo 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 JsonToBean.createJMSConnectionInfo(json); 1010 } 1011 else { 1012 handleError(conn); 1013 } 1014 return null; 1015 } 1016 } 1017 1018 private class WorkflowActionInfo extends ClientCallable<WorkflowAction> { 1019 WorkflowActionInfo(String actionId) { 1020 super("GET", RestConstants.JOB, notEmpty(actionId, "id"), prepareParams(RestConstants.JOB_SHOW_PARAM, 1021 RestConstants.JOB_SHOW_INFO)); 1022 } 1023 1024 @Override 1025 protected WorkflowAction call(HttpURLConnection conn) throws IOException, OozieClientException { 1026 if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) { 1027 Reader reader = new InputStreamReader(conn.getInputStream()); 1028 JSONObject json = (JSONObject) JSONValue.parse(reader); 1029 return JsonToBean.createWorkflowAction(json); 1030 } 1031 else { 1032 handleError(conn); 1033 } 1034 return null; 1035 } 1036 } 1037 1038 private class WorkflowActionRetriesInfo extends ClientCallable<List<Map<String, String>>> { 1039 WorkflowActionRetriesInfo(String actionId) { 1040 super("GET", RestConstants.JOB, notEmpty(actionId, "id"), 1041 prepareParams(RestConstants.JOB_SHOW_PARAM, RestConstants.JOB_SHOW_ACTION_RETRIES_PARAM)); 1042 } 1043 1044 @Override 1045 protected List<Map<String, String>> call(HttpURLConnection conn) 1046 throws IOException, OozieClientException { 1047 if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) { 1048 Reader reader = new InputStreamReader(conn.getInputStream()); 1049 JSONObject json = (JSONObject) JSONValue.parse(reader); 1050 return new ObjectMapper().readValue(json.get(JsonTags.WORKFLOW_ACTION_RETRIES).toString(), 1051 new TypeReference<List<Map<String, String>>>() { 1052 }); 1053 } 1054 else { 1055 handleError(conn); 1056 } 1057 return null; 1058 } 1059 } 1060 1061 /** 1062 * Get the info of a workflow job. 1063 * 1064 * @param jobId job Id. 1065 * @return the job info. 1066 * @throws OozieClientException thrown if the job info could not be retrieved. 1067 */ 1068 public WorkflowJob getJobInfo(String jobId) throws OozieClientException { 1069 return getJobInfo(jobId, 0, 0); 1070 } 1071 1072 /** 1073 * Get the JMS Connection info 1074 * @return JMSConnectionInfo object 1075 * @throws OozieClientException thrown if the JMS info could not be retrieved. 1076 */ 1077 public JMSConnectionInfo getJMSConnectionInfo() throws OozieClientException { 1078 return new JMSInfo().call(); 1079 } 1080 1081 /** 1082 * Get the info of a workflow job and subset actions. 1083 * 1084 * @param jobId job Id. 1085 * @param start starting index in the list of actions belonging to the job 1086 * @param len number of actions to be returned 1087 * @return the job info. 1088 * @throws OozieClientException thrown if the job info could not be retrieved. 1089 */ 1090 public WorkflowJob getJobInfo(String jobId, int start, int len) throws OozieClientException { 1091 return new JobInfo(jobId, start, len).call(); 1092 } 1093 1094 /** 1095 * Get the info of a workflow action. 1096 * 1097 * @param actionId Id. 1098 * @return the workflow action info. 1099 * @throws OozieClientException thrown if the job info could not be retrieved. 1100 */ 1101 public WorkflowAction getWorkflowActionInfo(String actionId) throws OozieClientException { 1102 return new WorkflowActionInfo(actionId).call(); 1103 } 1104 1105 1106 /** 1107 * Get the info of a workflow action. 1108 * 1109 * @param actionId Id. 1110 * @return the workflow action retries info. 1111 * @throws OozieClientException thrown if the job info could not be retrieved. 1112 */ 1113 public List<Map<String, String>> getWorkflowActionRetriesInfo(String actionId) throws OozieClientException { 1114 return new WorkflowActionRetriesInfo(actionId).call(); 1115 } 1116 1117 /** 1118 * Get the log of a workflow job. 1119 * 1120 * @param jobId job Id. 1121 * @return the job log. 1122 * @throws OozieClientException thrown if the job info could not be retrieved. 1123 */ 1124 public String getJobLog(String jobId) throws OozieClientException { 1125 return new JobLog(jobId).call(); 1126 } 1127 1128 /** 1129 * Get the audit log of a job. 1130 * 1131 * @param jobId the id of the job 1132 * @param ps the stream to write the result into 1133 * @throws OozieClientException thrown if the job audit log could not be retrieved. 1134 */ 1135 public void getJobAuditLog(String jobId, PrintStream ps) throws OozieClientException { 1136 new JobAuditLog(jobId, ps).call(); 1137 } 1138 1139 /** 1140 * Get the log of a job. 1141 * 1142 * @param jobId job Id. 1143 * @param logRetrievalType Based on which filter criteria the log is retrieved 1144 * @param logRetrievalScope Value for the retrieval type 1145 * @param logFilter log filter 1146 * @param ps Printstream of command line interface 1147 * @throws OozieClientException thrown if the job info could not be retrieved. 1148 */ 1149 public void getJobLog(String jobId, String logRetrievalType, String logRetrievalScope, String logFilter, 1150 PrintStream ps) throws OozieClientException { 1151 new JobLog(jobId, logRetrievalType, logRetrievalScope, logFilter, ps).call(); 1152 } 1153 1154 /** 1155 * Get the error log of a job. 1156 * 1157 * @param jobId the id of the job 1158 * @param ps the stream to write the result into 1159 * @throws OozieClientException thrown if the job log could not be retrieved. 1160 */ 1161 public void getJobErrorLog(String jobId, PrintStream ps) throws OozieClientException { 1162 new JobErrorLog(jobId, ps).call(); 1163 } 1164 1165 /** 1166 * Get the log of a job. 1167 * 1168 * @param jobId job Id. 1169 * @param logRetrievalType Based on which filter criteria the log is retrieved 1170 * @param logRetrievalScope Value for the retrieval type 1171 * @param ps Printstream of command line interface 1172 * @throws OozieClientException thrown if the job info could not be retrieved. 1173 */ 1174 public void getJobLog(String jobId, String logRetrievalType, String logRetrievalScope, PrintStream ps) 1175 throws OozieClientException { 1176 getJobLog(jobId, logRetrievalType, logRetrievalScope, null, ps); 1177 } 1178 1179 private class JobLog extends JobMetadata { 1180 JobLog(String jobId) { 1181 super(jobId, RestConstants.JOB_SHOW_LOG); 1182 } 1183 JobLog(String jobId, String logRetrievalType, String logRetrievalScope, String logFilter, PrintStream ps) { 1184 super(jobId, logRetrievalType, logRetrievalScope, RestConstants.JOB_SHOW_LOG, logFilter, ps); 1185 } 1186 } 1187 1188 private class JobErrorLog extends JobMetadata { 1189 JobErrorLog(String jobId, PrintStream ps) { 1190 super(jobId, RestConstants.JOB_SHOW_ERROR_LOG, ps); 1191 } 1192 } 1193 1194 private class JobAuditLog extends JobMetadata { 1195 JobAuditLog(String jobId, PrintStream ps) { 1196 super(jobId, RestConstants.JOB_SHOW_AUDIT_LOG, ps); 1197 } 1198 } 1199 1200 1201 /** 1202 * Gets the JMS topic name for a particular job 1203 * @param jobId given jobId 1204 * @return the JMS topic name 1205 * @throws OozieClientException thrown if the JMS topic could not be retrieved. 1206 */ 1207 public String getJMSTopicName(String jobId) throws OozieClientException { 1208 return new JMSTopic(jobId).call(); 1209 } 1210 1211 private class JMSTopic extends ClientCallable<String> { 1212 1213 JMSTopic(String jobId) { 1214 super("GET", RestConstants.JOB, notEmpty(jobId, "jobId"), prepareParams(RestConstants.JOB_SHOW_PARAM, 1215 RestConstants.JOB_SHOW_JMS_TOPIC)); 1216 } 1217 1218 protected String call(HttpURLConnection conn) throws IOException, OozieClientException { 1219 if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) { 1220 Reader reader = new InputStreamReader(conn.getInputStream()); 1221 JSONObject json = (JSONObject) JSONValue.parse(reader); 1222 return (String) json.get(JsonTags.JMS_TOPIC_NAME); 1223 } 1224 else { 1225 handleError(conn); 1226 } 1227 return null; 1228 } 1229 } 1230 1231 /** 1232 * Get the definition of a workflow job. 1233 * 1234 * @param jobId job Id. 1235 * @return the job log. 1236 * @throws OozieClientException thrown if the job info could not be retrieved. 1237 */ 1238 public String getJobDefinition(String jobId) throws OozieClientException { 1239 return new JobDefinition(jobId).call(); 1240 } 1241 1242 /** 1243 * Get coord action missing dependencies 1244 * @param jobId the id of the job 1245 * @param actionList the list of coordinator actions 1246 * @param dates a comma-separated list of date ranges. Each date range element is specified with two dates separated by '::' 1247 * @param ps the stream to write the result into 1248 * @throws OozieClientException thrown if the info could not be retrieved. 1249 */ 1250 public void getCoordActionMissingDependencies(String jobId, String actionList, String dates, PrintStream ps) 1251 throws OozieClientException { 1252 new CoordActionMissingDependencies(jobId, actionList, dates, ps).call(); 1253 } 1254 1255 /** 1256 * Get coord action missing dependencies 1257 * @param jobId the id of the job 1258 * @param actionList the list of coordinator actions 1259 * @param dates a comma-separated list of date ranges. Each date range element is specified with two dates separated by '::' 1260 * @throws OozieClientException thrown if the info could not be retrieved. 1261 */ 1262 public void getCoordActionMissingDependencies(String jobId, String actionList, String dates) 1263 throws OozieClientException { 1264 new CoordActionMissingDependencies(jobId, actionList, dates).call(); 1265 } 1266 1267 private class CoordActionMissingDependencies extends ClientCallable<String> { 1268 PrintStream printStream; 1269 public CoordActionMissingDependencies(String jobId, String actionList, String dates, PrintStream ps) { 1270 super("GET", RestConstants.JOB, notEmpty(jobId, "jobId"), prepareParams(RestConstants.JOB_SHOW_PARAM, 1271 RestConstants.COORD_ACTION_MISSING_DEPENDENCIES, RestConstants.JOB_COORD_SCOPE_ACTION_LIST, actionList, 1272 RestConstants.JOB_COORD_SCOPE_DATE, dates)); 1273 this.printStream = ps; 1274 } 1275 1276 public CoordActionMissingDependencies(String jobId, String actionList, String dates) { 1277 this(jobId, actionList, dates, System.out); 1278 } 1279 1280 1281 @SuppressWarnings("unchecked") 1282 protected String call(HttpURLConnection conn) throws IOException, OozieClientException { 1283 if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) { 1284 Reader reader = new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8); 1285 JSONObject json = (JSONObject) JSONValue.parse(reader); 1286 if (json != null) { 1287 JSONArray inputDependencies = (JSONArray) json.get(JsonTags.COORD_ACTION_MISSING_DEPENDENCIES); 1288 for (Object dependencies : inputDependencies) { 1289 printStream.println(); 1290 printStream.println("CoordAction : " 1291 + ((JSONObject) dependencies).get(JsonTags.COORDINATOR_ACTION_ID)); 1292 if (((JSONObject) dependencies).get(JsonTags.COORD_ACTION_FIRST_MISSING_DEPENDENCIES) != null) { 1293 printStream.println("Blocked on : " 1294 + ((JSONObject) dependencies) 1295 .get(JsonTags.COORD_ACTION_FIRST_MISSING_DEPENDENCIES)); 1296 } 1297 1298 if (((JSONObject) dependencies).get(JsonTags.COORDINATOR_ACTION_DATASETS) != null) { 1299 1300 JSONArray missingDependencies = (JSONArray) ((JSONObject) dependencies) 1301 .get(JsonTags.COORDINATOR_ACTION_DATASETS); 1302 1303 for (Object missingDependenciesJson : missingDependencies) { 1304 1305 printStream.println("Dataset : " 1306 + ((JSONObject) missingDependenciesJson) 1307 .get(JsonTags.COORDINATOR_ACTION_DATASET)); 1308 1309 JSONArray inputDependenciesList = (JSONArray) ((JSONObject) missingDependenciesJson) 1310 .get(JsonTags.COORDINATOR_ACTION_MISSING_DEPS); 1311 printStream.println("Pending Dependencies : "); 1312 1313 if (inputDependenciesList != null) { 1314 Iterator<String> iterator = inputDependenciesList.iterator(); 1315 while (iterator.hasNext()) { 1316 printStream.println("\t " + iterator.next()); 1317 } 1318 } 1319 printStream.println(); 1320 } 1321 } 1322 } 1323 } 1324 else { 1325 printStream.println(" No missing input dependencies found"); 1326 } 1327 1328 } 1329 else { 1330 handleError(conn); 1331 } 1332 return null; 1333 } 1334 } 1335 1336 private class JobDefinition extends JobMetadata { 1337 1338 JobDefinition(String jobId) { 1339 super(jobId, RestConstants.JOB_SHOW_DEFINITION); 1340 } 1341 } 1342 1343 private class JobMetadata extends ClientCallable<String> { 1344 PrintStream printStream; 1345 1346 JobMetadata(String jobId, String metaType) { 1347 super("GET", RestConstants.JOB, notEmpty(jobId, "jobId"), prepareParams(RestConstants.JOB_SHOW_PARAM, 1348 metaType)); 1349 } 1350 1351 JobMetadata(String jobId, String metaType, PrintStream ps) { 1352 this(jobId, metaType); 1353 printStream = ps; 1354 1355 } 1356 1357 JobMetadata(String jobId, String logRetrievalType, String logRetrievalScope, String metaType, String logFilter, 1358 PrintStream ps) { 1359 super("GET", RestConstants.JOB, notEmpty(jobId, "jobId"), prepareParams(RestConstants.JOB_SHOW_PARAM, 1360 metaType, RestConstants.JOB_LOG_TYPE_PARAM, logRetrievalType, RestConstants.JOB_LOG_SCOPE_PARAM, 1361 logRetrievalScope, RestConstants.LOG_FILTER_OPTION, logFilter)); 1362 printStream = ps; 1363 } 1364 1365 @Override 1366 protected String call(HttpURLConnection conn) throws IOException, OozieClientException { 1367 String returnVal = null; 1368 if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) { 1369 InputStream is = conn.getInputStream(); 1370 InputStreamReader isr = new InputStreamReader(is); 1371 try { 1372 if (printStream != null) { 1373 sendToOutputStream(isr, -1, printStream); 1374 } 1375 else { 1376 returnVal = getReaderAsString(isr, -1); 1377 } 1378 } 1379 finally { 1380 isr.close(); 1381 } 1382 } 1383 else { 1384 handleError(conn); 1385 } 1386 return returnVal; 1387 } 1388 1389 /** 1390 * Output the log to command line interface 1391 * 1392 * @param reader reader to read into a string. 1393 * @param maxLen max content length allowed, if -1 there is no limit. 1394 * @throws IOException 1395 */ 1396 private void sendToOutputStream(Reader reader, int maxLen, PrintStream printStream) throws IOException { 1397 notNull(reader, "reader"); 1398 StringBuilder sb = new StringBuilder(); 1399 char[] buffer = new char[2048]; 1400 int read; 1401 int count = 0; 1402 int noOfCharstoFlush = 1024; 1403 while ((read = reader.read(buffer)) > -1) { 1404 count += read; 1405 if ((maxLen > -1) && (count > maxLen)) { 1406 break; 1407 } 1408 sb.append(buffer, 0, read); 1409 if (sb.length() > noOfCharstoFlush) { 1410 printStream.print(sb.toString()); 1411 sb = new StringBuilder(""); 1412 } 1413 } 1414 printStream.print(sb.toString()); 1415 } 1416 1417 /** 1418 * Return a reader as string. 1419 * <p> 1420 * 1421 * @param reader reader to read into a string. 1422 * @param maxLen max content length allowed, if -1 there is no limit. 1423 * @return the reader content. 1424 * @throws IOException thrown if the resource could not be read. 1425 */ 1426 private String getReaderAsString(Reader reader, int maxLen) throws IOException { 1427 notNull(reader, "reader"); 1428 StringBuffer sb = new StringBuffer(); 1429 char[] buffer = new char[2048]; 1430 int read; 1431 int count = 0; 1432 while ((read = reader.read(buffer)) > -1) { 1433 count += read; 1434 1435 // read up to maxLen chars; 1436 if ((maxLen > -1) && (count > maxLen)) { 1437 break; 1438 } 1439 sb.append(buffer, 0, read); 1440 } 1441 reader.close(); 1442 return sb.toString(); 1443 } 1444 } 1445 1446 private class CoordJobInfo extends ClientCallable<CoordinatorJob> { 1447 1448 CoordJobInfo(String jobId, String filter, int start, int len, String order) { 1449 super("GET", RestConstants.JOB, notEmpty(jobId, "jobId"), prepareParams(RestConstants.JOB_SHOW_PARAM, 1450 RestConstants.JOB_SHOW_INFO, RestConstants.JOB_FILTER_PARAM, filter, RestConstants.OFFSET_PARAM, 1451 Integer.toString(start), RestConstants.LEN_PARAM, Integer.toString(len), RestConstants.ORDER_PARAM, 1452 order)); 1453 } 1454 1455 @Override 1456 protected CoordinatorJob call(HttpURLConnection conn) throws IOException, OozieClientException { 1457 if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) { 1458 Reader reader = new InputStreamReader(conn.getInputStream()); 1459 JSONObject json = (JSONObject) JSONValue.parse(reader); 1460 return JsonToBean.createCoordinatorJob(json); 1461 } 1462 else { 1463 handleError(conn); 1464 } 1465 return null; 1466 } 1467 } 1468 1469 private class WfsForCoordAction extends ClientCallable<List<WorkflowJob>> { 1470 1471 WfsForCoordAction(String coordActionId) { 1472 super("GET", RestConstants.JOB, notEmpty(coordActionId, "coordActionId"), prepareParams( 1473 RestConstants.JOB_SHOW_PARAM, RestConstants.ALL_WORKFLOWS_FOR_COORD_ACTION)); 1474 } 1475 1476 @Override 1477 protected List<WorkflowJob> call(HttpURLConnection conn) throws IOException, OozieClientException { 1478 if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) { 1479 Reader reader = new InputStreamReader(conn.getInputStream()); 1480 JSONObject json = (JSONObject) JSONValue.parse(reader); 1481 JSONArray workflows = (JSONArray) json.get(JsonTags.WORKFLOWS_JOBS); 1482 if (workflows == null) { 1483 workflows = new JSONArray(); 1484 } 1485 return JsonToBean.createWorkflowJobList(workflows); 1486 } 1487 else { 1488 handleError(conn); 1489 } 1490 return null; 1491 } 1492 } 1493 1494 1495 private class BundleJobInfo extends ClientCallable<BundleJob> { 1496 1497 BundleJobInfo(String jobId) { 1498 super("GET", RestConstants.JOB, notEmpty(jobId, "jobId"), prepareParams(RestConstants.JOB_SHOW_PARAM, 1499 RestConstants.JOB_SHOW_INFO)); 1500 } 1501 1502 @Override 1503 protected BundleJob call(HttpURLConnection conn) throws IOException, OozieClientException { 1504 if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) { 1505 Reader reader = new InputStreamReader(conn.getInputStream()); 1506 JSONObject json = (JSONObject) JSONValue.parse(reader); 1507 return JsonToBean.createBundleJob(json); 1508 } 1509 else { 1510 handleError(conn); 1511 } 1512 return null; 1513 } 1514 } 1515 1516 private class CoordActionInfo extends ClientCallable<CoordinatorAction> { 1517 CoordActionInfo(String actionId) { 1518 super("GET", RestConstants.JOB, notEmpty(actionId, "id"), prepareParams(RestConstants.JOB_SHOW_PARAM, 1519 RestConstants.JOB_SHOW_INFO)); 1520 } 1521 1522 @Override 1523 protected CoordinatorAction call(HttpURLConnection conn) throws IOException, OozieClientException { 1524 if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) { 1525 Reader reader = new InputStreamReader(conn.getInputStream()); 1526 JSONObject json = (JSONObject) JSONValue.parse(reader); 1527 return JsonToBean.createCoordinatorAction(json); 1528 } 1529 else { 1530 handleError(conn); 1531 } 1532 return null; 1533 } 1534 } 1535 1536 /** 1537 * Get the info of a bundle job. 1538 * 1539 * @param jobId job Id. 1540 * @return the job info. 1541 * @throws OozieClientException thrown if the job info could not be retrieved. 1542 */ 1543 public BundleJob getBundleJobInfo(String jobId) throws OozieClientException { 1544 return new BundleJobInfo(jobId).call(); 1545 } 1546 1547 /** 1548 * Get the info of a coordinator job. 1549 * 1550 * @param jobId job Id. 1551 * @return the job info. 1552 * @throws OozieClientException thrown if the job info could not be retrieved. 1553 */ 1554 public CoordinatorJob getCoordJobInfo(String jobId) throws OozieClientException { 1555 return new CoordJobInfo(jobId, null, -1, -1, "asc").call(); 1556 } 1557 1558 /** 1559 * Get the info of a coordinator job and subset actions. 1560 * 1561 * @param jobId job Id. 1562 * @param filter filter the status filter 1563 * @param start starting index in the list of actions belonging to the job 1564 * @param len number of actions to be returned 1565 * @return the job info. 1566 * @throws OozieClientException thrown if the job info could not be retrieved. 1567 */ 1568 public CoordinatorJob getCoordJobInfo(String jobId, String filter, int start, int len) 1569 throws OozieClientException { 1570 return new CoordJobInfo(jobId, filter, start, len, "asc").call(); 1571 } 1572 1573 /** 1574 * Get the info of a coordinator job and subset actions. 1575 * 1576 * @param jobId job Id. 1577 * @param filter filter the status filter 1578 * @param start starting index in the list of actions belonging to the job 1579 * @param len number of actions to be returned 1580 * @param order order to list coord actions (e.g, desc) 1581 * @return the job info. 1582 * @throws OozieClientException thrown if the job info could not be retrieved. 1583 */ 1584 public CoordinatorJob getCoordJobInfo(String jobId, String filter, int start, int len, String order) 1585 throws OozieClientException { 1586 return new CoordJobInfo(jobId, filter, start, len, order).call(); 1587 } 1588 1589 public List<WorkflowJob> getWfsForCoordAction(String coordActionId) throws OozieClientException { 1590 return new WfsForCoordAction(coordActionId).call(); 1591 } 1592 1593 /** 1594 * Get the info of a coordinator action. 1595 * 1596 * @param actionId Id. 1597 * @return the coordinator action info. 1598 * @throws OozieClientException thrown if the job info could not be retrieved. 1599 */ 1600 public CoordinatorAction getCoordActionInfo(String actionId) throws OozieClientException { 1601 return new CoordActionInfo(actionId).call(); 1602 } 1603 1604 private class JobsStatus extends ClientCallable<List<WorkflowJob>> { 1605 1606 JobsStatus(String filter, int start, int len) { 1607 super("GET", RestConstants.JOBS, "", prepareParams(RestConstants.JOBS_FILTER_PARAM, filter, 1608 RestConstants.JOBTYPE_PARAM, "wf", RestConstants.OFFSET_PARAM, Integer.toString(start), 1609 RestConstants.LEN_PARAM, Integer.toString(len))); 1610 } 1611 1612 @Override 1613 protected List<WorkflowJob> call(HttpURLConnection conn) throws IOException, OozieClientException { 1614 conn.setRequestProperty("content-type", RestConstants.XML_CONTENT_TYPE); 1615 if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) { 1616 Reader reader = new InputStreamReader(conn.getInputStream()); 1617 JSONObject json = (JSONObject) JSONValue.parse(reader); 1618 JSONArray workflows = (JSONArray) json.get(JsonTags.WORKFLOWS_JOBS); 1619 if (workflows == null) { 1620 workflows = new JSONArray(); 1621 } 1622 return JsonToBean.createWorkflowJobList(workflows); 1623 } 1624 else { 1625 handleError(conn); 1626 } 1627 return null; 1628 } 1629 } 1630 1631 private class CoordJobsStatus extends ClientCallable<List<CoordinatorJob>> { 1632 1633 CoordJobsStatus(String filter, int start, int len) { 1634 super("GET", RestConstants.JOBS, "", prepareParams(RestConstants.JOBS_FILTER_PARAM, filter, 1635 RestConstants.JOBTYPE_PARAM, "coord", RestConstants.OFFSET_PARAM, Integer.toString(start), 1636 RestConstants.LEN_PARAM, Integer.toString(len))); 1637 } 1638 1639 @Override 1640 protected List<CoordinatorJob> call(HttpURLConnection conn) throws IOException, OozieClientException { 1641 conn.setRequestProperty("content-type", RestConstants.XML_CONTENT_TYPE); 1642 if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) { 1643 Reader reader = new InputStreamReader(conn.getInputStream()); 1644 JSONObject json = (JSONObject) JSONValue.parse(reader); 1645 JSONArray jobs = (JSONArray) json.get(JsonTags.COORDINATOR_JOBS); 1646 if (jobs == null) { 1647 jobs = new JSONArray(); 1648 } 1649 return JsonToBean.createCoordinatorJobList(jobs); 1650 } 1651 else { 1652 handleError(conn); 1653 } 1654 return null; 1655 } 1656 } 1657 1658 private class BundleJobsStatus extends ClientCallable<List<BundleJob>> { 1659 1660 BundleJobsStatus(String filter, int start, int len) { 1661 super("GET", RestConstants.JOBS, "", prepareParams(RestConstants.JOBS_FILTER_PARAM, filter, 1662 RestConstants.JOBTYPE_PARAM, "bundle", RestConstants.OFFSET_PARAM, Integer.toString(start), 1663 RestConstants.LEN_PARAM, Integer.toString(len))); 1664 } 1665 1666 @Override 1667 protected List<BundleJob> call(HttpURLConnection conn) throws IOException, OozieClientException { 1668 conn.setRequestProperty("content-type", RestConstants.XML_CONTENT_TYPE); 1669 if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) { 1670 Reader reader = new InputStreamReader(conn.getInputStream()); 1671 JSONObject json = (JSONObject) JSONValue.parse(reader); 1672 JSONArray jobs = (JSONArray) json.get(JsonTags.BUNDLE_JOBS); 1673 if (jobs == null) { 1674 jobs = new JSONArray(); 1675 } 1676 return JsonToBean.createBundleJobList(jobs); 1677 } 1678 else { 1679 handleError(conn); 1680 } 1681 return null; 1682 } 1683 } 1684 1685 private class BulkResponseStatus extends ClientCallable<List<BulkResponse>> { 1686 1687 BulkResponseStatus(String filter, int start, int len) { 1688 super("GET", RestConstants.JOBS, "", prepareParams(RestConstants.JOBS_BULK_PARAM, filter, 1689 RestConstants.OFFSET_PARAM, Integer.toString(start), RestConstants.LEN_PARAM, Integer.toString(len))); 1690 } 1691 1692 @Override 1693 protected List<BulkResponse> call(HttpURLConnection conn) throws IOException, OozieClientException { 1694 conn.setRequestProperty("content-type", RestConstants.XML_CONTENT_TYPE); 1695 if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) { 1696 Reader reader = new InputStreamReader(conn.getInputStream()); 1697 JSONObject json = (JSONObject) JSONValue.parse(reader); 1698 JSONArray results = (JSONArray) json.get(JsonTags.BULK_RESPONSES); 1699 if (results == null) { 1700 results = new JSONArray(); 1701 } 1702 return JsonToBean.createBulkResponseList(results); 1703 } 1704 else { 1705 handleError(conn); 1706 } 1707 return null; 1708 } 1709 } 1710 1711 private class CoordActionsKill extends ClientCallable<List<CoordinatorAction>> { 1712 1713 CoordActionsKill(String jobId, String rangeType, String scope) { 1714 super("PUT", RestConstants.JOB, notEmpty(jobId, "jobId"), prepareParams(RestConstants.ACTION_PARAM, 1715 RestConstants.JOB_ACTION_KILL, RestConstants.JOB_COORD_RANGE_TYPE_PARAM, rangeType, 1716 RestConstants.JOB_COORD_SCOPE_PARAM, scope)); 1717 } 1718 1719 @Override 1720 protected List<CoordinatorAction> call(HttpURLConnection conn) throws IOException, OozieClientException { 1721 conn.setRequestProperty("content-type", RestConstants.XML_CONTENT_TYPE); 1722 if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) { 1723 Reader reader = new InputStreamReader(conn.getInputStream()); 1724 JSONObject json = (JSONObject) JSONValue.parse(reader); 1725 JSONArray coordActions = (JSONArray) json.get(JsonTags.COORDINATOR_ACTIONS); 1726 return JsonToBean.createCoordinatorActionList(coordActions); 1727 } 1728 else { 1729 handleError(conn); 1730 } 1731 return null; 1732 } 1733 } 1734 1735 private class CoordIgnore extends ClientCallable<List<CoordinatorAction>> { 1736 CoordIgnore(String jobId, String rerunType, String scope) { 1737 super("PUT", RestConstants.JOB, notEmpty(jobId, "jobId"), prepareParams(RestConstants.ACTION_PARAM, 1738 RestConstants.JOB_ACTION_IGNORE, RestConstants.JOB_COORD_RANGE_TYPE_PARAM, 1739 rerunType, RestConstants.JOB_COORD_SCOPE_PARAM, scope)); 1740 } 1741 1742 @Override 1743 protected List<CoordinatorAction> call(HttpURLConnection conn) throws IOException, OozieClientException { 1744 conn.setRequestProperty("content-type", RestConstants.XML_CONTENT_TYPE); 1745 if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) { 1746 Reader reader = new InputStreamReader(conn.getInputStream()); 1747 JSONObject json = (JSONObject) JSONValue.parse(reader); 1748 if(json != null) { 1749 JSONArray coordActions = (JSONArray) json.get(JsonTags.COORDINATOR_ACTIONS); 1750 return JsonToBean.createCoordinatorActionList(coordActions); 1751 } 1752 } 1753 else { 1754 handleError(conn); 1755 } 1756 return null; 1757 } 1758 } 1759 private class CoordRerun extends ClientCallable<List<CoordinatorAction>> { 1760 private final Properties conf; 1761 1762 CoordRerun(String jobId, String rerunType, String scope, boolean refresh, boolean noCleanup, boolean failed, 1763 Properties conf) { 1764 super("PUT", RestConstants.JOB, notEmpty(jobId, "jobId"), prepareParams(RestConstants.ACTION_PARAM, 1765 RestConstants.JOB_COORD_ACTION_RERUN, RestConstants.JOB_COORD_RANGE_TYPE_PARAM, rerunType, 1766 RestConstants.JOB_COORD_SCOPE_PARAM, scope, RestConstants.JOB_COORD_RERUN_REFRESH_PARAM, 1767 Boolean.toString(refresh), RestConstants.JOB_COORD_RERUN_NOCLEANUP_PARAM, Boolean 1768 .toString(noCleanup), RestConstants.JOB_COORD_RERUN_FAILED_PARAM, Boolean.toString(failed))); 1769 this.conf = conf; 1770 } 1771 1772 @Override 1773 protected List<CoordinatorAction> call(HttpURLConnection conn) throws IOException, OozieClientException { 1774 conn.setRequestProperty("content-type", RestConstants.XML_CONTENT_TYPE); 1775 if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) { 1776 Reader reader = new InputStreamReader(conn.getInputStream()); 1777 JSONObject json = (JSONObject) JSONValue.parse(reader); 1778 JSONArray coordActions = (JSONArray) json.get(JsonTags.COORDINATOR_ACTIONS); 1779 return JsonToBean.createCoordinatorActionList(coordActions); 1780 } 1781 else { 1782 handleError(conn); 1783 } 1784 return null; 1785 } 1786 } 1787 1788 private class BundleRerun extends ClientCallable<Void> { 1789 1790 BundleRerun(String jobId, String coordScope, String dateScope, boolean refresh, boolean noCleanup) { 1791 super("PUT", RestConstants.JOB, notEmpty(jobId, "jobId"), prepareParams(RestConstants.ACTION_PARAM, 1792 RestConstants.JOB_BUNDLE_ACTION_RERUN, RestConstants.JOB_BUNDLE_RERUN_COORD_SCOPE_PARAM, 1793 coordScope, RestConstants.JOB_BUNDLE_RERUN_DATE_SCOPE_PARAM, dateScope, 1794 RestConstants.JOB_COORD_RERUN_REFRESH_PARAM, Boolean.toString(refresh), 1795 RestConstants.JOB_COORD_RERUN_NOCLEANUP_PARAM, Boolean.toString(noCleanup))); 1796 } 1797 1798 @Override 1799 protected Void call(HttpURLConnection conn) throws IOException, OozieClientException { 1800 conn.setRequestProperty("content-type", RestConstants.XML_CONTENT_TYPE); 1801 if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) { 1802 return null; 1803 } 1804 else { 1805 handleError(conn); 1806 } 1807 return null; 1808 } 1809 } 1810 1811 /** 1812 * Rerun coordinator actions. 1813 * 1814 * @param jobId coordinator jobId 1815 * @param rerunType rerun type 'date' if -date is used, 'action-id' if -action is used 1816 * @param scope rerun scope for date or actionIds 1817 * @param refresh true if -refresh is given in command option 1818 * @param noCleanup true if -nocleanup is given in command option 1819 * @return the list of rerun coordinator actions 1820 * @throws OozieClientException thrown if the info could not be retrieved. 1821 */ 1822 public List<CoordinatorAction> reRunCoord(String jobId, String rerunType, String scope, boolean refresh, 1823 boolean noCleanup) throws OozieClientException { 1824 return new CoordRerun(jobId, rerunType, scope, refresh, noCleanup, false, null).call(); 1825 } 1826 1827 /** 1828 * Rerun coordinator actions with failed option. 1829 * 1830 * @param jobId coordinator jobId 1831 * @param rerunType rerun type 'date' if -date is used, 'action-id' if -action is used 1832 * @param scope rerun scope for date or actionIds 1833 * @param refresh true if -refresh is given in command option 1834 * @param noCleanup true if -nocleanup is given in command option 1835 * @param failed true if -failed is given in command option 1836 * @param props properties to use during rerun 1837 * @return new coordinator job execution 1838 * @throws OozieClientException thrown if the info could not be retrieved. 1839 */ 1840 public List<CoordinatorAction> reRunCoord(String jobId, String rerunType, String scope, boolean refresh, 1841 boolean noCleanup, boolean failed, Properties props) throws OozieClientException { 1842 return new CoordRerun(jobId, rerunType, scope, refresh, noCleanup, failed, props).call(); 1843 } 1844 1845 /** 1846 * Rerun bundle coordinators. 1847 * 1848 * @param jobId bundle jobId 1849 * @param coordScope rerun scope for coordinator jobs 1850 * @param dateScope rerun scope for date 1851 * @param refresh true if -refresh is given in command option 1852 * @param noCleanup true if -nocleanup is given in command option 1853 * @throws OozieClientException thrown if the info could not be retrieved. 1854 */ 1855 public Void reRunBundle(String jobId, String coordScope, String dateScope, boolean refresh, boolean noCleanup) 1856 throws OozieClientException { 1857 return new BundleRerun(jobId, coordScope, dateScope, refresh, noCleanup).call(); 1858 } 1859 1860 /** 1861 * Return the info of the workflow jobs that match the filter. 1862 * 1863 * @param filter job filter. Refer to the {@link OozieClient} for the filter syntax. 1864 * @param start jobs offset, base 1. 1865 * @param len number of jobs to return. 1866 * @return a list with the workflow jobs info, without node details. 1867 * @throws OozieClientException thrown if the jobs info could not be retrieved. 1868 */ 1869 public List<WorkflowJob> getJobsInfo(String filter, int start, int len) throws OozieClientException { 1870 return new JobsStatus(filter, start, len).call(); 1871 } 1872 1873 /** 1874 * Return the info of the workflow jobs that match the filter. 1875 * <p> 1876 * It returns the first 100 jobs that match the filter. 1877 * 1878 * @param filter job filter. Refer to the {@link OozieClient} for the filter syntax. 1879 * @return a list with the workflow jobs info, without node details. 1880 * @throws OozieClientException thrown if the jobs info could not be retrieved. 1881 */ 1882 public List<WorkflowJob> getJobsInfo(String filter) throws OozieClientException { 1883 return getJobsInfo(filter, 1, 50); 1884 } 1885 1886 /** 1887 * Sla enable alert. 1888 * 1889 * @param jobIds the job ids 1890 * @param actions comma separated list of action ids or action id ranges 1891 * @param dates comma separated list of the nominal times 1892 * @throws OozieClientException the oozie client exception 1893 */ 1894 public void slaEnableAlert(String jobIds, String actions, String dates) throws OozieClientException { 1895 new UpdateSLA(RestConstants.SLA_ENABLE_ALERT, jobIds, actions, dates, null).call(); 1896 } 1897 1898 /** 1899 * Sla enable alert for bundle with coord name/id. 1900 * 1901 * @param bundleId the bundle id 1902 * @param actions comma separated list of action ids or action id ranges 1903 * @param dates comma separated list of the nominal times 1904 * @param coords the coordinators 1905 * @throws OozieClientException the oozie client exception 1906 */ 1907 public void slaEnableAlert(String bundleId, String actions, String dates, String coords) 1908 throws OozieClientException { 1909 new UpdateSLA(RestConstants.SLA_ENABLE_ALERT, bundleId, actions, dates, coords).call(); 1910 } 1911 1912 /** 1913 * Sla disable alert. 1914 * 1915 * @param jobIds the job ids 1916 * @param actions comma separated list of action ids or action id ranges 1917 * @param dates comma separated list of the nominal times 1918 * @throws OozieClientException the oozie client exception 1919 */ 1920 public void slaDisableAlert(String jobIds, String actions, String dates) throws OozieClientException { 1921 new UpdateSLA(RestConstants.SLA_DISABLE_ALERT, jobIds, actions, dates, null).call(); 1922 } 1923 1924 /** 1925 * Sla disable alert for bundle with coord name/id. 1926 * 1927 * @param bundleId the bundle id 1928 * @param actions comma separated list of action ids or action id ranges 1929 * @param dates comma separated list of the nominal times 1930 * @param coords the coordinators 1931 * @throws OozieClientException the oozie client exception 1932 */ 1933 public void slaDisableAlert(String bundleId, String actions, String dates, String coords) 1934 throws OozieClientException { 1935 new UpdateSLA(RestConstants.SLA_DISABLE_ALERT, bundleId, actions, dates, coords).call(); 1936 } 1937 1938 /** 1939 * Sla change definations. 1940 * SLA change definition parameters can be [<key>=<value>,...<key>=<value>] 1941 * Supported parameter key names are should-start, should-end and max-duration 1942 * @param jobIds the job ids 1943 * @param actions comma separated list of action ids or action id ranges. 1944 * @param dates comma separated list of the nominal times 1945 * @param newSlaParams the new sla params 1946 * @throws OozieClientException the oozie client exception 1947 */ 1948 public void slaChange(String jobIds, String actions, String dates, String newSlaParams) throws OozieClientException { 1949 new UpdateSLA(RestConstants.SLA_CHANGE, jobIds, actions, dates, null, newSlaParams).call(); 1950 } 1951 1952 /** 1953 * Sla change defination for bundle with coord name/id. 1954 * SLA change definition parameters can be [<key>=<value>,...<key>=<value>] 1955 * Supported parameter key names are should-start, should-end and max-duration 1956 * @param bundleId the bundle id 1957 * @param actions comma separated list of action ids or action id ranges 1958 * @param dates comma separated list of the nominal times 1959 * @param coords the coords 1960 * @param newSlaParams the new sla params 1961 * @throws OozieClientException the oozie client exception 1962 */ 1963 public void slaChange(String bundleId, String actions, String dates, String coords, String newSlaParams) 1964 throws OozieClientException { 1965 new UpdateSLA(RestConstants.SLA_CHANGE, bundleId, actions, dates, coords, newSlaParams).call(); 1966 } 1967 1968 /** 1969 * Sla change with new sla param as hasmap. 1970 * Supported parameter key names are should-start, should-end and max-duration 1971 * @param bundleId the bundle id 1972 * @param actions comma separated list of action ids or action id ranges 1973 * @param dates comma separated list of the nominal times 1974 * @param coords the coords 1975 * @param newSlaParams the new sla params 1976 * @throws OozieClientException the oozie client exception 1977 */ 1978 public void slaChange(String bundleId, String actions, String dates, String coords, Map<String, String> newSlaParams) 1979 throws OozieClientException { 1980 new UpdateSLA(RestConstants.SLA_CHANGE, bundleId, actions, dates, coords, mapToString(newSlaParams)).call(); 1981 } 1982 1983 /** 1984 * Convert Map to string. 1985 * 1986 * @param map the map 1987 * @return the string 1988 */ 1989 protected String mapToString(Map<String, String> map) { 1990 StringBuilder sb = new StringBuilder(); 1991 Iterator<Entry<String, String>> it = map.entrySet().iterator(); 1992 while (it.hasNext()) { 1993 Entry<String, String> e = (Entry<String, String>) it.next(); 1994 sb.append(e.getKey()).append("=").append(e.getValue()).append(";"); 1995 } 1996 return sb.toString(); 1997 } 1998 1999 private class UpdateSLA extends ClientCallable<Void> { 2000 2001 UpdateSLA(String action, String jobIds, String coordActions, String dates, String coords) { 2002 super("PUT", RestConstants.JOB, notEmpty(jobIds, "jobIds"), prepareParams(RestConstants.ACTION_PARAM, 2003 action, RestConstants.JOB_COORD_SCOPE_ACTION_LIST, coordActions, RestConstants.JOB_COORD_SCOPE_DATE, 2004 dates, RestConstants.COORDINATORS_PARAM, coords)); 2005 } 2006 2007 UpdateSLA(String action, String jobIds, String coordActions, String dates, String coords, String newSlaParams) { 2008 super("PUT", RestConstants.JOB, notEmpty(jobIds, "jobIds"), prepareParams(RestConstants.ACTION_PARAM, 2009 action, RestConstants.JOB_COORD_SCOPE_ACTION_LIST, coordActions, RestConstants.JOB_COORD_SCOPE_DATE, 2010 dates, RestConstants.COORDINATORS_PARAM, coords, RestConstants.JOB_CHANGE_VALUE, newSlaParams)); 2011 } 2012 2013 @Override 2014 protected Void call(HttpURLConnection conn) throws IOException, OozieClientException { 2015 conn.setRequestProperty("content-type", RestConstants.XML_CONTENT_TYPE); 2016 if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) { 2017 System.out.println("Done"); 2018 } 2019 else { 2020 handleError(conn); 2021 } 2022 return null; 2023 } 2024 } 2025 2026 /** 2027 * Print sla info about coordinator and workflow jobs and actions. 2028 * 2029 * @param start starting offset 2030 * @param len number of results 2031 * @param filter filters to use. Elements must be semicolon-separated name=value pairs 2032 * @throws OozieClientException thrown if the sla info could not be retrieved 2033 */ 2034 public void getSlaInfo(int start, int len, String filter) throws OozieClientException { 2035 new SlaInfo(start, len, filter).call(); 2036 } 2037 2038 private class SlaInfo extends ClientCallable<Void> { 2039 2040 SlaInfo(int start, int len, String filter) { 2041 super("GET", WS_PROTOCOL_VERSION_1, RestConstants.SLA, "", prepareParams(RestConstants.SLA_GT_SEQUENCE_ID, 2042 Integer.toString(start), RestConstants.MAX_EVENTS, Integer.toString(len), 2043 RestConstants.JOBS_FILTER_PARAM, filter)); 2044 } 2045 2046 @Override 2047 protected Void call(HttpURLConnection conn) throws IOException, OozieClientException { 2048 conn.setRequestProperty("content-type", RestConstants.XML_CONTENT_TYPE); 2049 if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) { 2050 BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream())); 2051 String line = null; 2052 while ((line = br.readLine()) != null) { 2053 System.out.println(line); 2054 } 2055 } 2056 else { 2057 handleError(conn); 2058 } 2059 return null; 2060 } 2061 } 2062 2063 private class JobIdAction extends ClientCallable<String> { 2064 2065 JobIdAction(String externalId) { 2066 super("GET", RestConstants.JOBS, "", prepareParams(RestConstants.JOBTYPE_PARAM, "wf", 2067 RestConstants.JOBS_EXTERNAL_ID_PARAM, externalId)); 2068 } 2069 2070 @Override 2071 protected String call(HttpURLConnection conn) throws IOException, OozieClientException { 2072 if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) { 2073 Reader reader = new InputStreamReader(conn.getInputStream()); 2074 JSONObject json = (JSONObject) JSONValue.parse(reader); 2075 return (String) json.get(JsonTags.JOB_ID); 2076 } 2077 else { 2078 handleError(conn); 2079 } 2080 return null; 2081 } 2082 } 2083 2084 /** 2085 * Return the workflow job Id for an external Id. 2086 * <p> 2087 * The external Id must have provided at job creation time. 2088 * 2089 * @param externalId external Id given at job creation time. 2090 * @return the workflow job Id for an external Id, <code>null</code> if none. 2091 * @throws OozieClientException thrown if the operation could not be done. 2092 */ 2093 public String getJobId(String externalId) throws OozieClientException { 2094 return new JobIdAction(externalId).call(); 2095 } 2096 2097 private class SetSystemMode extends ClientCallable<Void> { 2098 2099 public SetSystemMode(SYSTEM_MODE status) { 2100 super("PUT", RestConstants.ADMIN, RestConstants.ADMIN_STATUS_RESOURCE, prepareParams( 2101 RestConstants.ADMIN_SYSTEM_MODE_PARAM, status + "")); 2102 } 2103 2104 @Override 2105 public Void call(HttpURLConnection conn) throws IOException, OozieClientException { 2106 if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) { 2107 handleError(conn); 2108 } 2109 return null; 2110 } 2111 } 2112 2113 /** 2114 * Enable or disable safe mode. Used by OozieCLI. In safe mode, Oozie would not accept any commands except status 2115 * command to change and view the safe mode status. 2116 * 2117 * @param status true to enable safe mode, false to disable safe mode. 2118 * @throws OozieClientException if it fails to set the safe mode status. 2119 */ 2120 public void setSystemMode(SYSTEM_MODE status) throws OozieClientException { 2121 new SetSystemMode(status).call(); 2122 } 2123 2124 private class GetSystemMode extends ClientCallable<SYSTEM_MODE> { 2125 2126 GetSystemMode() { 2127 super("GET", RestConstants.ADMIN, RestConstants.ADMIN_STATUS_RESOURCE, prepareParams()); 2128 } 2129 2130 @Override 2131 protected SYSTEM_MODE call(HttpURLConnection conn) throws IOException, OozieClientException { 2132 if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) { 2133 Reader reader = new InputStreamReader(conn.getInputStream()); 2134 JSONObject json = (JSONObject) JSONValue.parse(reader); 2135 return SYSTEM_MODE.valueOf((String) json.get(JsonTags.OOZIE_SYSTEM_MODE)); 2136 } 2137 else { 2138 handleError(conn); 2139 } 2140 return SYSTEM_MODE.NORMAL; 2141 } 2142 } 2143 2144 /** 2145 * Returns if Oozie is in safe mode or not. 2146 * 2147 * @return true if safe mode is ON<br> 2148 * false if safe mode is OFF 2149 * @throws OozieClientException throw if it could not obtain the safe mode status. 2150 */ 2151 /* 2152 * public boolean isInSafeMode() throws OozieClientException { return new GetSafeMode().call(); } 2153 */ 2154 public SYSTEM_MODE getSystemMode() throws OozieClientException { 2155 return new GetSystemMode().call(); 2156 } 2157 2158 public String updateShareLib() throws OozieClientException { 2159 return new UpdateSharelib().call(); 2160 } 2161 2162 public String listShareLib(String sharelibKey) throws OozieClientException { 2163 return new ListShareLib(sharelibKey).call(); 2164 } 2165 2166 private class GetBuildVersion extends ClientCallable<String> { 2167 2168 GetBuildVersion() { 2169 super("GET", RestConstants.ADMIN, RestConstants.ADMIN_BUILD_VERSION_RESOURCE, prepareParams()); 2170 } 2171 2172 @Override 2173 protected String call(HttpURLConnection conn) throws IOException, OozieClientException { 2174 if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) { 2175 Reader reader = new InputStreamReader(conn.getInputStream()); 2176 JSONObject json = (JSONObject) JSONValue.parse(reader); 2177 return json.get(JsonTags.BUILD_INFO).toString(); 2178 } 2179 else { 2180 handleError(conn); 2181 } 2182 return null; 2183 } 2184 } 2185 2186 private class ValidateXML extends ClientCallable<String> { 2187 2188 String file = null; 2189 2190 ValidateXML(String file, String user) { 2191 super("POST", RestConstants.VALIDATE, "", 2192 prepareParams(RestConstants.FILE_PARAM, file, RestConstants.USER_PARAM, user)); 2193 this.file = file; 2194 } 2195 2196 @Override 2197 protected String call(HttpURLConnection conn) throws IOException, OozieClientException { 2198 conn.setRequestProperty("content-type", RestConstants.XML_CONTENT_TYPE); 2199 if (file.startsWith("/")) { 2200 FileInputStream fi = new FileInputStream(new File(file)); 2201 byte[] buffer = new byte[1024]; 2202 int n = 0; 2203 while (-1 != (n = fi.read(buffer))) { 2204 conn.getOutputStream().write(buffer, 0, n); 2205 } 2206 } 2207 if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) { 2208 Reader reader = new InputStreamReader(conn.getInputStream()); 2209 JSONObject json = (JSONObject) JSONValue.parse(reader); 2210 return (String) json.get(JsonTags.VALIDATE); 2211 } 2212 else if ((conn.getResponseCode() == HttpURLConnection.HTTP_NOT_FOUND)) { 2213 return null; 2214 } 2215 else { 2216 handleError(conn); 2217 } 2218 return null; 2219 } 2220 } 2221 2222 2223 private class UpdateSharelib extends ClientCallable<String> { 2224 2225 UpdateSharelib() { 2226 super("GET", RestConstants.ADMIN, RestConstants.ADMIN_UPDATE_SHARELIB, prepareParams( 2227 RestConstants.ALL_SERVER_REQUEST, "true")); 2228 } 2229 2230 @Override 2231 protected String call(HttpURLConnection conn) throws IOException, OozieClientException { 2232 StringBuffer bf = new StringBuffer(); 2233 if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) { 2234 Reader reader = new InputStreamReader(conn.getInputStream()); 2235 Object sharelib = (Object) JSONValue.parse(reader); 2236 bf.append("[ShareLib update status]").append(System.getProperty("line.separator")); 2237 if (sharelib instanceof JSONArray) { 2238 for (Object o : ((JSONArray) sharelib)) { 2239 JSONObject obj = (JSONObject) ((JSONObject) o).get(JsonTags.SHARELIB_LIB_UPDATE); 2240 for (Object key : obj.keySet()) { 2241 bf.append("\t").append(key).append(" = ").append(obj.get(key)) 2242 .append(System.getProperty("line.separator")); 2243 } 2244 bf.append(System.getProperty("line.separator")); 2245 } 2246 } 2247 else{ 2248 JSONObject obj = (JSONObject) ((JSONObject) sharelib).get(JsonTags.SHARELIB_LIB_UPDATE); 2249 for (Object key : obj.keySet()) { 2250 bf.append("\t").append(key).append(" = ").append(obj.get(key)) 2251 .append(System.getProperty("line.separator")); 2252 } 2253 bf.append(System.getProperty("line.separator")); 2254 } 2255 return bf.toString(); 2256 } 2257 else { 2258 handleError(conn); 2259 } 2260 return null; 2261 } 2262 } 2263 2264 private class ListShareLib extends ClientCallable<String> { 2265 2266 ListShareLib(String sharelibKey) { 2267 super("GET", RestConstants.ADMIN, RestConstants.ADMIN_LIST_SHARELIB, prepareParams( 2268 RestConstants.SHARE_LIB_REQUEST_KEY, sharelibKey)); 2269 } 2270 2271 @Override 2272 protected String call(HttpURLConnection conn) throws IOException, OozieClientException { 2273 2274 if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) { 2275 StringBuffer bf = new StringBuffer(); 2276 Reader reader = new InputStreamReader(conn.getInputStream()); 2277 JSONObject json = (JSONObject) JSONValue.parse(reader); 2278 Object sharelib = json.get(JsonTags.SHARELIB_LIB); 2279 bf.append("[Available ShareLib]").append(System.getProperty("line.separator")); 2280 if (sharelib instanceof JSONArray) { 2281 for (Object o : ((JSONArray) sharelib)) { 2282 JSONObject obj = (JSONObject) o; 2283 bf.append(obj.get(JsonTags.SHARELIB_LIB_NAME)) 2284 .append(System.getProperty("line.separator")); 2285 if (obj.get(JsonTags.SHARELIB_LIB_FILES) != null) { 2286 for (Object file : ((JSONArray) obj.get(JsonTags.SHARELIB_LIB_FILES))) { 2287 bf.append("\t").append(file).append(System.getProperty("line.separator")); 2288 } 2289 } 2290 } 2291 return bf.toString(); 2292 } 2293 } 2294 else { 2295 handleError(conn); 2296 } 2297 return null; 2298 } 2299 2300 } 2301 2302 public String purgeCommand(String purgeOptions) throws OozieClientException { 2303 String workflowAge = ""; 2304 String coordAge = ""; 2305 String bundleAge = ""; 2306 String purgeLimit = ""; 2307 String oldCoordAction = ""; 2308 if (purgeOptions != null) { 2309 String options[] = purgeOptions.split(";"); 2310 for (String option : options) { 2311 String pair[] = option.split("="); 2312 if (pair.length < 2) { 2313 throw new OozieClientException(OozieClientException.INVALID_INPUT, 2314 "Invalid purge option pair [" + option + "] specified."); 2315 } 2316 String key = pair[0].toLowerCase(); 2317 String value = pair[1]; 2318 switch (key) { 2319 case "wf": 2320 workflowAge = String.valueOf(ValidationUtil.parsePositiveInteger(value)); 2321 break; 2322 case "coord": 2323 coordAge = String.valueOf(ValidationUtil.parsePositiveInteger(value)); 2324 break; 2325 case "bundle": 2326 bundleAge = String.valueOf(ValidationUtil.parsePositiveInteger(value)); 2327 break; 2328 case "limit": 2329 purgeLimit = String.valueOf(ValidationUtil.parsePositiveInteger(value)); 2330 break; 2331 case "oldcoordaction": 2332 oldCoordAction = value; 2333 break; 2334 default: 2335 throw new OozieClientException(OozieClientException.INVALID_INPUT, 2336 "Invalid purge option [" + key + "] specified."); 2337 } 2338 } 2339 } 2340 PurgeCommand purgeCommand = new PurgeCommand(workflowAge, coordAge, bundleAge, purgeLimit, oldCoordAction); 2341 return purgeCommand.call(); 2342 } 2343 2344 private class PurgeCommand extends ClientCallable<String> { 2345 2346 PurgeCommand(String workflowAge, String coordAge, String bundleAge, String purgeLimit, String oldCoordAction) { 2347 super("PUT", RestConstants.ADMIN, RestConstants.ADMIN_PURGE, prepareParams( 2348 RestConstants.PURGE_WF_AGE, workflowAge, 2349 RestConstants.PURGE_COORD_AGE, coordAge, 2350 RestConstants.PURGE_BUNDLE_AGE, bundleAge, 2351 RestConstants.PURGE_LIMIT, purgeLimit, 2352 RestConstants.PURGE_OLD_COORD_ACTION, oldCoordAction 2353 )); 2354 } 2355 2356 @Override 2357 protected String call(HttpURLConnection conn) throws IOException, OozieClientException { 2358 if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) { 2359 Reader reader = new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8); 2360 JSONObject jsonObject = (JSONObject) JSONValue.parse(reader); 2361 Object msg = jsonObject.get(JsonTags.PURGE); 2362 return msg.toString(); 2363 } else { 2364 handleError(conn); 2365 } 2366 return null; 2367 } 2368 } 2369 2370 /** 2371 * Return the Oozie server build version. 2372 * 2373 * @return the Oozie server build version. 2374 * @throws OozieClientException throw if the server build version could not be retrieved. 2375 */ 2376 public String getServerBuildVersion() throws OozieClientException { 2377 return new GetBuildVersion().call(); 2378 } 2379 2380 /** 2381 * Return the Oozie client build version. 2382 * 2383 * @return the Oozie client build version. 2384 */ 2385 public String getClientBuildVersion() { 2386 return BuildInfo.getBuildInfo().getProperty(BuildInfo.BUILD_VERSION); 2387 } 2388 2389 /** 2390 * Return the workflow application is valid. 2391 * 2392 * @param file local file or hdfs file. 2393 * @return the workflow application is valid. 2394 * @throws OozieClientException throw if it the workflow application's validation could not be retrieved. 2395 */ 2396 public String validateXML(String file) throws OozieClientException { 2397 String fileName = file; 2398 if (file.startsWith("file://")) { 2399 fileName = file.substring(7, file.length()); 2400 } 2401 if (!fileName.contains("://")) { 2402 File f = new File(fileName); 2403 if (!f.isFile()) { 2404 throw new OozieClientException("File error", "File does not exist : " + f.getAbsolutePath()); 2405 } 2406 fileName = f.getAbsolutePath(); 2407 } 2408 String user = USER_NAME_TL.get(); 2409 if (user == null) { 2410 user = System.getProperty("user.name"); 2411 } 2412 return new ValidateXML(fileName, user).call(); 2413 } 2414 2415 /** 2416 * Return the info of the coordinator jobs that match the filter. 2417 * 2418 * @param filter job filter. Refer to the {@link OozieClient} for the filter syntax. 2419 * @param start jobs offset, base 1. 2420 * @param len number of jobs to return. 2421 * @return a list with the coordinator jobs info 2422 * @throws OozieClientException thrown if the jobs info could not be retrieved. 2423 */ 2424 public List<CoordinatorJob> getCoordJobsInfo(String filter, int start, int len) throws OozieClientException { 2425 return new CoordJobsStatus(filter, start, len).call(); 2426 } 2427 2428 /** 2429 * Return the info of the bundle jobs that match the filter. 2430 * 2431 * @param filter job filter. Refer to the {@link OozieClient} for the filter syntax. 2432 * @param start jobs offset, base 1. 2433 * @param len number of jobs to return. 2434 * @return a list with the bundle jobs info 2435 * @throws OozieClientException thrown if the jobs info could not be retrieved. 2436 */ 2437 public List<BundleJob> getBundleJobsInfo(String filter, int start, int len) throws OozieClientException { 2438 return new BundleJobsStatus(filter, start, len).call(); 2439 } 2440 2441 public List<BulkResponse> getBulkInfo(String filter, int start, int len) throws OozieClientException { 2442 return new BulkResponseStatus(filter, start, len).call(); 2443 } 2444 2445 /** 2446 * Poll a job (Workflow Job ID, Coordinator Job ID, Coordinator Action ID, or Bundle Job ID) and return when it has reached a 2447 * terminal state. 2448 * (i.e. FAILED, KILLED, SUCCEEDED) 2449 * 2450 * @param id The Job ID 2451 * @param timeout timeout in minutes (negative values indicate no timeout) 2452 * @param interval polling interval in minutes (must be positive) 2453 * @param verbose if true, the current status will be printed out at each poll; if false, no output 2454 * @throws OozieClientException thrown if the job's status could not be retrieved 2455 */ 2456 public void pollJob(String id, int timeout, int interval, boolean verbose) throws OozieClientException { 2457 notEmpty("id", id); 2458 if (interval < 1) { 2459 throw new IllegalArgumentException("interval must be a positive integer"); 2460 } 2461 boolean noTimeout = (timeout < 1); 2462 long endTime = System.currentTimeMillis() + timeout * 60 * 1000; 2463 interval *= 60 * 1000; 2464 2465 final Set<String> completedStatuses; 2466 if (id.endsWith("-W")) { 2467 completedStatuses = COMPLETED_WF_STATUSES; 2468 } else if (id.endsWith("-C")) { 2469 completedStatuses = COMPLETED_COORD_AND_BUNDLE_STATUSES; 2470 } else if (id.endsWith("-B")) { 2471 completedStatuses = COMPLETED_COORD_AND_BUNDLE_STATUSES; 2472 } else if (id.contains("-C@")) { 2473 completedStatuses = COMPLETED_COORD_ACTION_STATUSES; 2474 } else { 2475 throw new IllegalArgumentException("invalid job type"); 2476 } 2477 2478 String status = getStatus(id); 2479 if (verbose) { 2480 System.out.println(status); 2481 } 2482 while(!completedStatuses.contains(status) && (noTimeout || System.currentTimeMillis() <= endTime)) { 2483 try { 2484 Thread.sleep(interval); 2485 } catch (InterruptedException ie) { 2486 // ignore 2487 } 2488 status = getStatus(id); 2489 if (verbose) { 2490 System.out.println(status); 2491 } 2492 } 2493 } 2494 2495 /** 2496 * Gets the status for a particular job (Workflow Job ID, Coordinator Job ID, Coordinator Action ID, or Bundle Job ID). 2497 * 2498 * @param jobId given jobId 2499 * @return the status 2500 * @throws OozieClientException thrown if the status could not be retrieved 2501 */ 2502 public String getStatus(String jobId) throws OozieClientException { 2503 return new Status(jobId).call(); 2504 } 2505 2506 private class Status extends ClientCallable<String> { 2507 2508 Status(String jobId) { 2509 super("GET", RestConstants.JOB, notEmpty(jobId, "jobId"), prepareParams(RestConstants.JOB_SHOW_PARAM, 2510 RestConstants.JOB_SHOW_STATUS)); 2511 } 2512 2513 @Override 2514 protected String call(HttpURLConnection conn) throws IOException, OozieClientException { 2515 if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) { 2516 Reader reader = new InputStreamReader(conn.getInputStream()); 2517 JSONObject json = (JSONObject) JSONValue.parse(reader); 2518 return (String) json.get(JsonTags.STATUS); 2519 } 2520 else { 2521 handleError(conn); 2522 } 2523 return null; 2524 } 2525 } 2526 2527 private class GetQueueDump extends ClientCallable<List<String>> { 2528 GetQueueDump() { 2529 super("GET", RestConstants.ADMIN, RestConstants.ADMIN_QUEUE_DUMP_RESOURCE, prepareParams()); 2530 } 2531 2532 @Override 2533 protected List<String> call(HttpURLConnection conn) throws IOException, OozieClientException { 2534 if ((conn.getResponseCode() != HttpURLConnection.HTTP_OK)) { 2535 handleError(conn); 2536 return null; 2537 } 2538 2539 Reader reader = new InputStreamReader(conn.getInputStream()); 2540 JSONObject json = (JSONObject) JSONValue.parse(reader); 2541 List<String> queueDumpMessages = Lists.newArrayList(); 2542 2543 addSeparator(queueDumpMessages); 2544 2545 addQueueMessages(json, 2546 queueDumpMessages, 2547 JsonTags.QUEUE_DUMP, 2548 JsonTags.CALLABLE_DUMP, 2549 "[Server Queue Dump]:", 2550 "The queue dump is empty, nothing to display."); 2551 2552 addSeparator(queueDumpMessages); 2553 2554 addQueueMessages(json, 2555 queueDumpMessages, 2556 JsonTags.UNIQUE_MAP_DUMP, 2557 JsonTags.UNIQUE_ENTRY_DUMP, 2558 "[Server Uniqueness Map Dump]:", 2559 "The uniqueness map dump is empty, nothing to display."); 2560 2561 addSeparator(queueDumpMessages); 2562 2563 return queueDumpMessages; 2564 } 2565 2566 private void addQueueMessages(JSONObject json, 2567 List<String> queueMessages, 2568 String queueName, 2569 String queueValue, 2570 String headerMessage, 2571 String emptyMessage) { 2572 2573 JSONArray queueDumpArray = (JSONArray) json.get(queueName); 2574 queueMessages.add(headerMessage); 2575 2576 for (Object o : queueDumpArray) { 2577 JSONObject entry = (JSONObject) o; 2578 if (entry.get(queueValue) != null) { 2579 String value = (String) entry.get(queueValue); 2580 queueMessages.add(value); 2581 } 2582 } 2583 2584 if (queueDumpArray.isEmpty()) { 2585 queueMessages.add(emptyMessage); 2586 } 2587 } 2588 2589 private void addSeparator(List<String> list) { 2590 list.add("******************************************"); 2591 } 2592 } 2593 2594 /** 2595 * Return the Oozie queue's commands' dump 2596 * 2597 * @return the list of strings of callable identification in queue 2598 * @throws OozieClientException throw if it the queue dump could not be retrieved. 2599 */ 2600 public List<String> getQueueDump() throws OozieClientException { 2601 return new GetQueueDump().call(); 2602 } 2603 2604 private class GetAvailableOozieServers extends MapClientCallable { 2605 2606 GetAvailableOozieServers() { 2607 super("GET", RestConstants.ADMIN, RestConstants.ADMIN_AVAILABLE_OOZIE_SERVERS_RESOURCE, prepareParams()); 2608 } 2609 } 2610 2611 /** 2612 * Return the list of available Oozie servers. 2613 * 2614 * @return the list of available Oozie servers. 2615 * @throws OozieClientException throw if it the list of available Oozie servers could not be retrieved. 2616 */ 2617 public Map<String, String> getAvailableOozieServers() throws OozieClientException { 2618 return new GetAvailableOozieServers().call(); 2619 } 2620 2621 private class GetServerConfiguration extends MapClientCallable { 2622 2623 GetServerConfiguration() { 2624 super("GET", RestConstants.ADMIN, RestConstants.ADMIN_CONFIG_RESOURCE, prepareParams()); 2625 } 2626 } 2627 2628 /** 2629 * Return the Oozie system configuration. 2630 * 2631 * @return the Oozie system configuration. 2632 * @throws OozieClientException throw if the system configuration could not be retrieved. 2633 */ 2634 public Map<String, String> getServerConfiguration() throws OozieClientException { 2635 return new GetServerConfiguration().call(); 2636 } 2637 2638 private class GetJavaSystemProperties extends MapClientCallable { 2639 2640 GetJavaSystemProperties() { 2641 super("GET", RestConstants.ADMIN, RestConstants.ADMIN_JAVA_SYS_PROPS_RESOURCE, prepareParams()); 2642 } 2643 } 2644 2645 /** 2646 * Return the Oozie Java system properties. 2647 * 2648 * @return the Oozie Java system properties. 2649 * @throws OozieClientException throw if the system properties could not be retrieved. 2650 */ 2651 public Map<String, String> getJavaSystemProperties() throws OozieClientException { 2652 return new GetJavaSystemProperties().call(); 2653 } 2654 2655 private class GetOSEnv extends MapClientCallable { 2656 2657 GetOSEnv() { 2658 super("GET", RestConstants.ADMIN, RestConstants.ADMIN_OS_ENV_RESOURCE, prepareParams()); 2659 } 2660 } 2661 2662 /** 2663 * Return the Oozie system OS environment. 2664 * 2665 * @return the Oozie system OS environment. 2666 * @throws OozieClientException throw if the system OS environment could not be retrieved. 2667 */ 2668 public Map<String, String> getOSEnv() throws OozieClientException { 2669 return new GetOSEnv().call(); 2670 } 2671 2672 private class GetMetrics extends ClientCallable<Metrics> { 2673 2674 GetMetrics() { 2675 super("GET", RestConstants.ADMIN, RestConstants.ADMIN_METRICS_RESOURCE, prepareParams()); 2676 } 2677 2678 @Override 2679 protected Metrics call(HttpURLConnection conn) throws IOException, OozieClientException { 2680 if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) { 2681 Reader reader = new InputStreamReader(conn.getInputStream()); 2682 JSONObject json = (JSONObject) JSONValue.parse(reader); 2683 Metrics metrics = new Metrics(json); 2684 return metrics; 2685 } 2686 else if ((conn.getResponseCode() == HttpURLConnection.HTTP_UNAVAILABLE)) { 2687 // Use Instrumentation endpoint 2688 return null; 2689 } 2690 else { 2691 handleError(conn); 2692 } 2693 return null; 2694 } 2695 } 2696 2697 public class Metrics { 2698 private Map<String, Long> counters; 2699 private Map<String, Object> gauges; 2700 private Map<String, Timer> timers; 2701 private Map<String, Histogram> histograms; 2702 2703 @SuppressWarnings("unchecked") 2704 public Metrics(JSONObject json) { 2705 JSONObject jCounters = (JSONObject) json.get("counters"); 2706 counters = new HashMap<String, Long>(jCounters.size()); 2707 for (Object entO : jCounters.entrySet()) { 2708 Entry<String, JSONObject> ent = (Entry<String, JSONObject>) entO; 2709 counters.put(ent.getKey(), (Long)ent.getValue().get("count")); 2710 } 2711 2712 JSONObject jGuages = (JSONObject) json.get("gauges"); 2713 gauges = new HashMap<String, Object>(jGuages.size()); 2714 for (Object entO : jGuages.entrySet()) { 2715 Entry<String, JSONObject> ent = (Entry<String, JSONObject>) entO; 2716 gauges.put(ent.getKey(), ent.getValue().get("value")); 2717 } 2718 2719 JSONObject jTimers = (JSONObject) json.get("timers"); 2720 timers = new HashMap<String, Timer>(jTimers.size()); 2721 for (Object entO : jTimers.entrySet()) { 2722 Entry<String, JSONObject> ent = (Entry<String, JSONObject>) entO; 2723 timers.put(ent.getKey(), new Timer(ent.getValue())); 2724 } 2725 2726 JSONObject jHistograms = (JSONObject) json.get("histograms"); 2727 histograms = new HashMap<String, Histogram>(jHistograms.size()); 2728 for (Object entO : jHistograms.entrySet()) { 2729 Entry<String, JSONObject> ent = (Entry<String, JSONObject>) entO; 2730 histograms.put(ent.getKey(), new Histogram(ent.getValue())); 2731 } 2732 } 2733 2734 public Map<String, Long> getCounters() { 2735 return counters; 2736 } 2737 2738 public Map<String, Object> getGauges() { 2739 return gauges; 2740 } 2741 2742 public Map<String, Timer> getTimers() { 2743 return timers; 2744 } 2745 2746 public Map<String, Histogram> getHistograms() { 2747 return histograms; 2748 } 2749 2750 public class Timer extends Histogram { 2751 private double m15Rate; 2752 private double m5Rate; 2753 private double m1Rate; 2754 private double meanRate; 2755 private String durationUnits; 2756 private String rateUnits; 2757 2758 public Timer(JSONObject json) { 2759 super(json); 2760 m15Rate = Double.valueOf(json.get("m15_rate").toString()); 2761 m5Rate = Double.valueOf(json.get("m5_rate").toString()); 2762 m1Rate = Double.valueOf(json.get("m1_rate").toString()); 2763 meanRate = Double.valueOf(json.get("mean_rate").toString()); 2764 durationUnits = json.get("duration_units").toString(); 2765 rateUnits = json.get("rate_units").toString(); 2766 } 2767 2768 public double get15MinuteRate() { 2769 return m15Rate; 2770 } 2771 2772 public double get5MinuteRate() { 2773 return m5Rate; 2774 } 2775 2776 public double get1MinuteRate() { 2777 return m1Rate; 2778 } 2779 2780 public double getMeanRate() { 2781 return meanRate; 2782 } 2783 2784 public String getDurationUnits() { 2785 return durationUnits; 2786 } 2787 2788 public String getRateUnits() { 2789 return rateUnits; 2790 } 2791 2792 @Override 2793 public String toString() { 2794 StringBuilder sb = new StringBuilder(super.toString()); 2795 sb.append("\n\t15 minute rate : ").append(m15Rate); 2796 sb.append("\n\t5 minute rate : ").append(m5Rate); 2797 sb.append("\n\t1 minute rate : ").append(m15Rate); 2798 sb.append("\n\tmean rate : ").append(meanRate); 2799 sb.append("\n\tduration units : ").append(durationUnits); 2800 sb.append("\n\trate units : ").append(rateUnits); 2801 return sb.toString(); 2802 } 2803 } 2804 2805 public class Histogram { 2806 private double p999; 2807 private double p99; 2808 private double p98; 2809 private double p95; 2810 private double p75; 2811 private double p50; 2812 private double mean; 2813 private double min; 2814 private double max; 2815 private double stdDev; 2816 private long count; 2817 2818 public Histogram(JSONObject json) { 2819 p999 = Double.valueOf(json.get("p999").toString()); 2820 p99 = Double.valueOf(json.get("p99").toString()); 2821 p98 = Double.valueOf(json.get("p98").toString()); 2822 p95 = Double.valueOf(json.get("p95").toString()); 2823 p75 = Double.valueOf(json.get("p75").toString()); 2824 p50 = Double.valueOf(json.get("p50").toString()); 2825 mean = Double.valueOf(json.get("mean").toString()); 2826 min = Double.valueOf(json.get("min").toString()); 2827 max = Double.valueOf(json.get("max").toString()); 2828 stdDev = Double.valueOf(json.get("stddev").toString()); 2829 count = Long.valueOf(json.get("count").toString()); 2830 } 2831 2832 public double get999thPercentile() { 2833 return p999; 2834 } 2835 2836 public double get99thPercentile() { 2837 return p99; 2838 } 2839 2840 public double get98thPercentile() { 2841 return p98; 2842 } 2843 2844 public double get95thPercentile() { 2845 return p95; 2846 } 2847 2848 public double get75thPercentile() { 2849 return p75; 2850 } 2851 2852 public double get50thPercentile() { 2853 return p50; 2854 } 2855 2856 public double getMean() { 2857 return mean; 2858 } 2859 2860 public double getMin() { 2861 return min; 2862 } 2863 2864 public double getMax() { 2865 return max; 2866 } 2867 2868 public double getStandardDeviation() { 2869 return stdDev; 2870 } 2871 2872 public long getCount() { 2873 return count; 2874 } 2875 2876 @Override 2877 public String toString() { 2878 StringBuilder sb = new StringBuilder(); 2879 sb.append("\t999th percentile : ").append(p999); 2880 sb.append("\n\t99th percentile : ").append(p99); 2881 sb.append("\n\t98th percentile : ").append(p98); 2882 sb.append("\n\t95th percentile : ").append(p95); 2883 sb.append("\n\t75th percentile : ").append(p75); 2884 sb.append("\n\t50th percentile : ").append(p50); 2885 sb.append("\n\tmean : ").append(mean); 2886 sb.append("\n\tmax : ").append(max); 2887 sb.append("\n\tmin : ").append(min); 2888 sb.append("\n\tcount : ").append(count); 2889 sb.append("\n\tstandard deviation : ").append(stdDev); 2890 return sb.toString(); 2891 } 2892 } 2893 } 2894 2895 /** 2896 * Return the Oozie metrics. If null is returned, then try {@link #getInstrumentation()}. 2897 * 2898 * @return the Oozie metrics or null. 2899 * @throws OozieClientException throw if the metrics could not be retrieved. 2900 */ 2901 public Metrics getMetrics() throws OozieClientException { 2902 return new GetMetrics().call(); 2903 } 2904 2905 private class GetInstrumentation extends ClientCallable<Instrumentation> { 2906 2907 GetInstrumentation() { 2908 super("GET", RestConstants.ADMIN, RestConstants.ADMIN_INSTRUMENTATION_RESOURCE, prepareParams()); 2909 } 2910 2911 @Override 2912 protected Instrumentation call(HttpURLConnection conn) throws IOException, OozieClientException { 2913 if ((conn.getResponseCode() == HttpURLConnection.HTTP_OK)) { 2914 Reader reader = new InputStreamReader(conn.getInputStream()); 2915 JSONObject json = (JSONObject) JSONValue.parse(reader); 2916 Instrumentation instrumentation = new Instrumentation(json); 2917 return instrumentation; 2918 } 2919 else if ((conn.getResponseCode() == HttpURLConnection.HTTP_UNAVAILABLE)) { 2920 // Use Metrics endpoint 2921 return null; 2922 } 2923 else { 2924 handleError(conn); 2925 } 2926 return null; 2927 } 2928 } 2929 2930 public class Instrumentation { 2931 private Map<String, Long> counters; 2932 private Map<String, Object> variables; 2933 private Map<String, Double> samplers; 2934 private Map<String, Timer> timers; 2935 2936 public Instrumentation(JSONObject json) { 2937 JSONArray jCounters = (JSONArray) json.get("counters"); 2938 counters = new HashMap<String, Long>(jCounters.size()); 2939 for (Object groupO : jCounters) { 2940 JSONObject group = (JSONObject) groupO; 2941 String groupName = group.get("group").toString() + "."; 2942 JSONArray data = (JSONArray) group.get("data"); 2943 for (Object datO : data) { 2944 JSONObject dat = (JSONObject) datO; 2945 counters.put(groupName + dat.get("name").toString(), Long.valueOf(dat.get("value").toString())); 2946 } 2947 } 2948 2949 JSONArray jVariables = (JSONArray) json.get("variables"); 2950 variables = new HashMap<String, Object>(jVariables.size()); 2951 for (Object groupO : jVariables) { 2952 JSONObject group = (JSONObject) groupO; 2953 String groupName = group.get("group").toString() + "."; 2954 JSONArray data = (JSONArray) group.get("data"); 2955 for (Object datO : data) { 2956 JSONObject dat = (JSONObject) datO; 2957 variables.put(groupName + dat.get("name").toString(), dat.get("value")); 2958 } 2959 } 2960 2961 JSONArray jSamplers = (JSONArray) json.get("samplers"); 2962 samplers = new HashMap<String, Double>(jSamplers.size()); 2963 for (Object groupO : jSamplers) { 2964 JSONObject group = (JSONObject) groupO; 2965 String groupName = group.get("group").toString() + "."; 2966 JSONArray data = (JSONArray) group.get("data"); 2967 for (Object datO : data) { 2968 JSONObject dat = (JSONObject) datO; 2969 samplers.put(groupName + dat.get("name").toString(), Double.valueOf(dat.get("value").toString())); 2970 } 2971 } 2972 2973 JSONArray jTimers = (JSONArray) json.get("timers"); 2974 timers = new HashMap<String, Timer>(jTimers.size()); 2975 for (Object groupO : jTimers) { 2976 JSONObject group = (JSONObject) groupO; 2977 String groupName = group.get("group").toString() + "."; 2978 JSONArray data = (JSONArray) group.get("data"); 2979 for (Object datO : data) { 2980 JSONObject dat = (JSONObject) datO; 2981 timers.put(groupName + dat.get("name").toString(), new Timer(dat)); 2982 } 2983 } 2984 } 2985 2986 public class Timer { 2987 private double ownTimeStdDev; 2988 private long ownTimeAvg; 2989 private long ownMaxTime; 2990 private long ownMinTime; 2991 private double totalTimeStdDev; 2992 private long totalTimeAvg; 2993 private long totalMaxTime; 2994 private long totalMinTime; 2995 private long ticks; 2996 2997 public Timer(JSONObject json) { 2998 ownTimeStdDev = Double.valueOf(json.get("ownTimeStdDev").toString()); 2999 ownTimeAvg = Long.valueOf(json.get("ownTimeAvg").toString()); 3000 ownMaxTime = Long.valueOf(json.get("ownMaxTime").toString()); 3001 ownMinTime = Long.valueOf(json.get("ownMinTime").toString()); 3002 totalTimeStdDev = Double.valueOf(json.get("totalTimeStdDev").toString()); 3003 totalTimeAvg = Long.valueOf(json.get("totalTimeAvg").toString()); 3004 totalMaxTime = Long.valueOf(json.get("totalMaxTime").toString()); 3005 totalMinTime = Long.valueOf(json.get("totalMinTime").toString()); 3006 ticks = Long.valueOf(json.get("ticks").toString()); 3007 } 3008 3009 public double getOwnTimeStandardDeviation() { 3010 return ownTimeStdDev; 3011 } 3012 3013 public long getOwnTimeAverage() { 3014 return ownTimeAvg; 3015 } 3016 3017 public long getOwnMaxTime() { 3018 return ownMaxTime; 3019 } 3020 3021 public long getOwnMinTime() { 3022 return ownMinTime; 3023 } 3024 3025 public double getTotalTimeStandardDeviation() { 3026 return totalTimeStdDev; 3027 } 3028 3029 public long getTotalTimeAverage() { 3030 return totalTimeAvg; 3031 } 3032 3033 public long getTotalMaxTime() { 3034 return totalMaxTime; 3035 } 3036 3037 public long getTotalMinTime() { 3038 return totalMinTime; 3039 } 3040 3041 public long getTicks() { 3042 return ticks; 3043 } 3044 3045 @Override 3046 public String toString() { 3047 StringBuilder sb = new StringBuilder(); 3048 sb.append("\town time standard deviation : ").append(ownTimeStdDev); 3049 sb.append("\n\town average time : ").append(ownTimeAvg); 3050 sb.append("\n\town max time : ").append(ownMaxTime); 3051 sb.append("\n\town min time : ").append(ownMinTime); 3052 sb.append("\n\ttotal time standard deviation : ").append(totalTimeStdDev); 3053 sb.append("\n\ttotal average time : ").append(totalTimeAvg); 3054 sb.append("\n\ttotal max time : ").append(totalMaxTime); 3055 sb.append("\n\ttotal min time : ").append(totalMinTime); 3056 sb.append("\n\tticks : ").append(ticks); 3057 return sb.toString(); 3058 } 3059 } 3060 3061 public Map<String, Long> getCounters() { 3062 return counters; 3063 } 3064 3065 public Map<String, Object> getVariables() { 3066 return variables; 3067 } 3068 3069 public Map<String, Double> getSamplers() { 3070 return samplers; 3071 } 3072 3073 public Map<String, Timer> getTimers() { 3074 return timers; 3075 } 3076 } 3077 3078 /** 3079 * Return the Oozie instrumentation. If null is returned, then try {@link #getMetrics()}. 3080 * 3081 * @return the Oozie intstrumentation or null. 3082 * @throws OozieClientException throw if the intstrumentation could not be retrieved. 3083 */ 3084 public Instrumentation getInstrumentation() throws OozieClientException { 3085 return new GetInstrumentation().call(); 3086 } 3087 3088 /** 3089 * Check if the string is not null or not empty. 3090 * 3091 * @param str the string to check 3092 * @param name the name to present in the error message 3093 * @return string 3094 */ 3095 public static String notEmpty(String str, String name) { 3096 if (str == null) { 3097 throw new IllegalArgumentException(name + " cannot be null"); 3098 } 3099 if (str.length() == 0) { 3100 throw new IllegalArgumentException(name + " cannot be empty"); 3101 } 3102 return str; 3103 } 3104 3105 /** 3106 * Check if the object is not null. 3107 * 3108 * @param <T> the type of the object 3109 * @param obj the object to check 3110 * @param name the name to present in the error message 3111 * @return string 3112 */ 3113 public static <T> T notNull(T obj, String name) { 3114 if (obj == null) { 3115 throw new IllegalArgumentException(name + " cannot be null"); 3116 } 3117 return obj; 3118 } 3119 3120}