@@ -171,32 +183,52 @@ Constructor for class org.rundeck.api.parser.ExecutionParser(String) -
Constructor for class org.rundeck.api.parser.ExecutionParser
Export the definitions of the jobs that belongs to the given project, and matches the given criteria (jobFilter,
- groupPath and jobIds), as an XML file
-
Get the executions of the given job
@@ -393,6 +434,9 @@ Method in class org.rundeck.api.domain.getShortDuration() -
Method in class org.rundeck.api.domain.RundeckExecution
Get system informations about the RunDeck server
@@ -452,6 +499,9 @@ Method in class org.rundeck.api.domain.hashCode() -
Method in class org.rundeck.api.domain.RundeckJob
Returns an array containing the constants of this enum type, in
@@ -906,6 +1006,14 @@ the order they are declared.
Static method in enum org.rundeck.api.domain.RundeckExecution.ExecutionStatus
Returns an array containing the constants of this enum type, in
the order they are declared.
+
Returns an array containing the constants of this enum type, in
+the order they are declared. This method may be used to iterate
+over the constants as follows:
+
+for (FileType c : FileType.values())
+ System.out.println(c);
+
+
+
+
+
Returns:
an array containing the constants of this enum type, in
+the order they are declared
Returns the enum constant of this type with the specified name.
+The string must match exactly an identifier used to declare an
+enum constant in this type. (Extraneous whitespace characters are
+not permitted.)
+
+
+
Parameters:
name - the name of the enum constant to be returned.
+
exportJobs(FileType format,
+ String project,
+ String jobFilter,
+ String groupPath,
+ String... jobIds)
+
+
+ Export the definitions of the jobs that belongs to the given project, and matches the given criteria (jobFilter,
+ groupPath and jobIds)
exportJobs(String format,
+ String project,
String jobFilter,
String groupPath,
String... jobIds)
@@ -236,16 +274,18 @@ Main entry point to talk to a RunDeck instance.
exportJobsToFile(String filename,
+ FileType format,
String project)
- Export the definitions of all jobs that belongs to the given project, as an XML file
+ Export the definitions of all jobs that belongs to the given project
Export the definitions of the jobs that belongs to the given project, and matches the given criteria (jobFilter,
- groupPath and jobIds), as an XML file
exportJobsToFile(String filename,
+ String format,
+ String project)
+
+
+ Export the definitions of all jobs that belongs to the given project
+
+
+
+ void
+
exportJobsToFile(String filename,
+ String format,
+ String project,
+ String jobFilter,
+ String groupPath,
+ String... jobIds)
+
+
+ Export the definitions of the jobs that belongs to the given project, and matches the given criteria (jobFilter,
+ groupPath and jobIds)
+
+
+
+ void
+
exportJobToFile(String filename,
+ FileType format,
String jobId)
- Export the definition of a single job (identified by the given ID), as an XML file
+ Export the definition of a single job (identified by the given ID)
+
+
+
+ void
+
exportJobToFile(String filename,
+ String format,
+ String jobId)
+
+
+ Export the definition of a single job (identified by the given ID)
@@ -315,6 +390,26 @@ Main entry point to talk to a RunDeck instance. Long max,
Long offset)
+
+ Get the executions of the given job
importJobs(InputStream stream,
+ String fileType,
+ String importBehavior)
+
+
+ Import the definitions of jobs, from the given input stream, using the given behavior
importJobs(String filename,
+ String fileType,
+ String importBehavior)
+
+
+ Import the definitions of jobs, from the given file, using the given behavior
+public void exportJobsToFile(String filename,
+ String format,
String project,
String jobFilter,
String groupPath,
@@ -930,27 +1132,61 @@ public void exportJobsToFile(IOException
Export the definitions of the jobs that belongs to the given project, and matches the given criteria (jobFilter,
- groupPath and jobIds), as an XML file
+ groupPath and jobIds)
-
Parameters:
filename - path of the file where the content should be saved
project - name of the project - mandatory
jobFilter - a filter for the job Name - optional
groupPath - a group or partial group path to include all jobs within that group path - optional
jobIds - a list of Job IDs to include - optional
+
Parameters:
filename - path of the file where the content should be saved - mandatory
RundeckClient.exportJobs(FileType format,
+ String project,
+ String jobFilter,
+ String groupPath,
+ String... jobIds)
+
+
+ Export the definitions of the jobs that belongs to the given project, and matches the given criteria (jobFilter,
+ groupPath and jobIds)
+
+
+
+ void
+
RundeckClient.exportJobsToFile(String filename,
+ FileType format,
+ String project)
+
+
+ Export the definitions of all jobs that belongs to the given project
+
+
+
+ void
+
RundeckClient.exportJobsToFile(String filename,
+ FileType format,
+ String project,
+ String jobFilter,
+ String groupPath,
+ String... jobIds)
+
+
+ Export the definitions of the jobs that belongs to the given project, and matches the given criteria (jobFilter,
+ groupPath and jobIds)
+
+
+
+ void
+
RundeckClient.exportJobToFile(String filename,
+ FileType format,
+ String jobId)
+
+
+ Export the definition of a single job (identified by the given ID)
RundeckClient.importJobs(String filename,
+ FileType fileType,
+ RundeckJobsImportMethod importBehavior)
+
+
+ Import the definitions of jobs, from the given file, using the given behavior
RundeckClient.exportJobs(FileType format,
+ String project,
+ String jobFilter,
+ String groupPath,
+ String... jobIds)
+
+
+ Export the definitions of the jobs that belongs to the given project, and matches the given criteria (jobFilter,
+ groupPath and jobIds)
RundeckClient.exportJobsToFile(String filename,
+ FileType format,
String project)
- Export the definitions of all jobs that belongs to the given project, as an XML file
+ Export the definitions of all jobs that belongs to the given project
RundeckClient.exportJobsToFile(String filename,
+ String format,
+ String project)
+
+
+ Export the definitions of all jobs that belongs to the given project
+
+
+
+ void
+
RundeckClient.exportJobsToFile(String filename,
+ String format,
+ String project,
+ String jobFilter,
+ String groupPath,
+ String... jobIds)
+
+
+ Export the definitions of the jobs that belongs to the given project, and matches the given criteria (jobFilter,
+ groupPath and jobIds)
+
+
+
+ void
+
RundeckClient.exportJobToFile(String filename,
+ FileType format,
String jobId)
- Export the definition of a single job (identified by the given ID), as an XML file
+ Export the definition of a single job (identified by the given ID)
+
+
+
+ void
+
RundeckClient.exportJobToFile(String filename,
+ String format,
+ String jobId)
+
+
+ Export the definition of a single job (identified by the given ID)
@@ -237,6 +308,26 @@ Uses of Long max,
Long offset)
+
+ Get the executions of the given job
RundeckClient.importJobs(InputStream stream,
+ String fileType,
+ String importBehavior)
+
+
+ Import the definitions of jobs, from the given input stream, using the given behavior
RundeckClient.importJobs(String filename,
+ FileType fileType,
+ RundeckJobsImportMethod importBehavior)
+
+
+ Import the definitions of jobs, from the given file, using the given behavior
RundeckClient.importJobs(String filename,
+ String fileType,
+ String importBehavior)
+
+
+ Import the definitions of jobs, from the given file, using the given behavior
RundeckClient.exportJobs(FileType format,
+ String project,
+ String jobFilter,
+ String groupPath,
+ String... jobIds)
+
+
+ Export the definitions of the jobs that belongs to the given project, and matches the given criteria (jobFilter,
+ groupPath and jobIds)
RundeckClient.exportJobsToFile(String filename,
+ FileType format,
String project)
- Export the definitions of all jobs that belongs to the given project, as an XML file
+ Export the definitions of all jobs that belongs to the given project
RundeckClient.exportJobsToFile(String filename,
+ String format,
+ String project)
+
+
+ Export the definitions of all jobs that belongs to the given project
+
+
+
+ void
+
RundeckClient.exportJobsToFile(String filename,
+ String format,
+ String project,
+ String jobFilter,
+ String groupPath,
+ String... jobIds)
+
+
+ Export the definitions of the jobs that belongs to the given project, and matches the given criteria (jobFilter,
+ groupPath and jobIds)
+
+
+
+ void
+
RundeckClient.exportJobToFile(String filename,
+ FileType format,
String jobId)
- Export the definition of a single job (identified by the given ID), as an XML file
+ Export the definition of a single job (identified by the given ID)
+
+
+
+ void
+
RundeckClient.exportJobToFile(String filename,
+ String format,
+ String jobId)
+
+
+ Export the definition of a single job (identified by the given ID)
@@ -257,6 +328,26 @@ Uses of Long max,
Long offset)
+
+ Get the executions of the given job
RundeckClient.importJobs(InputStream stream,
+ String fileType,
+ String importBehavior)
+
+
+ Import the definitions of jobs, from the given input stream, using the given behavior
RundeckClient.importJobs(String filename,
+ FileType fileType,
+ RundeckJobsImportMethod importBehavior)
+
+
+ Import the definitions of jobs, from the given file, using the given behavior
RundeckClient.importJobs(String filename,
+ String fileType,
+ String importBehavior)
+
+
+ Import the definitions of jobs, from the given file, using the given behavior
+
+
+
void
RundeckClient.ping()
diff --git a/apidocs/org/rundeck/api/class-use/RundeckClient.html b/apidocs/org/rundeck/api/class-use/RundeckClient.html
index 2647449..458dd67 100644
--- a/apidocs/org/rundeck/api/class-use/RundeckClient.html
+++ b/apidocs/org/rundeck/api/class-use/RundeckClient.html
@@ -2,13 +2,13 @@
-
+
-Uses of Class org.rundeck.api.RundeckClient (RunDeck API - Java Client 1.1-SNAPSHOT API)
+Uses of Class org.rundeck.api.RundeckClient (RunDeck API - Java Client 1.1 API)
-
+
@@ -16,7 +16,7 @@ Uses of Class org.rundeck.api.RundeckClient (RunDeck API - Java Client 1.1-SNAPS
function windowTitle()
{
if (location.href.indexOf('is-external=true') == -1) {
- parent.document.title="Uses of Class org.rundeck.api.RundeckClient (RunDeck API - Java Client 1.1-SNAPSHOT API)";
+ parent.document.title="Uses of Class org.rundeck.api.RundeckClient (RunDeck API - Java Client 1.1 API)";
}
}
diff --git a/apidocs/org/rundeck/api/domain/RundeckAbort.AbortStatus.html b/apidocs/org/rundeck/api/domain/RundeckAbort.AbortStatus.html
index a697149..7dbda2b 100644
--- a/apidocs/org/rundeck/api/domain/RundeckAbort.AbortStatus.html
+++ b/apidocs/org/rundeck/api/domain/RundeckAbort.AbortStatus.html
@@ -2,13 +2,13 @@
-
+
-RundeckAbort.AbortStatus (RunDeck API - Java Client 1.1-SNAPSHOT API)
+RundeckAbort.AbortStatus (RunDeck API - Java Client 1.1 API)
-
+
@@ -16,7 +16,7 @@ RundeckAbort.AbortStatus (RunDeck API - Java Client 1.1-SNAPSHOT API)
function windowTitle()
{
if (location.href.indexOf('is-external=true') == -1) {
- parent.document.title="RundeckAbort.AbortStatus (RunDeck API - Java Client 1.1-SNAPSHOT API)";
+ parent.document.title="RundeckAbort.AbortStatus (RunDeck API - Java Client 1.1 API)";
}
}
diff --git a/apidocs/org/rundeck/api/domain/RundeckAbort.html b/apidocs/org/rundeck/api/domain/RundeckAbort.html
index 0b9f4c5..c5fea84 100644
--- a/apidocs/org/rundeck/api/domain/RundeckAbort.html
+++ b/apidocs/org/rundeck/api/domain/RundeckAbort.html
@@ -2,13 +2,13 @@
-
+
-RundeckAbort (RunDeck API - Java Client 1.1-SNAPSHOT API)
+RundeckAbort (RunDeck API - Java Client 1.1 API)
-
+
@@ -16,7 +16,7 @@ RundeckAbort (RunDeck API - Java Client 1.1-SNAPSHOT API)
function windowTitle()
{
if (location.href.indexOf('is-external=true') == -1) {
- parent.document.title="RundeckAbort (RunDeck API - Java Client 1.1-SNAPSHOT API)";
+ parent.document.title="RundeckAbort (RunDeck API - Java Client 1.1 API)";
}
}
diff --git a/apidocs/org/rundeck/api/domain/RundeckExecution.ExecutionStatus.html b/apidocs/org/rundeck/api/domain/RundeckExecution.ExecutionStatus.html
index fd26335..c9a92e3 100644
--- a/apidocs/org/rundeck/api/domain/RundeckExecution.ExecutionStatus.html
+++ b/apidocs/org/rundeck/api/domain/RundeckExecution.ExecutionStatus.html
@@ -2,13 +2,13 @@
-
+
-RundeckExecution.ExecutionStatus (RunDeck API - Java Client 1.1-SNAPSHOT API)
+RundeckExecution.ExecutionStatus (RunDeck API - Java Client 1.1 API)
-
+
@@ -16,7 +16,7 @@ RundeckExecution.ExecutionStatus (RunDeck API - Java Client 1.1-SNAPSHOT API)
function windowTitle()
{
if (location.href.indexOf('is-external=true') == -1) {
- parent.document.title="RundeckExecution.ExecutionStatus (RunDeck API - Java Client 1.1-SNAPSHOT API)";
+ parent.document.title="RundeckExecution.ExecutionStatus (RunDeck API - Java Client 1.1 API)";
}
}
diff --git a/apidocs/org/rundeck/api/domain/RundeckExecution.html b/apidocs/org/rundeck/api/domain/RundeckExecution.html
index 3bcc826..7d5b179 100644
--- a/apidocs/org/rundeck/api/domain/RundeckExecution.html
+++ b/apidocs/org/rundeck/api/domain/RundeckExecution.html
@@ -2,13 +2,13 @@
-
+
-RundeckExecution (RunDeck API - Java Client 1.1-SNAPSHOT API)
+RundeckExecution (RunDeck API - Java Client 1.1 API)
-
+
@@ -16,7 +16,7 @@ RundeckExecution (RunDeck API - Java Client 1.1-SNAPSHOT API)
function windowTitle()
{
if (location.href.indexOf('is-external=true') == -1) {
- parent.document.title="RundeckExecution (RunDeck API - Java Client 1.1-SNAPSHOT API)";
+ parent.document.title="RundeckExecution (RunDeck API - Java Client 1.1 API)";
}
}
diff --git a/apidocs/org/rundeck/api/domain/RundeckJob.html b/apidocs/org/rundeck/api/domain/RundeckJob.html
index 3c96f00..d7f2876 100644
--- a/apidocs/org/rundeck/api/domain/RundeckJob.html
+++ b/apidocs/org/rundeck/api/domain/RundeckJob.html
@@ -2,13 +2,13 @@
-
+
-RundeckJob (RunDeck API - Java Client 1.1-SNAPSHOT API)
+RundeckJob (RunDeck API - Java Client 1.1 API)
-
+
@@ -16,7 +16,7 @@ RundeckJob (RunDeck API - Java Client 1.1-SNAPSHOT API)
function windowTitle()
{
if (location.href.indexOf('is-external=true') == -1) {
- parent.document.title="RundeckJob (RunDeck API - Java Client 1.1-SNAPSHOT API)";
+ parent.document.title="RundeckJob (RunDeck API - Java Client 1.1 API)";
}
}
@@ -57,7 +57,7 @@ function windowTitle()
Returns an array containing the constants of this enum type, in
+the order they are declared. This method may be used to iterate
+over the constants as follows:
+
+for (RundeckJobsImportMethod c : RundeckJobsImportMethod.values())
+ System.out.println(c);
+
+
+
+
+
Returns:
an array containing the constants of this enum type, in
+the order they are declared
Returns the enum constant of this type with the specified name.
+The string must match exactly an identifier used to declare an
+enum constant in this type. (Extraneous whitespace characters are
+not permitted.)
+
+
+
Parameters:
name - the name of the enum constant to be returned.
+
RundeckClient.importJobs(String filename,
+ FileType fileType,
+ RundeckJobsImportMethod importBehavior)
+
+
+ Import the definitions of jobs, from the given file, using the given behavior
RundeckClient.importJobs(InputStream stream,
+ String fileType,
+ String importBehavior)
+
+
+ Import the definitions of jobs, from the given input stream, using the given behavior
RundeckClient.importJobs(String filename,
+ FileType fileType,
+ RundeckJobsImportMethod importBehavior)
+
+
+ Import the definitions of jobs, from the given file, using the given behavior
RundeckClient.importJobs(String filename,
+ String fileType,
+ String importBehavior)
+
+
+ Import the definitions of jobs, from the given file, using the given behavior
Description: The codec package contains simple encoder and decoders for
various formats such as Base64 and Hexadecimal. In addition to these
widely used encoders and decoders, the codec package also maintains a
- collection of phonetic encoding utilities.
Description: Commons Lang, a package of Java utility classes for the
classes that are in java.lang's hierarchy, or are considered to be so
- standard as to justify existence in java.lang.
Project License: No project license is defined for this project.
xml-apis:xml-apis:jar:1.0.b2 (compile)
XML Commons External Components XML APIs
Description: xml-commons provides an Apache-hosted set of DOM, SAX, and
JAXP interfaces for use in other xml-based projects. Our hope is that we
can standardize on both a common version and packaging scheme for these
critical XML standards interfaces to make the lives of both our developers
and users easier. The External Components portion of xml-commons contains
interfaces that are defined by external standards organizations. For DOM,
that's the W3C; for SAX it's David Megginson and sax.sourceforge.net; for
- JAXP it's Sun.
Description: There is currently no description associated with this project.
Project License: No project license is defined for this project.
xerces:xercesImpl:jar:2.6.2 (compile)
Unnamed - xerces:xercesImpl:jar:2.6.2
Description: There is currently no description associated with this project.
Project License: No project license is defined for this project.
xalan:xalan:jar:2.6.0 (compile)
Unnamed - xalan:xalan:jar:2.6.0
Description: There is currently no description associated with this project.
Project License: No project license is defined for this project.
xml-apis:xml-apis:jar:1.0.b2 (compile)
XML Commons External Components XML APIs
Description: xml-commons provides an Apache-hosted set of DOM, SAX, and
JAXP interfaces for use in other xml-based projects. Our hope is that we
can standardize on both a common version and packaging scheme for these
critical XML standards interfaces to make the lives of both our developers
and users easier. The External Components portion of xml-commons contains
interfaces that are defined by external standards organizations. For DOM,
that's the W3C; for SAX it's David Megginson and sax.sourceforge.net; for
- JAXP it's Sun.
The Apache Software License, Version 2.0: Commons Codec, Commons IO, Commons Lang, Commons Logging, RunDeck API - Java Client, XML Commons External Components XML APIs
The Apache Software License, Version 2.0: Commons Codec, Commons IO, Commons Lang, Commons Logging, RunDeck API - Java Client, XML Commons External Components XML APIs
- Last Published: 2011-07-12
- | Version: 1.1-SNAPSHOT
+ Last Published: 2011-07-28
+ | Version: 1.1
@@ -112,7 +112,7 @@
Using the RunDeck API from Groovy scripts
Here are some examples of what you can do with this lib and a few lines of Groovy.
We can use Grape to download the lib (and its dependencies) from the Maven Central Repository, so you don't have to install anything manually (except Groovy, of course).
Basic usage
Save the following script in a file named "rundeck.groovy", and execute it with "groovy rundeck.groovy". Feeling Groovy ? ;-)
// we use Grape (Ivy) to download the lib (and its dependencies) from Maven Central Repository
-@Grab(group='org.rundeck', module='rundeck-api-java-client', version='1.0')
+@Grab(group='org.rundeck', module='rundeck-api-java-client', version='1.1')
import org.rundeck.api.RundeckClient
rundeck = new RundeckClient("http://localhost:4440", "admin", "admin")
@@ -162,8 +162,13 @@ execution = rundeck.runAdhocCommand("my-project", "uptime",
import org.rundeck.api.RundeckClient
+rundeck = new RundeckClient("http://localhost:4440", "admin", "admin")
+
+result = rundeck.importJobs("/tmp/jobs.xml", "xml")
+println "${result.succeededJobs.size} jobs successfully imported, ${result.skippedJobs.size} jobs skipped, and ${result.failedJobs.size} jobs failed"
And more...
See the API documentation of the RundeckClient class for more interactions with your RunDeck instance...
diff --git a/testapidocs/org/rundeck/api/parser/package-tree.html b/testapidocs/org/rundeck/api/parser/package-tree.html
index 894287f..77489eb 100644
--- a/testapidocs/org/rundeck/api/parser/package-tree.html
+++ b/testapidocs/org/rundeck/api/parser/package-tree.html
@@ -2,13 +2,13 @@
-
+
-org.rundeck.api.parser Class Hierarchy (RunDeck API - Java Client 1.1-SNAPSHOT Test API)
+org.rundeck.api.parser Class Hierarchy (RunDeck API - Java Client 1.1 Test API)
-
+
@@ -16,7 +16,7 @@ org.rundeck.api.parser Class Hierarchy (RunDeck API - Java Client 1.1-SNAPSHOT T
function windowTitle()
{
if (location.href.indexOf('is-external=true') == -1) {
- parent.document.title="org.rundeck.api.parser Class Hierarchy (RunDeck API - Java Client 1.1-SNAPSHOT Test API)";
+ parent.document.title="org.rundeck.api.parser Class Hierarchy (RunDeck API - Java Client 1.1 Test API)";
}
}
@@ -93,7 +93,7 @@ Class Hierarchy
+
+1/*
+2 * Copyright 2011 Vincent Behar
+3 *
+4 * Licensed under the Apache License, Version 2.0 (the "License");
+5 * you may not use this file except in compliance with the License.
+6 * You may obtain a copy of the License at
+7 *
+8 * http://www.apache.org/licenses/LICENSE-2.0
+9 *
+10 * Unless required by applicable law or agreed to in writing, software
+11 * distributed under the License is distributed on an "AS IS" BASIS,
+12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+13 * See the License for the specific language governing permissions and
+14 * limitations under the License.
+15 */
+16package org.rundeck.api.parser;
+17
+18import java.io.InputStream;
+19import org.dom4j.Document;
+20import org.junit.Assert;
+21import org.junit.Test;
+22import org.rundeck.api.domain.RundeckJob;
+23import org.rundeck.api.domain.RundeckJobsImportResult;
+24
+25/**
+26 * Test the {@link JobsImportResultParser}
+27 *
+28 * @author Vincent Behar
+29 */
+30publicclassJobsImportResultParserTest {
+31
+32 @Test
+33publicvoid parseResult() throws Exception {
+34 InputStream input = getClass().getResourceAsStream("jobs-import.xml");
+35 Document document = ParserHelper.loadDocument(input);
+36
+37 RundeckJobsImportResult result = new JobsImportResultParser("result").parseXmlNode(document);
+38
+39 Assert.assertEquals(2, result.getSucceededJobs().size());
+40 Assert.assertEquals(0, result.getSkippedJobs().size());
+41 Assert.assertEquals(1, result.getFailedJobs().size());
+42
+43 Assert.assertEquals("job-one", result.getSucceededJobs().get(0).getName());
+44 Assert.assertEquals("job-two", result.getSucceededJobs().get(1).getName());
+45
+46 RundeckJob failedJob = result.getFailedJobs().keySet().iterator().next();
+47 Assert.assertEquals("job-three", failedJob.getName());
+48 Assert.assertEquals("Error message", result.getFailedJobs().get(failedJob));
+49 }
+50
+51 }
+
+
+
+
+
+
diff --git a/xref/org/rundeck/api/ApiCall.html b/xref/org/rundeck/api/ApiCall.html
index e8196b2..b5572e0 100644
--- a/xref/org/rundeck/api/ApiCall.html
+++ b/xref/org/rundeck/api/ApiCall.html
@@ -28,311 +28,365 @@
18import java.io.ByteArrayInputStream;
19import java.io.IOException;
20import java.io.InputStream;
-21import java.security.KeyManagementException;
-22import java.security.KeyStoreException;
-23import java.security.NoSuchAlgorithmException;
-24import java.security.UnrecoverableKeyException;
-25import java.security.cert.CertificateException;
-26import java.security.cert.X509Certificate;
-27import java.util.ArrayList;
-28import java.util.List;
-29import org.apache.commons.lang.StringUtils;
-30import org.apache.http.HttpResponse;
-31import org.apache.http.NameValuePair;
-32import org.apache.http.ParseException;
-33import org.apache.http.client.HttpClient;
-34import org.apache.http.client.entity.UrlEncodedFormEntity;
-35import org.apache.http.client.methods.HttpDelete;
-36import org.apache.http.client.methods.HttpGet;
-37import org.apache.http.client.methods.HttpPost;
-38import org.apache.http.client.methods.HttpRequestBase;
-39import org.apache.http.conn.scheme.Scheme;
-40import org.apache.http.conn.ssl.SSLSocketFactory;
-41import org.apache.http.conn.ssl.TrustStrategy;
-42import org.apache.http.impl.client.DefaultHttpClient;
-43import org.apache.http.message.BasicNameValuePair;
-44import org.apache.http.protocol.HTTP;
-45import org.apache.http.util.EntityUtils;
-46import org.dom4j.Document;
-47import org.rundeck.api.RundeckApiException.RundeckApiLoginException;
-48import org.rundeck.api.parser.ParserHelper;
-49import org.rundeck.api.parser.XmlNodeParser;
-50import org.rundeck.api.util.AssertUtil;
-51
-52/**
-53 * Class responsible for making the HTTP API calls
-54 *
-55 * @author Vincent Behar
-56 */
-57classApiCall {
-58
-59privatefinalRundeckClient client;
-60
-61/**
-62 * Build a new instance, linked to the given RunDeck client
-63 *
-64 * @param client holding the RunDeck url and the credentials
-65 * @throws IllegalArgumentException if client is null
-66 */
-67publicApiCall(RundeckClient client) throws IllegalArgumentException {
-68super();
-69this.client = client;
-70 AssertUtil.notNull(client, "The RunDeck Client must not be null !");
-71 }
-72
-73/**
-74 * Try to "ping" the RunDeck instance to see if it is alive
-75 *
-76 * @throws RundeckApiException if the ping fails
-77 */
-78publicvoid ping() throws RundeckApiException {
-79 HttpClient httpClient = instantiateHttpClient();
-80try {
-81 HttpResponse response = httpClient.execute(new HttpGet(client.getUrl()));
-82if (response.getStatusLine().getStatusCode() / 100 != 2) {
-83thrownewRundeckApiException("Invalid HTTP response '" + response.getStatusLine() + "' when pinging "
-84 + client.getUrl());
-85 }
-86 } catch (IOException e) {
-87thrownewRundeckApiException("Failed to ping RunDeck instance at " + client.getUrl(), e);
-88 } finally {
-89 httpClient.getConnectionManager().shutdown();
-90 }
-91 }
-92
-93/**
-94 * Test the credentials (login/password) on the RunDeck instance
-95 *
-96 * @throws RundeckApiLoginException if the login fails
-97 */
-98publicvoid testCredentials() throws RundeckApiLoginException {
-99 HttpClient httpClient = instantiateHttpClient();
-100try {
-101 login(httpClient);
-102 } finally {
-103 httpClient.getConnectionManager().shutdown();
-104 }
-105 }
-106
-107/**
-108 * Execute an HTTP GET request to the RunDeck instance, on the given path. We will login first, and then execute the
-109 * API call. At the end, the given parser will be used to convert the response to a more useful result object.
-110 *
-111 * @param apiPath on which we will make the HTTP request - see {@link ApiPathBuilder}
-112 * @param parser used to parse the response
-113 * @return the result of the call, as formatted by the parser
-114 * @throws RundeckApiException in case of error when calling the API
-115 * @throws RundeckApiLoginException if the login fails
-116 */
-117public <T> T get(ApiPathBuilder apiPath, XmlNodeParser<T> parser) throws RundeckApiException,
-118RundeckApiLoginException {
-119return execute(new HttpGet(client.getUrl() + RundeckClient.API_ENDPOINT + apiPath), parser);
-120 }
-121
-122/**
-123 * Execute an HTTP GET request to the RunDeck instance, on the given path. We will login first, and then execute the
-124 * API call.
-125 *
-126 * @param apiPath on which we will make the HTTP request - see {@link ApiPathBuilder}
-127 * @return a new {@link InputStream} instance, not linked with network resources
-128 * @throws RundeckApiException in case of error when calling the API
-129 * @throws RundeckApiLoginException if the login fails
-130 */
-131public InputStream get(ApiPathBuilder apiPath) throws RundeckApiException, RundeckApiLoginException {
-132 ByteArrayInputStream response = execute(new HttpGet(client.getUrl() + RundeckClient.API_ENDPOINT + apiPath));
-133
-134// try to load the document, to throw an exception in case of error
-135 ParserHelper.loadDocument(response);
-136 response.reset();
-137
-138return response;
-139 }
-140
-141/**
-142 * Execute an HTTP DELETE request to the RunDeck instance, on the given path. We will login first, and then execute
-143 * the API call. At the end, the given parser will be used to convert the response to a more useful result object.
-144 *
-145 * @param apiPath on which we will make the HTTP request - see {@link ApiPathBuilder}
-146 * @param parser used to parse the response
-147 * @return the result of the call, as formatted by the parser
-148 * @throws RundeckApiException in case of error when calling the API
-149 * @throws RundeckApiLoginException if the login fails
-150 */
-151public <T> T delete(ApiPathBuilder apiPath, XmlNodeParser<T> parser) throws RundeckApiException,
-152RundeckApiLoginException {
-153return execute(new HttpDelete(client.getUrl() + RundeckClient.API_ENDPOINT + apiPath), parser);
-154 }
-155
-156/**
-157 * Execute an HTTP request to the RunDeck instance. We will login first, and then execute the API call. At the end,
-158 * the given parser will be used to convert the response to a more useful result object.
-159 *
-160 * @param request to execute. see {@link HttpGet}, {@link HttpDelete}, and so on...
-161 * @param parser used to parse the response
-162 * @return the result of the call, as formatted by the parser
-163 * @throws RundeckApiException in case of error when calling the API
-164 * @throws RundeckApiLoginException if the login fails
-165 */
-166private <T> T execute(HttpRequestBase request, XmlNodeParser<T> parser) throws RundeckApiException,
-167RundeckApiLoginException {
-168// execute the request
-169 InputStream response = execute(request);
+21import java.net.ProxySelector;
+22import java.security.KeyManagementException;
+23import java.security.KeyStoreException;
+24import java.security.NoSuchAlgorithmException;
+25import java.security.UnrecoverableKeyException;
+26import java.security.cert.CertificateException;
+27import java.security.cert.X509Certificate;
+28import java.util.ArrayList;
+29import java.util.List;
+30import java.util.Map.Entry;
+31import org.apache.commons.lang.StringUtils;
+32import org.apache.http.HttpResponse;
+33import org.apache.http.NameValuePair;
+34import org.apache.http.ParseException;
+35import org.apache.http.client.HttpClient;
+36import org.apache.http.client.entity.UrlEncodedFormEntity;
+37import org.apache.http.client.methods.HttpDelete;
+38import org.apache.http.client.methods.HttpGet;
+39import org.apache.http.client.methods.HttpPost;
+40import org.apache.http.client.methods.HttpRequestBase;
+41import org.apache.http.conn.scheme.Scheme;
+42import org.apache.http.conn.ssl.SSLSocketFactory;
+43import org.apache.http.conn.ssl.TrustStrategy;
+44import org.apache.http.entity.mime.HttpMultipartMode;
+45import org.apache.http.entity.mime.MultipartEntity;
+46import org.apache.http.entity.mime.content.InputStreamBody;
+47import org.apache.http.impl.client.DefaultHttpClient;
+48import org.apache.http.impl.conn.ProxySelectorRoutePlanner;
+49import org.apache.http.message.BasicNameValuePair;
+50import org.apache.http.protocol.HTTP;
+51import org.apache.http.util.EntityUtils;
+52import org.dom4j.Document;
+53import org.rundeck.api.RundeckApiException.RundeckApiLoginException;
+54import org.rundeck.api.parser.ParserHelper;
+55import org.rundeck.api.parser.XmlNodeParser;
+56import org.rundeck.api.util.AssertUtil;
+57
+58/**
+59 * Class responsible for making the HTTP API calls
+60 *
+61 * @author Vincent Behar
+62 */
+63classApiCall {
+64
+65privatefinalRundeckClient client;
+66
+67/**
+68 * Build a new instance, linked to the given RunDeck client
+69 *
+70 * @param client holding the RunDeck url and the credentials
+71 * @throws IllegalArgumentException if client is null
+72 */
+73publicApiCall(RundeckClient client) throws IllegalArgumentException {
+74super();
+75this.client = client;
+76 AssertUtil.notNull(client, "The RunDeck Client must not be null !");
+77 }
+78
+79/**
+80 * Try to "ping" the RunDeck instance to see if it is alive
+81 *
+82 * @throws RundeckApiException if the ping fails
+83 */
+84publicvoid ping() throws RundeckApiException {
+85 HttpClient httpClient = instantiateHttpClient();
+86try {
+87 HttpResponse response = httpClient.execute(new HttpGet(client.getUrl()));
+88if (response.getStatusLine().getStatusCode() / 100 != 2) {
+89thrownewRundeckApiException("Invalid HTTP response '" + response.getStatusLine() + "' when pinging "
+90 + client.getUrl());
+91 }
+92 } catch (IOException e) {
+93thrownewRundeckApiException("Failed to ping RunDeck instance at " + client.getUrl(), e);
+94 } finally {
+95 httpClient.getConnectionManager().shutdown();
+96 }
+97 }
+98
+99/**
+100 * Test the credentials (login/password) on the RunDeck instance
+101 *
+102 * @throws RundeckApiLoginException if the login fails
+103 */
+104publicvoid testCredentials() throws RundeckApiLoginException {
+105 HttpClient httpClient = instantiateHttpClient();
+106try {
+107 login(httpClient);
+108 } finally {
+109 httpClient.getConnectionManager().shutdown();
+110 }
+111 }
+112
+113/**
+114 * Execute an HTTP GET request to the RunDeck instance, on the given path. We will login first, and then execute the
+115 * API call. At the end, the given parser will be used to convert the response to a more useful result object.
+116 *
+117 * @param apiPath on which we will make the HTTP request - see {@link ApiPathBuilder}
+118 * @param parser used to parse the response
+119 * @return the result of the call, as formatted by the parser
+120 * @throws RundeckApiException in case of error when calling the API
+121 * @throws RundeckApiLoginException if the login fails
+122 */
+123public <T> T get(ApiPathBuilder apiPath, XmlNodeParser<T> parser) throws RundeckApiException,
+124RundeckApiLoginException {
+125return execute(new HttpGet(client.getUrl() + RundeckClient.API_ENDPOINT + apiPath), parser);
+126 }
+127
+128/**
+129 * Execute an HTTP GET request to the RunDeck instance, on the given path. We will login first, and then execute the
+130 * API call.
+131 *
+132 * @param apiPath on which we will make the HTTP request - see {@link ApiPathBuilder}
+133 * @return a new {@link InputStream} instance, not linked with network resources
+134 * @throws RundeckApiException in case of error when calling the API
+135 * @throws RundeckApiLoginException if the login fails
+136 */
+137public InputStream get(ApiPathBuilder apiPath) throws RundeckApiException, RundeckApiLoginException {
+138 ByteArrayInputStream response = execute(new HttpGet(client.getUrl() + RundeckClient.API_ENDPOINT + apiPath));
+139
+140// try to load the document, to throw an exception in case of error
+141 ParserHelper.loadDocument(response);
+142 response.reset();
+143
+144return response;
+145 }
+146
+147/**
+148 * Execute an HTTP POST request to the RunDeck instance, on the given path. We will login first, and then execute
+149 * the API call. At the end, the given parser will be used to convert the response to a more useful result object.
+150 *
+151 * @param apiPath on which we will make the HTTP request - see {@link ApiPathBuilder}
+152 * @param parser used to parse the response
+153 * @return the result of the call, as formatted by the parser
+154 * @throws RundeckApiException in case of error when calling the API
+155 * @throws RundeckApiLoginException if the login fails
+156 */
+157public <T> T post(ApiPathBuilder apiPath, XmlNodeParser<T> parser) throws RundeckApiException,
+158RundeckApiLoginException {
+159 HttpPost httpPost = new HttpPost(client.getUrl() + RundeckClient.API_ENDPOINT + apiPath);
+160
+161// POST a multi-part request, with all attachments
+162 MultipartEntity entity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE);
+163for (Entry<String, InputStream> attachment : apiPath.getAttachments().entrySet()) {
+164 entity.addPart(attachment.getKey(), new InputStreamBody(attachment.getValue(), attachment.getKey()));
+165 }
+166 httpPost.setEntity(entity);
+167
+168return execute(httpPost, parser);
+169 }
170
-171// read and parse the response
-172 Document xmlDocument = ParserHelper.loadDocument(response);
-173return parser.parseXmlNode(xmlDocument);
-174 }
-175
-176/**
-177 * Execute an HTTP request to the RunDeck instance. We will login first, and then execute the API call.
-178 *
-179 * @param request to execute. see {@link HttpGet}, {@link HttpDelete}, and so on...
-180 * @return a new {@link InputStream} instance, not linked with network resources
-181 * @throws RundeckApiException in case of error when calling the API
-182 * @throws RundeckApiLoginException if the login fails
-183 */
-184private ByteArrayInputStream execute(HttpRequestBase request) throws RundeckApiException, RundeckApiLoginException {
-185 HttpClient httpClient = instantiateHttpClient();
-186try {
-187 login(httpClient);
-188
-189// execute the HTTP request
-190 HttpResponse response = null;
-191try {
-192 response = httpClient.execute(request);
-193 } catch (IOException e) {
-194thrownewRundeckApiException("Failed to execute an HTTP " + request.getMethod() + " on url : "
-195 + request.getURI(), e);
-196 }
-197
-198// HTTP client refuses to handle redirects (code 3xx) for DELETE, so we have to do it manually...
-199// See http://rundeck.lighthouseapp.com/projects/59277/tickets/248
-200if (response.getStatusLine().getStatusCode() / 100 == 3
-201 && HttpDelete.METHOD_NAME.equals(request.getMethod())) {
-202 String newLocation = response.getFirstHeader("Location").getValue();
-203try {
-204 EntityUtils.consume(response.getEntity());
-205 } catch (IOException e) {
-206thrownewRundeckApiException("Failed to consume entity (release connection)", e);
-207 }
-208 request = new HttpDelete(newLocation);
-209try {
-210 response = httpClient.execute(request);
-211 } catch (IOException e) {
-212thrownewRundeckApiException("Failed to execute an HTTP " + request.getMethod() + " on url : "
-213 + request.getURI(), e);
-214 }
-215 }
-216
-217// check the response code (should be 2xx, even in case of error : error message is in the XML result)
-218if (response.getStatusLine().getStatusCode() / 100 != 2) {
-219thrownewRundeckApiException("Invalid HTTP response '" + response.getStatusLine() + "' for "
-220 + request.getURI());
-221 }
-222if (response.getEntity() == null) {
-223thrownewRundeckApiException("Empty RunDeck response ! HTTP status line is : "
-224 + response.getStatusLine());
-225 }
-226
-227// return a new inputStream, so that we can close all network resources
-228try {
-229returnnew ByteArrayInputStream(EntityUtils.toByteArray(response.getEntity()));
-230 } catch (IOException e) {
-231thrownewRundeckApiException("Failed to consume entity and convert the inputStream", e);
-232 }
-233 } finally {
-234 httpClient.getConnectionManager().shutdown();
-235 }
-236 }
-237
-238/**
-239 * Do the actual work of login, using the given {@link HttpClient} instance. You'll need to re-use this instance
-240 * when making API calls (such as running a job).
-241 *
-242 * @param httpClient pre-instantiated
-243 * @throws RundeckApiLoginException if the login failed
-244 */
-245privatevoid login(HttpClient httpClient) throws RundeckApiLoginException {
-246 String location = client.getUrl() + "/j_security_check";
-247
-248while (true) {
-249 HttpPost postLogin = new HttpPost(location);
-250 List<NameValuePair> params = new ArrayList<NameValuePair>();
-251 params.add(new BasicNameValuePair("j_username", client.getLogin()));
-252 params.add(new BasicNameValuePair("j_password", client.getPassword()));
-253 params.add(new BasicNameValuePair("action", "login"));
-254
-255 HttpResponse response = null;
-256try {
-257 postLogin.setEntity(new UrlEncodedFormEntity(params, HTTP.UTF_8));
-258 response = httpClient.execute(postLogin);
-259 } catch (IOException e) {
-260thrownewRundeckApiLoginException("Failed to post login form on " + location, e);
-261 }
-262
-263if (response.getStatusLine().getStatusCode() / 100 == 3) {
-264// HTTP client refuses to handle redirects (code 3xx) for POST, so we have to do it manually...
-265 location = response.getFirstHeader("Location").getValue();
-266try {
-267 EntityUtils.consume(response.getEntity());
-268 } catch (IOException e) {
-269thrownewRundeckApiLoginException("Failed to consume entity (release connection)", e);
-270 }
-271continue;
+171/**
+172 * Execute an HTTP DELETE request to the RunDeck instance, on the given path. We will login first, and then execute
+173 * the API call. At the end, the given parser will be used to convert the response to a more useful result object.
+174 *
+175 * @param apiPath on which we will make the HTTP request - see {@link ApiPathBuilder}
+176 * @param parser used to parse the response
+177 * @return the result of the call, as formatted by the parser
+178 * @throws RundeckApiException in case of error when calling the API
+179 * @throws RundeckApiLoginException if the login fails
+180 */
+181public <T> T delete(ApiPathBuilder apiPath, XmlNodeParser<T> parser) throws RundeckApiException,
+182RundeckApiLoginException {
+183return execute(new HttpDelete(client.getUrl() + RundeckClient.API_ENDPOINT + apiPath), parser);
+184 }
+185
+186/**
+187 * Execute an HTTP request to the RunDeck instance. We will login first, and then execute the API call. At the end,
+188 * the given parser will be used to convert the response to a more useful result object.
+189 *
+190 * @param request to execute. see {@link HttpGet}, {@link HttpDelete}, and so on...
+191 * @param parser used to parse the response
+192 * @return the result of the call, as formatted by the parser
+193 * @throws RundeckApiException in case of error when calling the API
+194 * @throws RundeckApiLoginException if the login fails
+195 */
+196private <T> T execute(HttpRequestBase request, XmlNodeParser<T> parser) throws RundeckApiException,
+197RundeckApiLoginException {
+198// execute the request
+199 InputStream response = execute(request);
+200
+201// read and parse the response
+202 Document xmlDocument = ParserHelper.loadDocument(response);
+203return parser.parseXmlNode(xmlDocument);
+204 }
+205
+206/**
+207 * Execute an HTTP request to the RunDeck instance. We will login first, and then execute the API call.
+208 *
+209 * @param request to execute. see {@link HttpGet}, {@link HttpDelete}, and so on...
+210 * @return a new {@link InputStream} instance, not linked with network resources
+211 * @throws RundeckApiException in case of error when calling the API
+212 * @throws RundeckApiLoginException if the login fails
+213 */
+214private ByteArrayInputStream execute(HttpRequestBase request) throws RundeckApiException, RundeckApiLoginException {
+215 HttpClient httpClient = instantiateHttpClient();
+216try {
+217 login(httpClient);
+218
+219// execute the HTTP request
+220 HttpResponse response = null;
+221try {
+222 response = httpClient.execute(request);
+223 } catch (IOException e) {
+224thrownewRundeckApiException("Failed to execute an HTTP " + request.getMethod() + " on url : "
+225 + request.getURI(), e);
+226 }
+227
+228// HTTP client refuses to handle redirects (code 3xx) for DELETE, so we have to do it manually...
+229// See http://rundeck.lighthouseapp.com/projects/59277/tickets/248
+230if (response.getStatusLine().getStatusCode() / 100 == 3
+231 && HttpDelete.METHOD_NAME.equals(request.getMethod())) {
+232 String newLocation = response.getFirstHeader("Location").getValue();
+233try {
+234 EntityUtils.consume(response.getEntity());
+235 } catch (IOException e) {
+236thrownewRundeckApiException("Failed to consume entity (release connection)", e);
+237 }
+238 request = new HttpDelete(newLocation);
+239try {
+240 response = httpClient.execute(request);
+241 } catch (IOException e) {
+242thrownewRundeckApiException("Failed to execute an HTTP " + request.getMethod() + " on url : "
+243 + request.getURI(), e);
+244 }
+245 }
+246
+247// in case of error, we get a redirect to /api/error
+248// that we need to follow manually for POST and DELETE requests (as GET)
+249if (response.getStatusLine().getStatusCode() / 100 == 3) {
+250 String newLocation = response.getFirstHeader("Location").getValue();
+251try {
+252 EntityUtils.consume(response.getEntity());
+253 } catch (IOException e) {
+254thrownewRundeckApiException("Failed to consume entity (release connection)", e);
+255 }
+256 request = new HttpGet(newLocation);
+257try {
+258 response = httpClient.execute(request);
+259 } catch (IOException e) {
+260thrownewRundeckApiException("Failed to execute an HTTP GET on url : " + request.getURI(), e);
+261 }
+262 }
+263
+264// check the response code (should be 2xx, even in case of error : error message is in the XML result)
+265if (response.getStatusLine().getStatusCode() / 100 != 2) {
+266thrownewRundeckApiException("Invalid HTTP response '" + response.getStatusLine() + "' for "
+267 + request.getURI());
+268 }
+269if (response.getEntity() == null) {
+270thrownewRundeckApiException("Empty RunDeck response ! HTTP status line is : "
+271 + response.getStatusLine());
272 }
-273if (response.getStatusLine().getStatusCode() / 100 != 2) {
-274thrownewRundeckApiLoginException("Invalid HTTP response '" + response.getStatusLine() + "' for "
-275 + location);
-276 }
-277try {
-278 String content = EntityUtils.toString(response.getEntity(), HTTP.UTF_8);
-279if (StringUtils.contains(content, "j_security_check")) {
-280thrownewRundeckApiLoginException("Login failed for user " + client.getLogin());
-281 }
-282try {
-283 EntityUtils.consume(response.getEntity());
-284 } catch (IOException e) {
-285thrownewRundeckApiLoginException("Failed to consume entity (release connection)", e);
-286 }
-287 } catch (IOException io) {
-288thrownewRundeckApiLoginException("Failed to read RunDeck result", io);
-289 } catch (ParseException p) {
-290thrownewRundeckApiLoginException("Failed to parse RunDeck response", p);
-291 }
-292break;
-293 }
-294 }
-295
-296/**
-297 * Instantiate a new {@link HttpClient} instance, configured to accept all SSL certificates
-298 *
-299 * @return an {@link HttpClient} instance - won't be null
-300 */
-301private HttpClient instantiateHttpClient() {
-302 SSLSocketFactory socketFactory = null;
-303try {
-304 socketFactory = new SSLSocketFactory(new TrustStrategy() {
-305
-306 @Override
-307publicboolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
-308returntrue;
-309 }
-310 }, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
-311 } catch (KeyManagementException e) {
-312thrownew RuntimeException(e);
-313 } catch (UnrecoverableKeyException e) {
-314thrownew RuntimeException(e);
-315 } catch (NoSuchAlgorithmException e) {
-316thrownew RuntimeException(e);
-317 } catch (KeyStoreException e) {
-318thrownew RuntimeException(e);
-319 }
-320
-321 HttpClient httpClient = new DefaultHttpClient();
-322 httpClient.getConnectionManager().getSchemeRegistry().register(new Scheme("https", 443, socketFactory));
-323return httpClient;
-324 }
-325 }
+273
+274// return a new inputStream, so that we can close all network resources
+275try {
+276returnnew ByteArrayInputStream(EntityUtils.toByteArray(response.getEntity()));
+277 } catch (IOException e) {
+278thrownewRundeckApiException("Failed to consume entity and convert the inputStream", e);
+279 }
+280 } finally {
+281 httpClient.getConnectionManager().shutdown();
+282 }
+283 }
+284
+285/**
+286 * Do the actual work of login, using the given {@link HttpClient} instance. You'll need to re-use this instance
+287 * when making API calls (such as running a job).
+288 *
+289 * @param httpClient pre-instantiated
+290 * @throws RundeckApiLoginException if the login failed
+291 */
+292privatevoid login(HttpClient httpClient) throws RundeckApiLoginException {
+293 String location = client.getUrl() + "/j_security_check";
+294
+295while (true) {
+296 HttpPost postLogin = new HttpPost(location);
+297 List<NameValuePair> params = new ArrayList<NameValuePair>();
+298 params.add(new BasicNameValuePair("j_username", client.getLogin()));
+299 params.add(new BasicNameValuePair("j_password", client.getPassword()));
+300 params.add(new BasicNameValuePair("action", "login"));
+301
+302 HttpResponse response = null;
+303try {
+304 postLogin.setEntity(new UrlEncodedFormEntity(params, HTTP.UTF_8));
+305 response = httpClient.execute(postLogin);
+306 } catch (IOException e) {
+307thrownewRundeckApiLoginException("Failed to post login form on " + location, e);
+308 }
+309
+310if (response.getStatusLine().getStatusCode() / 100 == 3) {
+311// HTTP client refuses to handle redirects (code 3xx) for POST, so we have to do it manually...
+312 location = response.getFirstHeader("Location").getValue();
+313try {
+314 EntityUtils.consume(response.getEntity());
+315 } catch (IOException e) {
+316thrownewRundeckApiLoginException("Failed to consume entity (release connection)", e);
+317 }
+318continue;
+319 }
+320if (response.getStatusLine().getStatusCode() / 100 != 2) {
+321thrownewRundeckApiLoginException("Invalid HTTP response '" + response.getStatusLine() + "' for "
+322 + location);
+323 }
+324try {
+325 String content = EntityUtils.toString(response.getEntity(), HTTP.UTF_8);
+326if (StringUtils.contains(content, "j_security_check")) {
+327thrownewRundeckApiLoginException("Login failed for user " + client.getLogin());
+328 }
+329try {
+330 EntityUtils.consume(response.getEntity());
+331 } catch (IOException e) {
+332thrownewRundeckApiLoginException("Failed to consume entity (release connection)", e);
+333 }
+334 } catch (IOException io) {
+335thrownewRundeckApiLoginException("Failed to read RunDeck result", io);
+336 } catch (ParseException p) {
+337thrownewRundeckApiLoginException("Failed to parse RunDeck response", p);
+338 }
+339break;
+340 }
+341 }
+342
+343/**
+344 * Instantiate a new {@link HttpClient} instance, configured to accept all SSL certificates
+345 *
+346 * @return an {@link HttpClient} instance - won't be null
+347 */
+348private HttpClient instantiateHttpClient() {
+349 DefaultHttpClient httpClient = new DefaultHttpClient();
+350
+351// configure SSL
+352 SSLSocketFactory socketFactory = null;
+353try {
+354 socketFactory = new SSLSocketFactory(new TrustStrategy() {
+355
+356 @Override
+357publicboolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
+358returntrue;
+359 }
+360 }, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
+361 } catch (KeyManagementException e) {
+362thrownew RuntimeException(e);
+363 } catch (UnrecoverableKeyException e) {
+364thrownew RuntimeException(e);
+365 } catch (NoSuchAlgorithmException e) {
+366thrownew RuntimeException(e);
+367 } catch (KeyStoreException e) {
+368thrownew RuntimeException(e);
+369 }
+370 httpClient.getConnectionManager().getSchemeRegistry().register(new Scheme("https", 443, socketFactory));
+371
+372// configure proxy (use system env : http.proxyHost / http.proxyPort)
+373 System.setProperty("java.net.useSystemProxies", "true");
+374 httpClient.setRoutePlanner(new ProxySelectorRoutePlanner(httpClient.getConnectionManager().getSchemeRegistry(),
+375 ProxySelector.getDefault()));
+376
+377return httpClient;
+378 }
+379 }
diff --git a/xref/org/rundeck/api/ApiPathBuilder.html b/xref/org/rundeck/api/ApiPathBuilder.html
index 3244960..5fcdb12 100644
--- a/xref/org/rundeck/api/ApiPathBuilder.html
+++ b/xref/org/rundeck/api/ApiPathBuilder.html
@@ -25,147 +25,192 @@
15 */16package org.rundeck.api;
17
-18import java.util.Properties;
-19import org.apache.commons.lang.StringUtils;
-20import org.rundeck.api.util.ParametersUtil;
-21
-22/**
-23 * Builder for API paths
-24 *
-25 * @author Vincent Behar
-26 */
-27classApiPathBuilder {
-28
-29/** Internally, we store everything in a {@link StringBuilder} */
-30privatefinal StringBuilder apiPath;
+18import java.io.InputStream;
+19import java.util.HashMap;
+20import java.util.Map;
+21import java.util.Properties;
+22import org.apache.commons.lang.StringUtils;
+23import org.rundeck.api.util.ParametersUtil;
+24
+25/**
+26 * Builder for API paths
+27 *
+28 * @author Vincent Behar
+29 */
+30classApiPathBuilder {
31
-32/** Maker for using the right separator between parameters ("?" or "&") */
-33privateboolean firstParamDone = false;
+32/** Internally, we store everything in a {@link StringBuilder} */
+33privatefinal StringBuilder apiPath;
34
-35/**
-36 * Build a new instance, for the given "path" (the "path" is the part before the parameters. The path and the
-37 * parameters are separated by a "?")
-38 *
-39 * @param paths elements of the path
-40 */
-41publicApiPathBuilder(String... paths) {
-42 apiPath = new StringBuilder();
-43if (paths != null) {
-44for (String path : paths) {
-45if (StringUtils.isNotBlank(path)) {
-46 append(path);
-47 }
-48 }
-49 }
-50 }
-51
-52/**
-53 * Append the given parameter (key and value). This will only append the parameter if it is not blank (null, empty
-54 * or whitespace), and make sure to add the right separator ("?" or "&") before. The key and value will be separated
-55 * by the "=" character. Also, the value will be url-encoded.
-56 *
-57 * @param key of the parameter. Must not be null or empty
-58 * @param value of the parameter. May be null/empty/blank. Will be url-encoded.
-59 * @return this, for method chaining
-60 */
-61publicApiPathBuilder param(String key, String value) {
-62if (StringUtils.isNotBlank(value)) {
-63 appendSeparator();
-64 append(key);
-65 append("=");
-66 append(ParametersUtil.urlEncode(value));
-67 }
-68returnthis;
-69 }
-70
-71/**
-72 * Append the given parameter (key and value). This will only append the parameter if it is not null, and make sure
-73 * to add the right separator ("?" or "&") before. The key and value will be separated by the "=" character.
-74 *
-75 * @param key of the parameter. Must not be null or empty
-76 * @param value of the parameter. May be null
-77 * @return this, for method chaining
-78 */
-79publicApiPathBuilder param(String key, Long value) {
-80if (value != null) {
-81 param(key, value.toString());
-82 }
-83returnthis;
-84 }
-85
-86/**
-87 * Append the given parameter (key and value). This will only append the parameter if it is not null, and make sure
-88 * to add the right separator ("?" or "&") before. The key and value will be separated by the "=" character.
-89 *
-90 * @param key of the parameter. Must not be null or empty
-91 * @param value of the parameter. May be null
-92 * @return this, for method chaining
-93 */
-94publicApiPathBuilder param(String key, Integer value) {
-95if (value != null) {
-96 param(key, value.toString());
-97 }
-98returnthis;
-99 }
-100
-101/**
-102 * Append the given parameter (key and value). This will only append the parameter if it is not null, and make sure
-103 * to add the right separator ("?" or "&") before. The key and value will be separated by the "=" character.
-104 *
-105 * @param key of the parameter. Must not be null or empty
-106 * @param value of the parameter. May be null
-107 * @return this, for method chaining
-108 */
-109publicApiPathBuilder param(String key, Boolean value) {
-110if (value != null) {
-111 param(key, value.toString());
-112 }
-113returnthis;
-114 }
-115
-116/**
-117 * Append the given node filters, only if it is not null/empty
-118 *
-119 * @param nodeFilters may be null/empty
-120 * @return this, for method chaining
-121 * @see ParametersUtil#generateNodeFiltersString(Properties)
-122 */
-123publicApiPathBuilder nodeFilters(Properties nodeFilters) {
-124 String filters = ParametersUtil.generateNodeFiltersString(nodeFilters);
-125if (StringUtils.isNotBlank(filters)) {
-126 appendSeparator();
-127 append(filters);
-128 }
-129returnthis;
-130 }
-131
-132 @Override
-133public String toString() {
-134return apiPath.toString();
-135 }
-136
-137/**
-138 * Append the given string
-139 *
-140 * @param str to append
-141 */
-142privatevoid append(String str) {
-143 apiPath.append(str);
-144 }
-145
-146/**
-147 * Append the right separator "?" or "&" between 2 parameters
-148 */
-149privatevoid appendSeparator() {
-150if (firstParamDone) {
-151 append("&");
-152 } else {
-153 append("?");
-154 firstParamDone = true;
-155 }
-156 }
-157
-158 }
+35/** When POSTing, we can add attachments */
+36privatefinal Map<String, InputStream> attachments;
+37
+38/** Marker for using the right separator between parameters ("?" or "&") */
+39privateboolean firstParamDone = false;
+40
+41/**
+42 * Build a new instance, for the given "path" (the "path" is the part before the parameters. The path and the
+43 * parameters are separated by a "?")
+44 *
+45 * @param paths elements of the path
+46 */
+47publicApiPathBuilder(String... paths) {
+48 apiPath = new StringBuilder();
+49 attachments = new HashMap<String, InputStream>();
+50if (paths != null) {
+51for (String path : paths) {
+52if (StringUtils.isNotBlank(path)) {
+53 append(path);
+54 }
+55 }
+56 }
+57 }
+58
+59/**
+60 * Append the given parameter (key and value). This will only append the parameter if it is not blank (null, empty
+61 * or whitespace), and make sure to add the right separator ("?" or "&") before. The key and value will be separated
+62 * by the "=" character. Also, the value will be url-encoded.
+63 *
+64 * @param key of the parameter. Must not be null or empty
+65 * @param value of the parameter. May be null/empty/blank. Will be url-encoded.
+66 * @return this, for method chaining
+67 */
+68publicApiPathBuilder param(String key, String value) {
+69if (StringUtils.isNotBlank(value)) {
+70 appendSeparator();
+71 append(key);
+72 append("=");
+73 append(ParametersUtil.urlEncode(value));
+74 }
+75returnthis;
+76 }
+77
+78/**
+79 * Append the given parameter (key and value). This will only append the parameter if it is not null, and make sure
+80 * to add the right separator ("?" or "&") before. The key and value will be separated by the "=" character. Also,
+81 * the value will be converted to lower-case.
+82 *
+83 * @param key of the parameter. Must not be null or empty
+84 * @param value of the parameter. May be null
+85 * @return this, for method chaining
+86 */
+87publicApiPathBuilder param(String key, Enum<?> value) {
+88if (value != null) {
+89 param(key, StringUtils.lowerCase(value.toString()));
+90 }
+91returnthis;
+92 }
+93
+94/**
+95 * Append the given parameter (key and value). This will only append the parameter if it is not null, and make sure
+96 * to add the right separator ("?" or "&") before. The key and value will be separated by the "=" character.
+97 *
+98 * @param key of the parameter. Must not be null or empty
+99 * @param value of the parameter. May be null
+100 * @return this, for method chaining
+101 */
+102publicApiPathBuilder param(String key, Long value) {
+103if (value != null) {
+104 param(key, value.toString());
+105 }
+106returnthis;
+107 }
+108
+109/**
+110 * Append the given parameter (key and value). This will only append the parameter if it is not null, and make sure
+111 * to add the right separator ("?" or "&") before. The key and value will be separated by the "=" character.
+112 *
+113 * @param key of the parameter. Must not be null or empty
+114 * @param value of the parameter. May be null
+115 * @return this, for method chaining
+116 */
+117publicApiPathBuilder param(String key, Integer value) {
+118if (value != null) {
+119 param(key, value.toString());
+120 }
+121returnthis;
+122 }
+123
+124/**
+125 * Append the given parameter (key and value). This will only append the parameter if it is not null, and make sure
+126 * to add the right separator ("?" or "&") before. The key and value will be separated by the "=" character.
+127 *
+128 * @param key of the parameter. Must not be null or empty
+129 * @param value of the parameter. May be null
+130 * @return this, for method chaining
+131 */
+132publicApiPathBuilder param(String key, Boolean value) {
+133if (value != null) {
+134 param(key, value.toString());
+135 }
+136returnthis;
+137 }
+138
+139/**
+140 * Append the given node filters, only if it is not null/empty
+141 *
+142 * @param nodeFilters may be null/empty
+143 * @return this, for method chaining
+144 * @see ParametersUtil#generateNodeFiltersString(Properties)
+145 */
+146publicApiPathBuilder nodeFilters(Properties nodeFilters) {
+147 String filters = ParametersUtil.generateNodeFiltersString(nodeFilters);
+148if (StringUtils.isNotBlank(filters)) {
+149 appendSeparator();
+150 append(filters);
+151 }
+152returnthis;
+153 }
+154
+155/**
+156 * When POSTing a request, add the given {@link InputStream} as an attachment to the content of the request. This
+157 * will only add the stream if it is not null.
+158 *
+159 * @param name of the attachment. Must not be null or empty
+160 * @param stream. May be null
+161 * @return this, for method chaining
+162 */
+163publicApiPathBuilder attach(String name, InputStream stream) {
+164if (stream != null) {
+165 attachments.put(name, stream);
+166 }
+167returnthis;
+168 }
+169
+170/**
+171 * @return all attachments to be POSTed, with their names
+172 */
+173public Map<String, InputStream> getAttachments() {
+174return attachments;
+175 }
+176
+177 @Override
+178public String toString() {
+179return apiPath.toString();
+180 }
+181
+182/**
+183 * Append the given string
+184 *
+185 * @param str to append
+186 */
+187privatevoid append(String str) {
+188 apiPath.append(str);
+189 }
+190
+191/**
+192 * Append the right separator "?" or "&" between 2 parameters
+193 */
+194privatevoid appendSeparator() {
+195if (firstParamDone) {
+196 append("&");
+197 } else {
+198 append("?");
+199 firstParamDone = true;
+200 }
+201 }
+202
+203 }
diff --git a/xref/org/rundeck/api/FileType.html b/xref/org/rundeck/api/FileType.html
new file mode 100644
index 0000000..e98760c
--- /dev/null
+++ b/xref/org/rundeck/api/FileType.html
@@ -0,0 +1,39 @@
+
+
+
+
+FileType xref
+
+
+
+
+
+1/*
+2 * Copyright 2011 Vincent Behar
+3 *
+4 * Licensed under the Apache License, Version 2.0 (the "License");
+5 * you may not use this file except in compliance with the License.
+6 * You may obtain a copy of the License at
+7 *
+8 * http://www.apache.org/licenses/LICENSE-2.0
+9 *
+10 * Unless required by applicable law or agreed to in writing, software
+11 * distributed under the License is distributed on an "AS IS" BASIS,
+12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+13 * See the License for the specific language governing permissions and
+14 * limitations under the License.
+15 */
+16package org.rundeck.api;
+17
+18/**
+19 * All supported types of files.
+20 *
+21 * @author Vincent Behar
+22 */
+23public enum FileType {
+24 XML, YAML;
+25 }
+
+
+
+
diff --git a/xref/org/rundeck/api/RundeckClient.html b/xref/org/rundeck/api/RundeckClient.html
index a0cddb4..a0369a3 100644
--- a/xref/org/rundeck/api/RundeckClient.html
+++ b/xref/org/rundeck/api/RundeckClient.html
@@ -26,1021 +26,1383 @@
16package org.rundeck.api;
1718import java.io.File;
-19import java.io.IOException;
-20import java.io.InputStream;
-21import java.io.Serializable;
-22import java.util.ArrayList;
-23import java.util.List;
-24import java.util.Properties;
-25import java.util.concurrent.TimeUnit;
-26import org.apache.commons.io.FileUtils;
-27import org.apache.commons.io.IOUtils;
-28import org.apache.commons.lang.StringUtils;
-29import org.rundeck.api.RundeckApiException.RundeckApiLoginException;
-30import org.rundeck.api.domain.RundeckAbort;
-31import org.rundeck.api.domain.RundeckExecution;
-32import org.rundeck.api.domain.RundeckJob;
-33import org.rundeck.api.domain.RundeckNode;
-34import org.rundeck.api.domain.RundeckProject;
-35import org.rundeck.api.domain.RundeckSystemInfo;
-36import org.rundeck.api.domain.RundeckExecution.ExecutionStatus;
-37import org.rundeck.api.parser.AbortParser;
-38import org.rundeck.api.parser.ExecutionParser;
-39import org.rundeck.api.parser.JobParser;
-40import org.rundeck.api.parser.ListParser;
-41import org.rundeck.api.parser.NodeParser;
-42import org.rundeck.api.parser.ProjectParser;
-43import org.rundeck.api.parser.StringParser;
-44import org.rundeck.api.parser.SystemInfoParser;
-45import org.rundeck.api.util.AssertUtil;
-46import org.rundeck.api.util.ParametersUtil;
-47
-48/**
-49 * Main entry point to talk to a RunDeck instance.<br>
-50 * Usage : <br>
-51 * <code>
-52 * <pre>
-53 * RundeckClient rundeck = new RundeckClient("http://localhost:4440", "admin", "admin");
-54 * List<RundeckJob> jobs = rundeck.getJobs();
-55 *
-56 * RundeckJob job = rundeck.findJob("my-project", "main-group/sub-group", "job-name");
-57 * RundeckExecution execution = rundeck.triggerJob(job.getId(),
-58 * new OptionsBuilder().addOption("version", "1.2.0").toProperties());
-59 *
-60 * List<RundeckExecution> runningExecutions = rundeck.getRunningExecutions("my-project");
-61 * </pre>
-62 * </code>
-63 *
-64 * @author Vincent Behar
-65 */
-66publicclassRundeckClientimplements Serializable {
-67
-68privatestaticfinallong serialVersionUID = 1L;
-69
-70publicstaticfinaltransientint API_VERSION = 1;
-71
-72publicstaticfinaltransient String API_ENDPOINT = "/api/" + API_VERSION;
-73
-74privatefinal String url;
+19import java.io.FileInputStream;
+20import java.io.IOException;
+21import java.io.InputStream;
+22import java.io.Serializable;
+23import java.util.ArrayList;
+24import java.util.List;
+25import java.util.Properties;
+26import java.util.concurrent.TimeUnit;
+27import org.apache.commons.io.FileUtils;
+28import org.apache.commons.io.IOUtils;
+29import org.apache.commons.lang.StringUtils;
+30import org.rundeck.api.RundeckApiException.RundeckApiLoginException;
+31import org.rundeck.api.domain.RundeckAbort;
+32import org.rundeck.api.domain.RundeckExecution;
+33import org.rundeck.api.domain.RundeckJob;
+34import org.rundeck.api.domain.RundeckJobsImportMethod;
+35import org.rundeck.api.domain.RundeckJobsImportResult;
+36import org.rundeck.api.domain.RundeckNode;
+37import org.rundeck.api.domain.RundeckProject;
+38import org.rundeck.api.domain.RundeckSystemInfo;
+39import org.rundeck.api.domain.RundeckExecution.ExecutionStatus;
+40import org.rundeck.api.parser.AbortParser;
+41import org.rundeck.api.parser.ExecutionParser;
+42import org.rundeck.api.parser.JobParser;
+43import org.rundeck.api.parser.JobsImportResultParser;
+44import org.rundeck.api.parser.ListParser;
+45import org.rundeck.api.parser.NodeParser;
+46import org.rundeck.api.parser.ProjectParser;
+47import org.rundeck.api.parser.StringParser;
+48import org.rundeck.api.parser.SystemInfoParser;
+49import org.rundeck.api.util.AssertUtil;
+50import org.rundeck.api.util.ParametersUtil;
+51
+52/**
+53 * Main entry point to talk to a RunDeck instance.<br>
+54 * Usage : <br>
+55 * <code>
+56 * <pre>
+57 * RundeckClient rundeck = new RundeckClient("http://localhost:4440", "admin", "admin");
+58 *
+59 * List<RundeckProject> projects = rundeck.getProjects();
+60 *
+61 * RundeckJob job = rundeck.findJob("my-project", "main-group/sub-group", "job-name");
+62 * RundeckExecution execution = rundeck.triggerJob(job.getId(),
+63 * new OptionsBuilder().addOption("version", "1.2.0").toProperties());
+64 *
+65 * List<RundeckExecution> runningExecutions = rundeck.getRunningExecutions("my-project");
+66 *
+67 * rundeck.exportJobsToFile("/tmp/jobs.xml", FileType.XML, "my-project");
+68 * rundeck.importJobs("/tmp/jobs.xml", FileType.XML);
+69 * </pre>
+70 * </code>
+71 *
+72 * @author Vincent Behar
+73 */
+74publicclassRundeckClientimplements Serializable {
75
-76privatefinal String login;
+76privatestaticfinallong serialVersionUID = 1L;
77
-78privatefinal String password;
+78publicstaticfinaltransientint API_VERSION = 1;
79
-80/**
-81 * Instantiate a new {@link RundeckClient} for the RunDeck instance at the given url
-82 *
-83 * @param url of the RunDeck instance ("http://localhost:4440", "http://rundeck.your-compagny.com/", etc)
-84 * @param login
-85 * @param password
-86 * @throws IllegalArgumentException if the url, login or password is blank (null, empty or whitespace)
-87 */
-88publicRundeckClient(String url, String login, String password) throws IllegalArgumentException {
-89super();
-90this.url = url;
-91this.login = login;
-92this.password = password;
-93 AssertUtil.notBlank(url, "The RunDeck URL is mandatory !");
-94 AssertUtil.notBlank(login, "The RunDeck login is mandatory !");
-95 AssertUtil.notBlank(password, "The RunDeck password is mandatory !");
-96 }
-97
-98/**
-99 * Try to "ping" the RunDeck instance to see if it is alive
-100 *
-101 * @throws RundeckApiException if the ping fails
-102 */
-103publicvoid ping() throws RundeckApiException {
-104newApiCall(this).ping();
-105 }
-106
-107/**
-108 * Test your credentials (login/password) on the RunDeck instance
-109 *
-110 * @throws RundeckApiLoginException if the login fails
-111 */
-112publicvoid testCredentials() throws RundeckApiLoginException {
-113newApiCall(this).testCredentials();
-114 }
-115
-116/*
-117 * Projects
-118 */
-119
-120/**
-121 * List all projects
-122 *
-123 * @return a {@link List} of {@link RundeckProject} : might be empty, but won't be null
-124 * @throws RundeckApiException in case of error when calling the API
-125 * @throws RundeckApiLoginException if the login failed
-126 */
-127public List<RundeckProject> getProjects() throws RundeckApiException, RundeckApiLoginException {
-128returnnewApiCall(this).get(newApiPathBuilder("/projects"),
-129new ListParser<RundeckProject>(newProjectParser(), "result/projects/project"));
-130 }
-131
-132/**
-133 * Get the definition of a single project, identified by the given name
-134 *
-135 * @param projectName name of the project - mandatory
-136 * @return a {@link RundeckProject} instance - won't be null
-137 * @throws RundeckApiException in case of error when calling the API (non-existent project with this name)
-138 * @throws RundeckApiLoginException if the login failed
-139 * @throws IllegalArgumentException if the projectName is blank (null, empty or whitespace)
-140 */
-141publicRundeckProject getProject(String projectName) throws RundeckApiException, RundeckApiLoginException,
-142 IllegalArgumentException {
-143 AssertUtil.notBlank(projectName, "projectName is mandatory to get the details of a project !");
-144returnnewApiCall(this).get(newApiPathBuilder("/project/", projectName),
-145newProjectParser("result/projects/project"));
-146 }
-147
-148/*
-149 * Jobs
-150 */
-151
-152/**
-153 * List all jobs (for all projects)
-154 *
-155 * @return a {@link List} of {@link RundeckJob} : might be empty, but won't be null
-156 * @throws RundeckApiException in case of error when calling the API
-157 * @throws RundeckApiLoginException if the login failed
-158 */
-159public List<RundeckJob> getJobs() throws RundeckApiException, RundeckApiLoginException {
-160 List<RundeckJob> jobs = new ArrayList<RundeckJob>();
-161for (RundeckProject project : getProjects()) {
-162 jobs.addAll(getJobs(project.getName()));
-163 }
-164return jobs;
-165 }
-166
-167/**
-168 * List all jobs that belongs to the given project
-169 *
-170 * @param project name of the project - mandatory
-171 * @return a {@link List} of {@link RundeckJob} : might be empty, but won't be null
-172 * @throws RundeckApiException in case of error when calling the API (non-existent project with this name)
-173 * @throws RundeckApiLoginException if the login failed
-174 * @throws IllegalArgumentException if the project is blank (null, empty or whitespace)
-175 * @see #getJobs(String, String, String, String...)
-176 */
-177public List<RundeckJob> getJobs(String project) throws RundeckApiException, RundeckApiLoginException,
-178 IllegalArgumentException {
-179return getJobs(project, null, null, new String[0]);
-180 }
-181
-182/**
-183 * List the jobs that belongs to the given project, and matches the given criteria (jobFilter, groupPath and jobIds)
-184 *
-185 * @param project name of the project - mandatory
-186 * @param jobFilter a filter for the job Name - optional
-187 * @param groupPath a group or partial group path to include all jobs within that group path - optional
-188 * @param jobIds a list of Job IDs to include - optional
-189 * @return a {@link List} of {@link RundeckJob} : might be empty, but won't be null
-190 * @throws RundeckApiException in case of error when calling the API (non-existent project with this name)
-191 * @throws RundeckApiLoginException if the login failed
-192 * @throws IllegalArgumentException if the project is blank (null, empty or whitespace)
-193 * @see #getJobs(String)
-194 */
-195public List<RundeckJob> getJobs(String project, String jobFilter, String groupPath, String... jobIds)
-196throws RundeckApiException, RundeckApiLoginException, IllegalArgumentException {
-197 AssertUtil.notBlank(project, "project is mandatory to get all jobs !");
-198returnnewApiCall(this).get(newApiPathBuilder("/jobs").param("project", project)
-199 .param("jobFilter", jobFilter)
-200 .param("groupPath", groupPath)
-201 .param("idlist", StringUtils.join(jobIds, ",")),
-202new ListParser<RundeckJob>(newJobParser(), "result/jobs/job"));
-203 }
-204
-205/**
-206 * Export the definitions of all jobs that belongs to the given project, as an XML file
-207 *
-208 * @param filename path of the file where the content should be saved
-209 * @param project name of the project - mandatory
-210 * @throws RundeckApiException in case of error when calling the API (non-existent project with this name)
-211 * @throws RundeckApiLoginException if the login failed
-212 * @throws IllegalArgumentException if the project is blank (null, empty or whitespace)
-213 * @throws IOException if we failed to write to the file
-214 * @see #exportJobsToFile(String, String, String, String, String...)
-215 * @see #exportJobs(String)
-216 */
-217publicvoid exportJobsToFile(String filename, String project) throws RundeckApiException, RundeckApiLoginException,
-218 IllegalArgumentException, IOException {
-219 exportJobsToFile(filename, project, null, null, new String[0]);
-220 }
-221
-222/**
-223 * Export the definitions of the jobs that belongs to the given project, and matches the given criteria (jobFilter,
-224 * groupPath and jobIds), as an XML file
-225 *
-226 * @param filename path of the file where the content should be saved
-227 * @param project name of the project - mandatory
-228 * @param jobFilter a filter for the job Name - optional
-229 * @param groupPath a group or partial group path to include all jobs within that group path - optional
-230 * @param jobIds a list of Job IDs to include - optional
-231 * @throws RundeckApiException in case of error when calling the API (non-existent project with this name)
-232 * @throws RundeckApiLoginException if the login failed
-233 * @throws IllegalArgumentException if the project is blank (null, empty or whitespace)
-234 * @throws IOException if we failed to write to the file
-235 * @see #exportJobsToFile(String, String)
-236 * @see #exportJobs(String, String, String, String...)
-237 */
-238publicvoid exportJobsToFile(String filename, String project, String jobFilter, String groupPath, String... jobIds)
-239throws RundeckApiException, RundeckApiLoginException, IllegalArgumentException, IOException {
-240 InputStream inputStream = exportJobs(project, jobFilter, groupPath, jobIds);
-241 FileUtils.writeByteArrayToFile(new File(filename), IOUtils.toByteArray(inputStream));
-242 }
-243
-244/**
-245 * Export the definitions of all jobs that belongs to the given project
-246 *
-247 * @param project name of the project - mandatory
-248 * @return an {@link InputStream} instance, not linked to any network resources - won't be null
-249 * @throws RundeckApiException in case of error when calling the API (non-existent project with this name)
-250 * @throws RundeckApiLoginException if the login failed
-251 * @throws IllegalArgumentException if the project is blank (null, empty or whitespace)
-252 * @see #exportJobs(String, String, String, String...)
-253 * @see #exportJobsToFile(String, String)
-254 */
-255public InputStream exportJobs(String project) throws RundeckApiException, RundeckApiLoginException,
-256 IllegalArgumentException {
-257return exportJobs(project, null, null, new String[0]);
-258 }
-259
-260/**
-261 * Export the definitions of the jobs that belongs to the given project, and matches the given criteria (jobFilter,
-262 * groupPath and jobIds)
-263 *
-264 * @param project name of the project - mandatory
-265 * @param jobFilter a filter for the job Name - optional
-266 * @param groupPath a group or partial group path to include all jobs within that group path - optional
-267 * @param jobIds a list of Job IDs to include - optional
-268 * @return an {@link InputStream} instance, not linked to any network resources - won't be null
-269 * @throws RundeckApiException in case of error when calling the API (non-existent project with this name)
-270 * @throws RundeckApiLoginException if the login failed
-271 * @throws IllegalArgumentException if the project is blank (null, empty or whitespace)
-272 * @see #exportJobs(String)
-273 * @see #exportJobsToFile(String, String, String, String, String...)
-274 */
-275public InputStream exportJobs(String project, String jobFilter, String groupPath, String... jobIds)
-276throws RundeckApiException, RundeckApiLoginException, IllegalArgumentException {
-277 AssertUtil.notBlank(project, "project is mandatory to export all jobs !");
-278returnnewApiCall(this).get(newApiPathBuilder("/jobs/export").param("project", project)
-279 .param("jobFilter", jobFilter)
-280 .param("groupPath", groupPath)
-281 .param("idlist", StringUtils.join(jobIds, ",")));
-282 }
-283
-284/**
-285 * Export the definition of a single job (identified by the given ID), as an XML file
-286 *
-287 * @param filename path of the file where the content should be saved
-288 * @param jobId identifier of the job - mandatory
-289 * @throws RundeckApiException in case of error when calling the API (non-existent job with this ID)
-290 * @throws RundeckApiLoginException if the login failed
-291 * @throws IllegalArgumentException if the jobId is blank (null, empty or whitespace)
-292 * @throws IOException if we failed to write to the file
-293 * @see #exportJob(String)
-294 * @see #getJob(String)
-295 */
-296publicvoid exportJobToFile(String filename, String jobId) throws RundeckApiException, RundeckApiLoginException,
-297 IllegalArgumentException, IOException {
-298 InputStream inputStream = exportJob(jobId);
-299 FileUtils.writeByteArrayToFile(new File(filename), IOUtils.toByteArray(inputStream));
-300 }
-301
-302/**
-303 * Export the definition of a single job, identified by the given ID
-304 *
-305 * @param jobId identifier of the job - mandatory
-306 * @return an {@link InputStream} instance, not linked to any network resources - won't be null
-307 * @throws RundeckApiException in case of error when calling the API (non-existent job with this ID)
-308 * @throws RundeckApiLoginException if the login failed
-309 * @throws IllegalArgumentException if the jobId is blank (null, empty or whitespace)
-310 * @see #exportJobToFile(String, String)
-311 * @see #getJob(String)
-312 */
-313public InputStream exportJob(String jobId) throws RundeckApiException, RundeckApiLoginException,
-314 IllegalArgumentException {
-315 AssertUtil.notBlank(jobId, "jobId is mandatory to get the details of a job !");
-316returnnewApiCall(this).get(newApiPathBuilder("/job/", jobId));
-317 }
-318
-319/**
-320 * Find a job, identified by its project, group and name. Note that the groupPath is optional, as a job does not
-321 * need to belong to a group (either pass null, or an empty string).
-322 *
-323 * @param project name of the project - mandatory
-324 * @param groupPath group to which the job belongs (if it belongs to a group) - optional
-325 * @param name of the job to find - mandatory
-326 * @return a {@link RundeckJob} instance - null if not found
-327 * @throws RundeckApiException in case of error when calling the API (non-existent project with this name)
-328 * @throws RundeckApiLoginException if the login failed
-329 * @throws IllegalArgumentException if the project or the name is blank (null, empty or whitespace)
-330 * @see #getJob(String)
-331 */
-332publicRundeckJob findJob(String project, String groupPath, String name) throws RundeckApiException,
-333 RundeckApiLoginException, IllegalArgumentException {
-334 AssertUtil.notBlank(project, "project is mandatory to find a job !");
-335 AssertUtil.notBlank(name, "job name is mandatory to find a job !");
-336 List<RundeckJob> jobs = getJobs(project, name, groupPath, new String[0]);
-337return jobs.isEmpty() ? null : jobs.get(0);
-338 }
-339
-340/**
-341 * Get the definition of a single job, identified by the given ID
-342 *
-343 * @param jobId identifier of the job - mandatory
-344 * @return a {@link RundeckJob} instance - won't be null
-345 * @throws RundeckApiException in case of error when calling the API (non-existent job with this ID)
-346 * @throws RundeckApiLoginException if the login failed
-347 * @throws IllegalArgumentException if the jobId is blank (null, empty or whitespace)
-348 * @see #findJob(String, String, String)
-349 * @see #exportJob(String)
-350 */
-351publicRundeckJob getJob(String jobId) throws RundeckApiException, RundeckApiLoginException,
-352 IllegalArgumentException {
-353 AssertUtil.notBlank(jobId, "jobId is mandatory to get the details of a job !");
-354returnnewApiCall(this).get(newApiPathBuilder("/job/", jobId), newJobParser("joblist/job"));
-355 }
-356
-357/**
-358 * Delete a single job, identified by the given ID
-359 *
-360 * @param jobId identifier of the job - mandatory
-361 * @return the success message (note that in case of error, you'll get an exception)
-362 * @throws RundeckApiException in case of error when calling the API (non-existent job with this ID)
-363 * @throws RundeckApiLoginException if the login failed
-364 * @throws IllegalArgumentException if the jobId is blank (null, empty or whitespace)
-365 */
-366public String deleteJob(String jobId) throws RundeckApiException, RundeckApiLoginException,
-367 IllegalArgumentException {
-368 AssertUtil.notBlank(jobId, "jobId is mandatory to delete a job !");
-369returnnewApiCall(this).delete(newApiPathBuilder("/job/", jobId), newStringParser("result/success/message"));
-370 }
-371
-372/**
-373 * Trigger the execution of a RunDeck job (identified by the given ID), and return immediately (without waiting the
-374 * end of the job execution)
-375 *
-376 * @param jobId identifier of the job - mandatory
-377 * @return a {@link RundeckExecution} instance for the newly created (and running) execution - won't be null
-378 * @throws RundeckApiException in case of error when calling the API (non-existent job with this ID)
-379 * @throws RundeckApiLoginException if the login failed
-380 * @throws IllegalArgumentException if the jobId is blank (null, empty or whitespace)
-381 * @see #triggerJob(String, Properties, Properties)
-382 * @see #runJob(String)
-383 */
-384publicRundeckExecution triggerJob(String jobId) throws RundeckApiException, RundeckApiLoginException,
-385 IllegalArgumentException {
-386return triggerJob(jobId, null);
-387 }
-388
-389/**
-390 * Trigger the execution of a RunDeck job (identified by the given ID), and return immediately (without waiting the
-391 * end of the job execution)
-392 *
-393 * @param jobId identifier of the job - mandatory
-394 * @param options of the job - optional. See {@link OptionsBuilder}.
-395 * @return a {@link RundeckExecution} instance for the newly created (and running) execution - won't be null
-396 * @throws RundeckApiException in case of error when calling the API (non-existent job with this ID)
-397 * @throws RundeckApiLoginException if the login failed
-398 * @throws IllegalArgumentException if the jobId is blank (null, empty or whitespace)
-399 * @see #triggerJob(String, Properties, Properties)
-400 * @see #runJob(String, Properties)
-401 */
-402publicRundeckExecution triggerJob(String jobId, Properties options) throws RundeckApiException,
-403 RundeckApiLoginException, IllegalArgumentException {
-404return triggerJob(jobId, options, null);
-405 }
-406
-407/**
-408 * Trigger the execution of a RunDeck job (identified by the given ID), and return immediately (without waiting the
-409 * end of the job execution)
-410 *
-411 * @param jobId identifier of the job - mandatory
-412 * @param options of the job - optional. See {@link OptionsBuilder}.
-413 * @param nodeFilters for overriding the nodes on which the job will be executed - optional. See
-414 * {@link NodeFiltersBuilder}
-415 * @return a {@link RundeckExecution} instance for the newly created (and running) execution - won't be null
-416 * @throws RundeckApiException in case of error when calling the API (non-existent job with this ID)
-417 * @throws RundeckApiLoginException if the login failed
-418 * @throws IllegalArgumentException if the jobId is blank (null, empty or whitespace)
-419 * @see #triggerJob(String)
-420 * @see #runJob(String, Properties, Properties)
-421 */
-422publicRundeckExecution triggerJob(String jobId, Properties options, Properties nodeFilters)
-423throws RundeckApiException, RundeckApiLoginException, IllegalArgumentException {
-424 AssertUtil.notBlank(jobId, "jobId is mandatory to trigger a job !");
-425returnnewApiCall(this).get(newApiPathBuilder("/job/", jobId, "/run").param("argString",
-426 ParametersUtil.generateArgString(options))
-427 .nodeFilters(nodeFilters),
-428newExecutionParser("result/executions/execution"));
-429 }
-430
-431/**
-432 * Run a RunDeck job (identified by the given ID), and wait until its execution is finished (or aborted) to return.
-433 * We will poll the RunDeck server at regular interval (every 5 seconds) to know if the execution is finished (or
-434 * aborted) or is still running.
+80publicstaticfinaltransient String API_ENDPOINT = "/api/" + API_VERSION;
+81
+82privatefinal String url;
+83
+84privatefinal String login;
+85
+86privatefinal String password;
+87
+88/**
+89 * Instantiate a new {@link RundeckClient} for the RunDeck instance at the given url
+90 *
+91 * @param url of the RunDeck instance ("http://localhost:4440", "http://rundeck.your-compagny.com/", etc)
+92 * @param login
+93 * @param password
+94 * @throws IllegalArgumentException if the url, login or password is blank (null, empty or whitespace)
+95 */
+96publicRundeckClient(String url, String login, String password) throws IllegalArgumentException {
+97super();
+98this.url = url;
+99this.login = login;
+100this.password = password;
+101 AssertUtil.notBlank(url, "The RunDeck URL is mandatory !");
+102 AssertUtil.notBlank(login, "The RunDeck login is mandatory !");
+103 AssertUtil.notBlank(password, "The RunDeck password is mandatory !");
+104 }
+105
+106/**
+107 * Try to "ping" the RunDeck instance to see if it is alive
+108 *
+109 * @throws RundeckApiException if the ping fails
+110 */
+111publicvoid ping() throws RundeckApiException {
+112newApiCall(this).ping();
+113 }
+114
+115/**
+116 * Test your credentials (login/password) on the RunDeck instance
+117 *
+118 * @throws RundeckApiLoginException if the login fails
+119 */
+120publicvoid testCredentials() throws RundeckApiLoginException {
+121newApiCall(this).testCredentials();
+122 }
+123
+124/*
+125 * Projects
+126 */
+127
+128/**
+129 * List all projects
+130 *
+131 * @return a {@link List} of {@link RundeckProject} : might be empty, but won't be null
+132 * @throws RundeckApiException in case of error when calling the API
+133 * @throws RundeckApiLoginException if the login failed
+134 */
+135public List<RundeckProject> getProjects() throws RundeckApiException, RundeckApiLoginException {
+136returnnewApiCall(this).get(newApiPathBuilder("/projects"),
+137new ListParser<RundeckProject>(newProjectParser(), "result/projects/project"));
+138 }
+139
+140/**
+141 * Get the definition of a single project, identified by the given name
+142 *
+143 * @param projectName name of the project - mandatory
+144 * @return a {@link RundeckProject} instance - won't be null
+145 * @throws RundeckApiException in case of error when calling the API (non-existent project with this name)
+146 * @throws RundeckApiLoginException if the login failed
+147 * @throws IllegalArgumentException if the projectName is blank (null, empty or whitespace)
+148 */
+149publicRundeckProject getProject(String projectName) throws RundeckApiException, RundeckApiLoginException,
+150 IllegalArgumentException {
+151 AssertUtil.notBlank(projectName, "projectName is mandatory to get the details of a project !");
+152returnnewApiCall(this).get(newApiPathBuilder("/project/", projectName),
+153newProjectParser("result/projects/project"));
+154 }
+155
+156/*
+157 * Jobs
+158 */
+159
+160/**
+161 * List all jobs (for all projects)
+162 *
+163 * @return a {@link List} of {@link RundeckJob} : might be empty, but won't be null
+164 * @throws RundeckApiException in case of error when calling the API
+165 * @throws RundeckApiLoginException if the login failed
+166 */
+167public List<RundeckJob> getJobs() throws RundeckApiException, RundeckApiLoginException {
+168 List<RundeckJob> jobs = new ArrayList<RundeckJob>();
+169for (RundeckProject project : getProjects()) {
+170 jobs.addAll(getJobs(project.getName()));
+171 }
+172return jobs;
+173 }
+174
+175/**
+176 * List all jobs that belongs to the given project
+177 *
+178 * @param project name of the project - mandatory
+179 * @return a {@link List} of {@link RundeckJob} : might be empty, but won't be null
+180 * @throws RundeckApiException in case of error when calling the API (non-existent project with this name)
+181 * @throws RundeckApiLoginException if the login failed
+182 * @throws IllegalArgumentException if the project is blank (null, empty or whitespace)
+183 * @see #getJobs(String, String, String, String...)
+184 */
+185public List<RundeckJob> getJobs(String project) throws RundeckApiException, RundeckApiLoginException,
+186 IllegalArgumentException {
+187return getJobs(project, null, null, new String[0]);
+188 }
+189
+190/**
+191 * List the jobs that belongs to the given project, and matches the given criteria (jobFilter, groupPath and jobIds)
+192 *
+193 * @param project name of the project - mandatory
+194 * @param jobFilter a filter for the job Name - optional
+195 * @param groupPath a group or partial group path to include all jobs within that group path - optional
+196 * @param jobIds a list of Job IDs to include - optional
+197 * @return a {@link List} of {@link RundeckJob} : might be empty, but won't be null
+198 * @throws RundeckApiException in case of error when calling the API (non-existent project with this name)
+199 * @throws RundeckApiLoginException if the login failed
+200 * @throws IllegalArgumentException if the project is blank (null, empty or whitespace)
+201 * @see #getJobs(String)
+202 */
+203public List<RundeckJob> getJobs(String project, String jobFilter, String groupPath, String... jobIds)
+204throws RundeckApiException, RundeckApiLoginException, IllegalArgumentException {
+205 AssertUtil.notBlank(project, "project is mandatory to get all jobs !");
+206returnnewApiCall(this).get(newApiPathBuilder("/jobs").param("project", project)
+207 .param("jobFilter", jobFilter)
+208 .param("groupPath", groupPath)
+209 .param("idlist", StringUtils.join(jobIds, ",")),
+210new ListParser<RundeckJob>(newJobParser(), "result/jobs/job"));
+211 }
+212
+213/**
+214 * Export the definitions of all jobs that belongs to the given project
+215 *
+216 * @param filename path of the file where the content should be saved - mandatory
+217 * @param format of the export. See {@link FileType} - mandatory
+218 * @param project name of the project - mandatory
+219 * @throws RundeckApiException in case of error when calling the API (non-existent project with this name)
+220 * @throws RundeckApiLoginException if the login failed
+221 * @throws IllegalArgumentException if the format or project is blank (null, empty or whitespace), or the format is
+222 * invalid
+223 * @throws IOException if we failed to write to the file
+224 * @see #exportJobsToFile(String, FileType, String, String, String, String...)
+225 * @see #exportJobs(String, String)
+226 */
+227publicvoid exportJobsToFile(String filename, String format, String project) throws RundeckApiException,
+228 RundeckApiLoginException, IllegalArgumentException, IOException {
+229 AssertUtil.notBlank(format, "format is mandatory to export jobs !");
+230 exportJobsToFile(filename, FileType.valueOf(StringUtils.upperCase(format)), project);
+231 }
+232
+233/**
+234 * Export the definitions of all jobs that belongs to the given project
+235 *
+236 * @param filename path of the file where the content should be saved - mandatory
+237 * @param format of the export. See {@link FileType} - mandatory
+238 * @param project name of the project - mandatory
+239 * @throws RundeckApiException in case of error when calling the API (non-existent project with this name)
+240 * @throws RundeckApiLoginException if the login failed
+241 * @throws IllegalArgumentException if the project is blank (null, empty or whitespace) or the format is null
+242 * @throws IOException if we failed to write to the file
+243 * @see #exportJobsToFile(String, FileType, String, String, String, String...)
+244 * @see #exportJobs(FileType, String)
+245 */
+246publicvoid exportJobsToFile(String filename, FileType format, String project) throws RundeckApiException,
+247 RundeckApiLoginException, IllegalArgumentException, IOException {
+248 exportJobsToFile(filename, format, project, null, null, new String[0]);
+249 }
+250
+251/**
+252 * Export the definitions of the jobs that belongs to the given project, and matches the given criteria (jobFilter,
+253 * groupPath and jobIds)
+254 *
+255 * @param filename path of the file where the content should be saved - mandatory
+256 * @param format of the export. See {@link FileType} - mandatory
+257 * @param project name of the project - mandatory
+258 * @param jobFilter a filter for the job Name - optional
+259 * @param groupPath a group or partial group path to include all jobs within that group path - optional
+260 * @param jobIds a list of Job IDs to include - optional
+261 * @throws RundeckApiException in case of error when calling the API (non-existent project with this name)
+262 * @throws RundeckApiLoginException if the login failed
+263 * @throws IllegalArgumentException if the filename, format or project is blank (null, empty or whitespace), or the
+264 * format is invalid
+265 * @throws IOException if we failed to write to the file
+266 * @see #exportJobsToFile(String, FileType, String, String, String, String...)
+267 * @see #exportJobs(FileType, String, String, String, String...)
+268 */
+269publicvoid exportJobsToFile(String filename, String format, String project, String jobFilter, String groupPath,
+270 String... jobIds) throws RundeckApiException, RundeckApiLoginException, IllegalArgumentException,
+271 IOException {
+272 AssertUtil.notBlank(format, "format is mandatory to export jobs !");
+273 exportJobsToFile(filename,
+274 FileType.valueOf(StringUtils.upperCase(format)),
+275 project,
+276 jobFilter,
+277 groupPath,
+278 jobIds);
+279 }
+280
+281/**
+282 * Export the definitions of the jobs that belongs to the given project, and matches the given criteria (jobFilter,
+283 * groupPath and jobIds)
+284 *
+285 * @param filename path of the file where the content should be saved - mandatory
+286 * @param format of the export. See {@link FileType} - mandatory
+287 * @param project name of the project - mandatory
+288 * @param jobFilter a filter for the job Name - optional
+289 * @param groupPath a group or partial group path to include all jobs within that group path - optional
+290 * @param jobIds a list of Job IDs to include - optional
+291 * @throws RundeckApiException in case of error when calling the API (non-existent project with this name)
+292 * @throws RundeckApiLoginException if the login failed
+293 * @throws IllegalArgumentException if the filename or project is blank (null, empty or whitespace), or the format
+294 * is null
+295 * @throws IOException if we failed to write to the file
+296 * @see #exportJobs(FileType, String, String, String, String...)
+297 */
+298publicvoid exportJobsToFile(String filename, FileType format, String project, String jobFilter, String groupPath,
+299 String... jobIds) throws RundeckApiException, RundeckApiLoginException, IllegalArgumentException,
+300 IOException {
+301 AssertUtil.notBlank(filename, "filename is mandatory to export a job !");
+302 InputStream inputStream = exportJobs(format, project, jobFilter, groupPath, jobIds);
+303 FileUtils.writeByteArrayToFile(new File(filename), IOUtils.toByteArray(inputStream));
+304 }
+305
+306/**
+307 * Export the definitions of all jobs that belongs to the given project
+308 *
+309 * @param format of the export. See {@link FileType} - mandatory
+310 * @param project name of the project - mandatory
+311 * @return an {@link InputStream} instance, not linked to any network resources - won't be null
+312 * @throws RundeckApiException in case of error when calling the API (non-existent project with this name)
+313 * @throws RundeckApiLoginException if the login failed
+314 * @throws IllegalArgumentException if the format or project is blank (null, empty or whitespace), or the format is
+315 * invalid
+316 * @see #exportJobs(FileType, String, String, String, String...)
+317 * @see #exportJobsToFile(String, String, String)
+318 */
+319public InputStream exportJobs(String format, String project) throws RundeckApiException, RundeckApiLoginException,
+320 IllegalArgumentException {
+321 AssertUtil.notBlank(format, "format is mandatory to export jobs !");
+322return exportJobs(FileType.valueOf(StringUtils.upperCase(format)), project);
+323 }
+324
+325/**
+326 * Export the definitions of all jobs that belongs to the given project
+327 *
+328 * @param format of the export. See {@link FileType} - mandatory
+329 * @param project name of the project - mandatory
+330 * @return an {@link InputStream} instance, not linked to any network resources - won't be null
+331 * @throws RundeckApiException in case of error when calling the API (non-existent project with this name)
+332 * @throws RundeckApiLoginException if the login failed
+333 * @throws IllegalArgumentException if the project is blank (null, empty or whitespace) or the format is null
+334 * @see #exportJobs(FileType, String, String, String, String...)
+335 * @see #exportJobsToFile(String, FileType, String)
+336 */
+337public InputStream exportJobs(FileType format, String project) throws RundeckApiException,
+338 RundeckApiLoginException, IllegalArgumentException {
+339return exportJobs(format, project, null, null, new String[0]);
+340 }
+341
+342/**
+343 * Export the definitions of the jobs that belongs to the given project, and matches the given criteria (jobFilter,
+344 * groupPath and jobIds)
+345 *
+346 * @param format of the export. See {@link FileType} - mandatory
+347 * @param project name of the project - mandatory
+348 * @param jobFilter a filter for the job Name - optional
+349 * @param groupPath a group or partial group path to include all jobs within that group path - optional
+350 * @param jobIds a list of Job IDs to include - optional
+351 * @return an {@link InputStream} instance, not linked to any network resources - won't be null
+352 * @throws RundeckApiException in case of error when calling the API (non-existent project with this name)
+353 * @throws RundeckApiLoginException if the login failed
+354 * @throws IllegalArgumentException if the format or project is blank (null, empty or whitespace), or the format is
+355 * invalid
+356 * @see #exportJobs(FileType, String, String, String, String...)
+357 * @see #exportJobsToFile(String, String, String, String, String, String...)
+358 */
+359public InputStream exportJobs(String format, String project, String jobFilter, String groupPath, String... jobIds)
+360throws RundeckApiException, RundeckApiLoginException, IllegalArgumentException {
+361 AssertUtil.notBlank(format, "format is mandatory to export jobs !");
+362return exportJobs(FileType.valueOf(StringUtils.upperCase(format)), project, jobFilter, groupPath, jobIds);
+363 }
+364
+365/**
+366 * Export the definitions of the jobs that belongs to the given project, and matches the given criteria (jobFilter,
+367 * groupPath and jobIds)
+368 *
+369 * @param format of the export. See {@link FileType} - mandatory
+370 * @param project name of the project - mandatory
+371 * @param jobFilter a filter for the job Name - optional
+372 * @param groupPath a group or partial group path to include all jobs within that group path - optional
+373 * @param jobIds a list of Job IDs to include - optional
+374 * @return an {@link InputStream} instance, not linked to any network resources - won't be null
+375 * @throws RundeckApiException in case of error when calling the API (non-existent project with this name)
+376 * @throws RundeckApiLoginException if the login failed
+377 * @throws IllegalArgumentException if the project is blank (null, empty or whitespace) or the format is null
+378 * @see #exportJobsToFile(String, FileType, String, String, String, String...)
+379 */
+380public InputStream exportJobs(FileType format, String project, String jobFilter, String groupPath, String... jobIds)
+381throws RundeckApiException, RundeckApiLoginException, IllegalArgumentException {
+382 AssertUtil.notNull(format, "format is mandatory to export jobs !");
+383 AssertUtil.notBlank(project, "project is mandatory to export jobs !");
+384returnnewApiCall(this).get(newApiPathBuilder("/jobs/export").param("format", format)
+385 .param("project", project)
+386 .param("jobFilter", jobFilter)
+387 .param("groupPath", groupPath)
+388 .param("idlist", StringUtils.join(jobIds, ",")));
+389 }
+390
+391/**
+392 * Export the definition of a single job (identified by the given ID)
+393 *
+394 * @param filename path of the file where the content should be saved - mandatory
+395 * @param format of the export. See {@link FileType} - mandatory
+396 * @param jobId identifier of the job - mandatory
+397 * @throws RundeckApiException in case of error when calling the API (non-existent job with this ID)
+398 * @throws RundeckApiLoginException if the login failed
+399 * @throws IllegalArgumentException if the filename, format or jobId is blank (null, empty or whitespace), or the
+400 * format is invalid
+401 * @throws IOException if we failed to write to the file
+402 * @see #exportJobToFile(String, FileType, String)
+403 * @see #exportJob(String, String)
+404 * @see #getJob(String)
+405 */
+406publicvoid exportJobToFile(String filename, String format, String jobId) throws RundeckApiException,
+407 RundeckApiLoginException, IllegalArgumentException, IOException {
+408 AssertUtil.notBlank(format, "format is mandatory to export a job !");
+409 exportJobToFile(filename, FileType.valueOf(StringUtils.upperCase(format)), jobId);
+410 }
+411
+412/**
+413 * Export the definition of a single job (identified by the given ID)
+414 *
+415 * @param filename path of the file where the content should be saved - mandatory
+416 * @param format of the export. See {@link FileType} - mandatory
+417 * @param jobId identifier of the job - mandatory
+418 * @throws RundeckApiException in case of error when calling the API (non-existent job with this ID)
+419 * @throws RundeckApiLoginException if the login failed
+420 * @throws IllegalArgumentException if the filename or jobId is blank (null, empty or whitespace), or the format is
+421 * null
+422 * @throws IOException if we failed to write to the file
+423 * @see #exportJob(FileType, String)
+424 * @see #getJob(String)
+425 */
+426publicvoid exportJobToFile(String filename, FileType format, String jobId) throws RundeckApiException,
+427 RundeckApiLoginException, IllegalArgumentException, IOException {
+428 AssertUtil.notBlank(filename, "filename is mandatory to export a job !");
+429 InputStream inputStream = exportJob(format, jobId);
+430 FileUtils.writeByteArrayToFile(new File(filename), IOUtils.toByteArray(inputStream));
+431 }
+432
+433/**
+434 * Export the definition of a single job, identified by the given ID435 *
-436 * @param jobId identifier of the job - mandatory
-437 * @return a {@link RundeckExecution} instance for the (finished/aborted) execution - won't be null
-438 * @throws RundeckApiException in case of error when calling the API (non-existent job with this ID)
-439 * @throws RundeckApiLoginException if the login failed
-440 * @throws IllegalArgumentException if the jobId is blank (null, empty or whitespace)
-441 * @see #triggerJob(String)
-442 * @see #runJob(String, Properties, Properties, long, TimeUnit)
-443 */
-444publicRundeckExecution runJob(String jobId) throws RundeckApiException, RundeckApiLoginException,
-445 IllegalArgumentException {
-446return runJob(jobId, null);
-447 }
-448
-449/**
-450 * Run a RunDeck job (identified by the given ID), and wait until its execution is finished (or aborted) to return.
-451 * We will poll the RunDeck server at regular interval (every 5 seconds) to know if the execution is finished (or
-452 * aborted) or is still running.
-453 *
-454 * @param jobId identifier of the job - mandatory
-455 * @param options of the job - optional. See {@link OptionsBuilder}.
-456 * @return a {@link RundeckExecution} instance for the (finished/aborted) execution - won't be null
-457 * @throws RundeckApiException in case of error when calling the API (non-existent job with this ID)
-458 * @throws RundeckApiLoginException if the login failed
-459 * @throws IllegalArgumentException if the jobId is blank (null, empty or whitespace)
-460 * @see #triggerJob(String, Properties)
-461 * @see #runJob(String, Properties, Properties, long, TimeUnit)
-462 */
-463publicRundeckExecution runJob(String jobId, Properties options) throws RundeckApiException,
-464 RundeckApiLoginException, IllegalArgumentException {
-465return runJob(jobId, options, null);
-466 }
-467
-468/**
-469 * Run a RunDeck job (identified by the given ID), and wait until its execution is finished (or aborted) to return.
-470 * We will poll the RunDeck server at regular interval (every 5 seconds) to know if the execution is finished (or
-471 * aborted) or is still running.
-472 *
-473 * @param jobId identifier of the job - mandatory
-474 * @param options of the job - optional. See {@link OptionsBuilder}.
-475 * @param nodeFilters for overriding the nodes on which the job will be executed - optional. See
-476 * {@link NodeFiltersBuilder}
-477 * @return a {@link RundeckExecution} instance for the (finished/aborted) execution - won't be null
-478 * @throws RundeckApiException in case of error when calling the API (non-existent job with this ID)
-479 * @throws RundeckApiLoginException if the login failed
-480 * @throws IllegalArgumentException if the jobId is blank (null, empty or whitespace)
-481 * @see #triggerJob(String, Properties, Properties)
-482 * @see #runJob(String, Properties, Properties, long, TimeUnit)
-483 */
-484publicRundeckExecution runJob(String jobId, Properties options, Properties nodeFilters)
-485throws RundeckApiException, RundeckApiLoginException, IllegalArgumentException {
-486return runJob(jobId, options, nodeFilters, 5, TimeUnit.SECONDS);
-487 }
-488
-489/**
-490 * Run a RunDeck job (identified by the given ID), and wait until its execution is finished (or aborted) to return.
-491 * We will poll the RunDeck server at regular interval (configured by the poolingInterval/poolingUnit couple) to
-492 * know if the execution is finished (or aborted) or is still running.
+436 * @param format of the export. See {@link FileType} - mandatory
+437 * @param jobId identifier of the job - mandatory
+438 * @return an {@link InputStream} instance, not linked to any network resources - won't be null
+439 * @throws RundeckApiException in case of error when calling the API (non-existent job with this ID)
+440 * @throws RundeckApiLoginException if the login failed
+441 * @throws IllegalArgumentException if the format or jobId is blank (null, empty or whitespace), or the format is
+442 * invalid
+443 * @see #exportJobToFile(String, String, String)
+444 * @see #getJob(String)
+445 */
+446public InputStream exportJob(String format, String jobId) throws RundeckApiException, RundeckApiLoginException,
+447 IllegalArgumentException {
+448 AssertUtil.notBlank(format, "format is mandatory to export a job !");
+449return exportJob(FileType.valueOf(StringUtils.upperCase(format)), jobId);
+450 }
+451
+452/**
+453 * Export the definition of a single job, identified by the given ID
+454 *
+455 * @param format of the export. See {@link FileType} - mandatory
+456 * @param jobId identifier of the job - mandatory
+457 * @return an {@link InputStream} instance, not linked to any network resources - won't be null
+458 * @throws RundeckApiException in case of error when calling the API (non-existent job with this ID)
+459 * @throws RundeckApiLoginException if the login failed
+460 * @throws IllegalArgumentException if the jobId is blank (null, empty or whitespace), or the format is null
+461 * @see #exportJobToFile(String, FileType, String)
+462 * @see #getJob(String)
+463 */
+464public InputStream exportJob(FileType format, String jobId) throws RundeckApiException, RundeckApiLoginException,
+465 IllegalArgumentException {
+466 AssertUtil.notNull(format, "format is mandatory to export a job !");
+467 AssertUtil.notBlank(jobId, "jobId is mandatory to export a job !");
+468returnnewApiCall(this).get(newApiPathBuilder("/job/", jobId).param("format", format));
+469 }
+470
+471/**
+472 * Import the definitions of jobs, from the given file
+473 *
+474 * @param filename of the file containing the jobs definitions - mandatory
+475 * @param fileType type of the file. See {@link FileType} - mandatory
+476 * @return a {@link RundeckJobsImportResult} instance - won't be null
+477 * @throws RundeckApiException in case of error when calling the API
+478 * @throws RundeckApiLoginException if the login failed
+479 * @throws IllegalArgumentException if the filename or fileType is blank (null, empty or whitespace), or the
+480 * fileType is invalid
+481 * @throws IOException if we failed to read the file
+482 * @see #importJobs(InputStream, String)
+483 * @see #importJobs(String, FileType, RundeckJobsImportMethod)
+484 */
+485publicRundeckJobsImportResult importJobs(String filename, String fileType) throws RundeckApiException,
+486 RundeckApiLoginException, IllegalArgumentException, IOException {
+487 AssertUtil.notBlank(fileType, "fileType is mandatory to import jobs !");
+488return importJobs(filename, FileType.valueOf(StringUtils.upperCase(fileType)));
+489 }
+490
+491/**
+492 * Import the definitions of jobs, from the given file493 *
-494 * @param jobId identifier of the job - mandatory
-495 * @param options of the job - optional. See {@link OptionsBuilder}.
-496 * @param poolingInterval for checking the status of the execution. Must be > 0.
-497 * @param poolingUnit unit (seconds, milli-seconds, ...) of the interval. Default to seconds.
-498 * @return a {@link RundeckExecution} instance for the (finished/aborted) execution - won't be null
-499 * @throws RundeckApiException in case of error when calling the API (non-existent job with this ID)
-500 * @throws RundeckApiLoginException if the login failed
-501 * @throws IllegalArgumentException if the jobId is blank (null, empty or whitespace)
-502 * @see #triggerJob(String, Properties)
-503 * @see #runJob(String, Properties, Properties, long, TimeUnit)
-504 */
-505publicRundeckExecution runJob(String jobId, Properties options, long poolingInterval, TimeUnit poolingUnit)
-506throws RundeckApiException, RundeckApiLoginException, IllegalArgumentException {
-507return runJob(jobId, options, null, poolingInterval, poolingUnit);
-508 }
-509
-510/**
-511 * Run a RunDeck job (identified by the given ID), and wait until its execution is finished (or aborted) to return.
-512 * We will poll the RunDeck server at regular interval (configured by the poolingInterval/poolingUnit couple) to
-513 * know if the execution is finished (or aborted) or is still running.
-514 *
-515 * @param jobId identifier of the job - mandatory
-516 * @param options of the job - optional. See {@link OptionsBuilder}.
-517 * @param nodeFilters for overriding the nodes on which the job will be executed - optional. See
-518 * {@link NodeFiltersBuilder}
-519 * @param poolingInterval for checking the status of the execution. Must be > 0.
-520 * @param poolingUnit unit (seconds, milli-seconds, ...) of the interval. Default to seconds.
-521 * @return a {@link RundeckExecution} instance for the (finished/aborted) execution - won't be null
-522 * @throws RundeckApiException in case of error when calling the API (non-existent job with this ID)
-523 * @throws RundeckApiLoginException if the login failed
-524 * @throws IllegalArgumentException if the jobId is blank (null, empty or whitespace)
-525 * @see #triggerJob(String, Properties)
-526 * @see #runJob(String, Properties, Properties, long, TimeUnit)
-527 */
-528publicRundeckExecution runJob(String jobId, Properties options, Properties nodeFilters, long poolingInterval,
-529 TimeUnit poolingUnit) throws RundeckApiException, RundeckApiLoginException, IllegalArgumentException {
-530if (poolingInterval <= 0) {
-531 poolingInterval = 5;
-532 poolingUnit = TimeUnit.SECONDS;
-533 }
-534if (poolingUnit == null) {
-535 poolingUnit = TimeUnit.SECONDS;
-536 }
-537
-538RundeckExecution execution = triggerJob(jobId, options, nodeFilters);
-539while (ExecutionStatus.RUNNING.equals(execution.getStatus())) {
-540try {
-541 Thread.sleep(poolingUnit.toMillis(poolingInterval));
-542 } catch (InterruptedException e) {
-543break;
-544 }
-545 execution = getExecution(execution.getId());
-546 }
-547return execution;
-548 }
-549
-550/*
-551 * Ad-hoc commands
-552 */
-553
-554/**
-555 * Trigger the execution of an ad-hoc command, and return immediately (without waiting the end of the execution).
-556 * The command will not be dispatched to nodes, but be executed on the RunDeck server.
-557 *
-558 * @param project name of the project - mandatory
-559 * @param command to be executed - mandatory
-560 * @return a {@link RundeckExecution} instance for the newly created (and running) execution - won't be null
-561 * @throws RundeckApiException in case of error when calling the API (non-existent project with this name)
-562 * @throws RundeckApiLoginException if the login failed
-563 * @throws IllegalArgumentException if the project or command is blank (null, empty or whitespace)
-564 * @see #triggerAdhocCommand(String, String, Properties, Integer, Boolean)
-565 * @see #runAdhocCommand(String, String)
-566 */
-567publicRundeckExecution triggerAdhocCommand(String project, String command) throws RundeckApiException,
-568 RundeckApiLoginException, IllegalArgumentException {
-569return triggerAdhocCommand(project, command, null);
-570 }
-571
-572/**
-573 * Trigger the execution of an ad-hoc command, and return immediately (without waiting the end of the execution).
-574 * The command will be dispatched to nodes, accordingly to the nodeFilters parameter.
-575 *
-576 * @param project name of the project - mandatory
-577 * @param command to be executed - mandatory
-578 * @param nodeFilters for selecting nodes on which the command will be executed. See {@link NodeFiltersBuilder}
-579 * @return a {@link RundeckExecution} instance for the newly created (and running) execution - won't be null
-580 * @throws RundeckApiException in case of error when calling the API (non-existent project with this name)
-581 * @throws RundeckApiLoginException if the login failed
-582 * @throws IllegalArgumentException if the project or command is blank (null, empty or whitespace)
-583 * @see #triggerAdhocCommand(String, String, Properties, Integer, Boolean)
-584 * @see #runAdhocCommand(String, String, Properties)
-585 */
-586publicRundeckExecution triggerAdhocCommand(String project, String command, Properties nodeFilters)
-587throws RundeckApiException, RundeckApiLoginException, IllegalArgumentException {
-588return triggerAdhocCommand(project, command, nodeFilters, null, null);
-589 }
-590
-591/**
-592 * Trigger the execution of an ad-hoc command, and return immediately (without waiting the end of the execution).
-593 * The command will be dispatched to nodes, accordingly to the nodeFilters parameter.
-594 *
-595 * @param project name of the project - mandatory
-596 * @param command to be executed - mandatory
-597 * @param nodeFilters for selecting nodes on which the command will be executed. See {@link NodeFiltersBuilder}
-598 * @param nodeThreadcount thread count to use (for parallelizing when running on multiple nodes) - optional
-599 * @param nodeKeepgoing if true, continue executing on other nodes even if some fail - optional
-600 * @return a {@link RundeckExecution} instance for the newly created (and running) execution - won't be null
-601 * @throws RundeckApiException in case of error when calling the API (non-existent project with this name)
-602 * @throws RundeckApiLoginException if the login failed
-603 * @throws IllegalArgumentException if the project or command is blank (null, empty or whitespace)
-604 * @see #triggerAdhocCommand(String, String)
-605 * @see #runAdhocCommand(String, String, Properties)
+494 * @param filename of the file containing the jobs definitions - mandatory
+495 * @param fileType type of the file. See {@link FileType} - mandatory
+496 * @return a {@link RundeckJobsImportResult} instance - won't be null
+497 * @throws RundeckApiException in case of error when calling the API
+498 * @throws RundeckApiLoginException if the login failed
+499 * @throws IllegalArgumentException if the filename is blank (null, empty or whitespace), or the fileType is null
+500 * @throws IOException if we failed to read the file
+501 * @see #importJobs(InputStream, FileType)
+502 * @see #importJobs(String, FileType, RundeckJobsImportMethod)
+503 */
+504publicRundeckJobsImportResult importJobs(String filename, FileType fileType) throws RundeckApiException,
+505 RundeckApiLoginException, IllegalArgumentException, IOException {
+506return importJobs(filename, fileType, (RundeckJobsImportMethod) null);
+507 }
+508
+509/**
+510 * Import the definitions of jobs, from the given file, using the given behavior
+511 *
+512 * @param filename of the file containing the jobs definitions - mandatory
+513 * @param fileType type of the file. See {@link FileType} - mandatory
+514 * @param importBehavior see {@link RundeckJobsImportMethod}
+515 * @return a {@link RundeckJobsImportResult} instance - won't be null
+516 * @throws RundeckApiException in case of error when calling the API
+517 * @throws RundeckApiLoginException if the login failed
+518 * @throws IllegalArgumentException if the filename or fileType is blank (null, empty or whitespace), or the
+519 * fileType or behavior is not valid
+520 * @throws IOException if we failed to read the file
+521 * @see #importJobs(InputStream, String, String)
+522 * @see #importJobs(String, FileType, RundeckJobsImportMethod)
+523 */
+524publicRundeckJobsImportResult importJobs(String filename, String fileType, String importBehavior)
+525throws RundeckApiException, RundeckApiLoginException, IllegalArgumentException, IOException {
+526 AssertUtil.notBlank(fileType, "fileType is mandatory to import jobs !");
+527return importJobs(filename,
+528 FileType.valueOf(StringUtils.upperCase(fileType)),
+529 RundeckJobsImportMethod.valueOf(StringUtils.upperCase(importBehavior)));
+530 }
+531
+532/**
+533 * Import the definitions of jobs, from the given file, using the given behavior
+534 *
+535 * @param filename of the file containing the jobs definitions - mandatory
+536 * @param fileType type of the file. See {@link FileType} - mandatory
+537 * @param importBehavior see {@link RundeckJobsImportMethod}
+538 * @return a {@link RundeckJobsImportResult} instance - won't be null
+539 * @throws RundeckApiException in case of error when calling the API
+540 * @throws RundeckApiLoginException if the login failed
+541 * @throws IllegalArgumentException if the filename is blank (null, empty or whitespace), or the fileType is null
+542 * @throws IOException if we failed to read the file
+543 * @see #importJobs(InputStream, FileType, RundeckJobsImportMethod)
+544 */
+545publicRundeckJobsImportResult importJobs(String filename, FileType fileType, RundeckJobsImportMethod importBehavior)
+546throws RundeckApiException, RundeckApiLoginException, IllegalArgumentException, IOException {
+547 AssertUtil.notBlank(filename, "filename (of jobs file) is mandatory to import jobs !");
+548 FileInputStream stream = null;
+549try {
+550 stream = FileUtils.openInputStream(new File(filename));
+551return importJobs(stream, fileType, importBehavior);
+552 } finally {
+553 IOUtils.closeQuietly(stream);
+554 }
+555 }
+556
+557/**
+558 * Import the definitions of jobs, from the given input stream
+559 *
+560 * @param stream inputStream for reading the definitions - mandatory
+561 * @param fileType type of the file. See {@link FileType} - mandatory
+562 * @return a {@link RundeckJobsImportResult} instance - won't be null
+563 * @throws RundeckApiException in case of error when calling the API
+564 * @throws RundeckApiLoginException if the login failed
+565 * @throws IllegalArgumentException if the stream is null, or the fileType is blank (null, empty or whitespace) or
+566 * invalid
+567 * @see #importJobs(String, String)
+568 * @see #importJobs(InputStream, FileType, RundeckJobsImportMethod)
+569 */
+570publicRundeckJobsImportResult importJobs(InputStream stream, String fileType) throws RundeckApiException,
+571 RundeckApiLoginException, IllegalArgumentException {
+572 AssertUtil.notBlank(fileType, "fileType is mandatory to import jobs !");
+573return importJobs(stream, FileType.valueOf(StringUtils.upperCase(fileType)));
+574 }
+575
+576/**
+577 * Import the definitions of jobs, from the given input stream
+578 *
+579 * @param stream inputStream for reading the definitions - mandatory
+580 * @param fileType type of the file. See {@link FileType} - mandatory
+581 * @return a {@link RundeckJobsImportResult} instance - won't be null
+582 * @throws RundeckApiException in case of error when calling the API
+583 * @throws RundeckApiLoginException if the login failed
+584 * @throws IllegalArgumentException if the stream or fileType is null
+585 * @see #importJobs(String, FileType)
+586 * @see #importJobs(InputStream, FileType, RundeckJobsImportMethod)
+587 */
+588publicRundeckJobsImportResult importJobs(InputStream stream, FileType fileType) throws RundeckApiException,
+589 RundeckApiLoginException, IllegalArgumentException {
+590return importJobs(stream, fileType, (RundeckJobsImportMethod) null);
+591 }
+592
+593/**
+594 * Import the definitions of jobs, from the given input stream, using the given behavior
+595 *
+596 * @param stream inputStream for reading the definitions - mandatory
+597 * @param fileType type of the file. See {@link FileType} - mandatory
+598 * @param importBehavior see {@link RundeckJobsImportMethod}
+599 * @return a {@link RundeckJobsImportResult} instance - won't be null
+600 * @throws RundeckApiException in case of error when calling the API
+601 * @throws RundeckApiLoginException if the login failed
+602 * @throws IllegalArgumentException if the stream is null, or the fileType is blank (null, empty or whitespace), or
+603 * the fileType or behavior is not valid
+604 * @see #importJobs(String, String, String)
+605 * @see #importJobs(InputStream, FileType, RundeckJobsImportMethod)606 */
-607publicRundeckExecution triggerAdhocCommand(String project, String command, Properties nodeFilters,
-608 Integer nodeThreadcount, Boolean nodeKeepgoing) throws RundeckApiException, RundeckApiLoginException,
-609 IllegalArgumentException {
-610 AssertUtil.notBlank(project, "project is mandatory to trigger an ad-hoc command !");
-611 AssertUtil.notBlank(command, "command is mandatory to trigger an ad-hoc command !");
-612RundeckExecution execution = newApiCall(this).get(newApiPathBuilder("/run/command").param("project", project)
-613 .param("exec", command)
-614 .param("nodeThreadcount",
-615 nodeThreadcount)
-616 .param("nodeKeepgoing",
-617 nodeKeepgoing)
-618 .nodeFilters(nodeFilters),
-619newExecutionParser("result/execution"));
-620// the first call just returns the ID of the execution, so we need another call to get a "real" execution
-621return getExecution(execution.getId());
-622 }
-623
-624/**
-625 * Run an ad-hoc command, and wait until its execution is finished (or aborted) to return. We will poll the RunDeck
-626 * server at regular interval (every 5 seconds) to know if the execution is finished (or aborted) or is still
-627 * running. The command will not be dispatched to nodes, but be executed on the RunDeck server.
-628 *
-629 * @param project name of the project - mandatory
-630 * @param command to be executed - mandatory
-631 * @return a {@link RundeckExecution} instance for the (finished/aborted) execution - won't be null
-632 * @throws RundeckApiException in case of error when calling the API (non-existent project with this name)
-633 * @throws RundeckApiLoginException if the login failed
-634 * @throws IllegalArgumentException if the project or command is blank (null, empty or whitespace)
-635 * @see #runAdhocCommand(String, String, Properties, Integer, Boolean, long, TimeUnit)
-636 * @see #triggerAdhocCommand(String, String)
-637 */
-638publicRundeckExecution runAdhocCommand(String project, String command) throws RundeckApiException,
-639 RundeckApiLoginException, IllegalArgumentException {
-640return runAdhocCommand(project, command, null);
-641 }
-642
-643/**
-644 * Run an ad-hoc command, and wait until its execution is finished (or aborted) to return. We will poll the RunDeck
-645 * server at regular interval (configured by the poolingInterval/poolingUnit couple) to know if the execution is
-646 * finished (or aborted) or is still running. The command will not be dispatched to nodes, but be executed on the
-647 * RunDeck server.
-648 *
-649 * @param project name of the project - mandatory
-650 * @param command to be executed - mandatory
-651 * @param poolingInterval for checking the status of the execution. Must be > 0.
-652 * @param poolingUnit unit (seconds, milli-seconds, ...) of the interval. Default to seconds.
-653 * @return a {@link RundeckExecution} instance for the (finished/aborted) execution - won't be null
-654 * @throws RundeckApiException in case of error when calling the API (non-existent project with this name)
-655 * @throws RundeckApiLoginException if the login failed
-656 * @throws IllegalArgumentException if the project or command is blank (null, empty or whitespace)
-657 * @see #runAdhocCommand(String, String, Properties, Integer, Boolean, long, TimeUnit)
-658 * @see #triggerAdhocCommand(String, String)
-659 */
-660publicRundeckExecution runAdhocCommand(String project, String command, long poolingInterval, TimeUnit poolingUnit)
-661throws RundeckApiException, RundeckApiLoginException, IllegalArgumentException {
-662return runAdhocCommand(project, command, null, poolingInterval, poolingUnit);
-663 }
-664
-665/**
-666 * Run an ad-hoc command, and wait until its execution is finished (or aborted) to return. We will poll the RunDeck
-667 * server at regular interval (every 5 seconds) to know if the execution is finished (or aborted) or is still
-668 * running. The command will be dispatched to nodes, accordingly to the nodeFilters parameter.
-669 *
-670 * @param project name of the project - mandatory
-671 * @param command to be executed - mandatory
-672 * @param nodeFilters for selecting nodes on which the command will be executed. See {@link NodeFiltersBuilder}
-673 * @return a {@link RundeckExecution} instance for the (finished/aborted) execution - won't be null
-674 * @throws RundeckApiException in case of error when calling the API (non-existent project with this name)
-675 * @throws RundeckApiLoginException if the login failed
-676 * @throws IllegalArgumentException if the project or command is blank (null, empty or whitespace)
-677 * @see #runAdhocCommand(String, String, Properties, Integer, Boolean, long, TimeUnit)
-678 * @see #triggerAdhocCommand(String, String, Properties)
-679 */
-680publicRundeckExecution runAdhocCommand(String project, String command, Properties nodeFilters)
-681throws RundeckApiException, RundeckApiLoginException, IllegalArgumentException {
-682return runAdhocCommand(project, command, nodeFilters, null, null);
-683 }
-684
-685/**
-686 * Run an ad-hoc command, and wait until its execution is finished (or aborted) to return. We will poll the RunDeck
-687 * server at regular interval (configured by the poolingInterval/poolingUnit couple) to know if the execution is
-688 * finished (or aborted) or is still running. The command will be dispatched to nodes, accordingly to the
-689 * nodeFilters parameter.
-690 *
-691 * @param project name of the project - mandatory
-692 * @param command to be executed - mandatory
-693 * @param nodeFilters for selecting nodes on which the command will be executed. See {@link NodeFiltersBuilder}
-694 * @param poolingInterval for checking the status of the execution. Must be > 0.
-695 * @param poolingUnit unit (seconds, milli-seconds, ...) of the interval. Default to seconds.
-696 * @return a {@link RundeckExecution} instance for the (finished/aborted) execution - won't be null
-697 * @throws RundeckApiException in case of error when calling the API (non-existent project with this name)
+607publicRundeckJobsImportResult importJobs(InputStream stream, String fileType, String importBehavior)
+608throws RundeckApiException, RundeckApiLoginException, IllegalArgumentException {
+609 AssertUtil.notBlank(fileType, "fileType is mandatory to import jobs !");
+610return importJobs(stream,
+611 FileType.valueOf(StringUtils.upperCase(fileType)),
+612 RundeckJobsImportMethod.valueOf(StringUtils.upperCase(importBehavior)));
+613 }
+614
+615/**
+616 * Import the definitions of jobs, from the given input stream, using the given behavior
+617 *
+618 * @param stream inputStream for reading the definitions - mandatory
+619 * @param fileType type of the file. See {@link FileType} - mandatory
+620 * @param importBehavior see {@link RundeckJobsImportMethod}
+621 * @return a {@link RundeckJobsImportResult} instance - won't be null
+622 * @throws RundeckApiException in case of error when calling the API
+623 * @throws RundeckApiLoginException if the login failed
+624 * @throws IllegalArgumentException if the stream or fileType is null
+625 * @see #importJobs(String, FileType, RundeckJobsImportMethod)
+626 */
+627publicRundeckJobsImportResult importJobs(InputStream stream, FileType fileType,
+628RundeckJobsImportMethod importBehavior) throws RundeckApiException, RundeckApiLoginException,
+629 IllegalArgumentException {
+630 AssertUtil.notNull(stream, "inputStream of jobs is mandatory to import jobs !");
+631 AssertUtil.notNull(fileType, "fileType is mandatory to import jobs !");
+632returnnewApiCall(this).post(newApiPathBuilder("/jobs/import").param("format", fileType)
+633 .param("dupeOption", importBehavior)
+634 .attach("xmlBatch", stream),
+635newJobsImportResultParser("result"));
+636 }
+637
+638/**
+639 * Find a job, identified by its project, group and name. Note that the groupPath is optional, as a job does not
+640 * need to belong to a group (either pass null, or an empty string).
+641 *
+642 * @param project name of the project - mandatory
+643 * @param groupPath group to which the job belongs (if it belongs to a group) - optional
+644 * @param name of the job to find - mandatory
+645 * @return a {@link RundeckJob} instance - null if not found
+646 * @throws RundeckApiException in case of error when calling the API (non-existent project with this name)
+647 * @throws RundeckApiLoginException if the login failed
+648 * @throws IllegalArgumentException if the project or the name is blank (null, empty or whitespace)
+649 * @see #getJob(String)
+650 */
+651publicRundeckJob findJob(String project, String groupPath, String name) throws RundeckApiException,
+652 RundeckApiLoginException, IllegalArgumentException {
+653 AssertUtil.notBlank(project, "project is mandatory to find a job !");
+654 AssertUtil.notBlank(name, "job name is mandatory to find a job !");
+655 List<RundeckJob> jobs = getJobs(project, name, groupPath, new String[0]);
+656return jobs.isEmpty() ? null : jobs.get(0);
+657 }
+658
+659/**
+660 * Get the definition of a single job, identified by the given ID
+661 *
+662 * @param jobId identifier of the job - mandatory
+663 * @return a {@link RundeckJob} instance - won't be null
+664 * @throws RundeckApiException in case of error when calling the API (non-existent job with this ID)
+665 * @throws RundeckApiLoginException if the login failed
+666 * @throws IllegalArgumentException if the jobId is blank (null, empty or whitespace)
+667 * @see #findJob(String, String, String)
+668 * @see #exportJob(String)
+669 */
+670publicRundeckJob getJob(String jobId) throws RundeckApiException, RundeckApiLoginException,
+671 IllegalArgumentException {
+672 AssertUtil.notBlank(jobId, "jobId is mandatory to get the details of a job !");
+673returnnewApiCall(this).get(newApiPathBuilder("/job/", jobId), newJobParser("joblist/job"));
+674 }
+675
+676/**
+677 * Delete a single job, identified by the given ID
+678 *
+679 * @param jobId identifier of the job - mandatory
+680 * @return the success message (note that in case of error, you'll get an exception)
+681 * @throws RundeckApiException in case of error when calling the API (non-existent job with this ID)
+682 * @throws RundeckApiLoginException if the login failed
+683 * @throws IllegalArgumentException if the jobId is blank (null, empty or whitespace)
+684 */
+685public String deleteJob(String jobId) throws RundeckApiException, RundeckApiLoginException,
+686 IllegalArgumentException {
+687 AssertUtil.notBlank(jobId, "jobId is mandatory to delete a job !");
+688returnnewApiCall(this).delete(newApiPathBuilder("/job/", jobId), newStringParser("result/success/message"));
+689 }
+690
+691/**
+692 * Trigger the execution of a RunDeck job (identified by the given ID), and return immediately (without waiting the
+693 * end of the job execution)
+694 *
+695 * @param jobId identifier of the job - mandatory
+696 * @return a {@link RundeckExecution} instance for the newly created (and running) execution - won't be null
+697 * @throws RundeckApiException in case of error when calling the API (non-existent job with this ID)698 * @throws RundeckApiLoginException if the login failed
-699 * @throws IllegalArgumentException if the project or command is blank (null, empty or whitespace)
-700 * @see #runAdhocCommand(String, String, Properties, Integer, Boolean, long, TimeUnit)
-701 * @see #triggerAdhocCommand(String, String, Properties)
+699 * @throws IllegalArgumentException if the jobId is blank (null, empty or whitespace)
+700 * @see #triggerJob(String, Properties, Properties)
+701 * @see #runJob(String)702 */
-703publicRundeckExecution runAdhocCommand(String project, String command, Properties nodeFilters,
-704long poolingInterval, TimeUnit poolingUnit) throws RundeckApiException, RundeckApiLoginException,
-705 IllegalArgumentException {
-706return runAdhocCommand(project, command, nodeFilters, null, null, poolingInterval, poolingUnit);
-707 }
-708
-709/**
-710 * Run an ad-hoc command, and wait until its execution is finished (or aborted) to return. We will poll the RunDeck
-711 * server at regular interval (every 5 seconds) to know if the execution is finished (or aborted) or is still
-712 * running. The command will be dispatched to nodes, accordingly to the nodeFilters parameter.
-713 *
-714 * @param project name of the project - mandatory
-715 * @param command to be executed - mandatory
-716 * @param nodeFilters for selecting nodes on which the command will be executed. See {@link NodeFiltersBuilder}
-717 * @param nodeThreadcount thread count to use (for parallelizing when running on multiple nodes) - optional
-718 * @param nodeKeepgoing if true, continue executing on other nodes even if some fail - optional
-719 * @return a {@link RundeckExecution} instance for the (finished/aborted) execution - won't be null
-720 * @throws RundeckApiException in case of error when calling the API (non-existent project with this name)
-721 * @throws RundeckApiLoginException if the login failed
-722 * @throws IllegalArgumentException if the project or command is blank (null, empty or whitespace)
-723 * @see #runAdhocCommand(String, String, Properties, Integer, Boolean, long, TimeUnit)
-724 * @see #triggerAdhocCommand(String, String, Properties, Integer, Boolean)
-725 */
-726publicRundeckExecution runAdhocCommand(String project, String command, Properties nodeFilters,
-727 Integer nodeThreadcount, Boolean nodeKeepgoing) throws RundeckApiException, RundeckApiLoginException,
-728 IllegalArgumentException {
-729return runAdhocCommand(project, command, nodeFilters, nodeThreadcount, nodeKeepgoing, 5, TimeUnit.SECONDS);
-730 }
-731
-732/**
-733 * Run an ad-hoc command, and wait until its execution is finished (or aborted) to return. We will poll the RunDeck
-734 * server at regular interval (configured by the poolingInterval/poolingUnit couple) to know if the execution is
-735 * finished (or aborted) or is still running. The command will be dispatched to nodes, accordingly to the
-736 * nodeFilters parameter.
-737 *
-738 * @param project name of the project - mandatory
-739 * @param command to be executed - mandatory
-740 * @param nodeFilters for selecting nodes on which the command will be executed. See {@link NodeFiltersBuilder}
-741 * @param nodeThreadcount thread count to use (for parallelizing when running on multiple nodes) - optional
-742 * @param nodeKeepgoing if true, continue executing on other nodes even if some fail - optional
-743 * @param poolingInterval for checking the status of the execution. Must be > 0.
-744 * @param poolingUnit unit (seconds, milli-seconds, ...) of the interval. Default to seconds.
-745 * @return a {@link RundeckExecution} instance for the (finished/aborted) execution - won't be null
-746 * @throws RundeckApiException in case of error when calling the API (non-existent project with this name)
-747 * @throws RundeckApiLoginException if the login failed
-748 * @throws IllegalArgumentException if the project or command is blank (null, empty or whitespace)
-749 * @see #triggerAdhocCommand(String, String, Properties, Integer, Boolean)
-750 */
-751publicRundeckExecution runAdhocCommand(String project, String command, Properties nodeFilters,
-752 Integer nodeThreadcount, Boolean nodeKeepgoing, long poolingInterval, TimeUnit poolingUnit)
-753throws RundeckApiException, RundeckApiLoginException, IllegalArgumentException {
-754if (poolingInterval <= 0) {
-755 poolingInterval = 5;
-756 poolingUnit = TimeUnit.SECONDS;
-757 }
-758if (poolingUnit == null) {
-759 poolingUnit = TimeUnit.SECONDS;
-760 }
-761
-762RundeckExecution execution = triggerAdhocCommand(project, command, nodeFilters, nodeThreadcount, nodeKeepgoing);
-763while (ExecutionStatus.RUNNING.equals(execution.getStatus())) {
-764try {
-765 Thread.sleep(poolingUnit.toMillis(poolingInterval));
-766 } catch (InterruptedException e) {
-767break;
-768 }
-769 execution = getExecution(execution.getId());
-770 }
-771return execution;
-772 }
-773
-774/*
-775 * Executions
-776 */
-777
-778/**
-779 * Get all running executions (for all projects)
-780 *
-781 * @return a {@link List} of {@link RundeckExecution} : might be empty, but won't be null
-782 * @throws RundeckApiException in case of error when calling the API
-783 * @throws RundeckApiLoginException if the login failed
-784 */
-785public List<RundeckExecution> getRunningExecutions() throws RundeckApiException, RundeckApiLoginException {
-786 List<RundeckExecution> executions = new ArrayList<RundeckExecution>();
-787for (RundeckProject project : getProjects()) {
-788 executions.addAll(getRunningExecutions(project.getName()));
-789 }
-790return executions;
-791 }
-792
-793/**
-794 * Get the running executions for the given project
-795 *
-796 * @param project name of the project - mandatory
-797 * @return a {@link List} of {@link RundeckExecution} : might be empty, but won't be null
-798 * @throws RundeckApiException in case of error when calling the API (non-existent project with this name)
-799 * @throws RundeckApiLoginException if the login failed
-800 * @throws IllegalArgumentException if the project is blank (null, empty or whitespace)
-801 */
-802public List<RundeckExecution> getRunningExecutions(String project) throws RundeckApiException,
-803 RundeckApiLoginException, IllegalArgumentException {
-804 AssertUtil.notBlank(project, "project is mandatory get all running executions !");
-805returnnewApiCall(this).get(newApiPathBuilder("/executions/running").param("project", project),
-806new ListParser<RundeckExecution>(newExecutionParser(),
-807"result/executions/execution"));
-808 }
-809
-810/**
-811 * Get the executions of the given job
+703publicRundeckExecution triggerJob(String jobId) throws RundeckApiException, RundeckApiLoginException,
+704 IllegalArgumentException {
+705return triggerJob(jobId, null);
+706 }
+707
+708/**
+709 * Trigger the execution of a RunDeck job (identified by the given ID), and return immediately (without waiting the
+710 * end of the job execution)
+711 *
+712 * @param jobId identifier of the job - mandatory
+713 * @param options of the job - optional. See {@link OptionsBuilder}.
+714 * @return a {@link RundeckExecution} instance for the newly created (and running) execution - won't be null
+715 * @throws RundeckApiException in case of error when calling the API (non-existent job with this ID)
+716 * @throws RundeckApiLoginException if the login failed
+717 * @throws IllegalArgumentException if the jobId is blank (null, empty or whitespace)
+718 * @see #triggerJob(String, Properties, Properties)
+719 * @see #runJob(String, Properties)
+720 */
+721publicRundeckExecution triggerJob(String jobId, Properties options) throws RundeckApiException,
+722 RundeckApiLoginException, IllegalArgumentException {
+723return triggerJob(jobId, options, null);
+724 }
+725
+726/**
+727 * Trigger the execution of a RunDeck job (identified by the given ID), and return immediately (without waiting the
+728 * end of the job execution)
+729 *
+730 * @param jobId identifier of the job - mandatory
+731 * @param options of the job - optional. See {@link OptionsBuilder}.
+732 * @param nodeFilters for overriding the nodes on which the job will be executed - optional. See
+733 * {@link NodeFiltersBuilder}
+734 * @return a {@link RundeckExecution} instance for the newly created (and running) execution - won't be null
+735 * @throws RundeckApiException in case of error when calling the API (non-existent job with this ID)
+736 * @throws RundeckApiLoginException if the login failed
+737 * @throws IllegalArgumentException if the jobId is blank (null, empty or whitespace)
+738 * @see #triggerJob(String)
+739 * @see #runJob(String, Properties, Properties)
+740 */
+741publicRundeckExecution triggerJob(String jobId, Properties options, Properties nodeFilters)
+742throws RundeckApiException, RundeckApiLoginException, IllegalArgumentException {
+743 AssertUtil.notBlank(jobId, "jobId is mandatory to trigger a job !");
+744returnnewApiCall(this).get(newApiPathBuilder("/job/", jobId, "/run").param("argString",
+745 ParametersUtil.generateArgString(options))
+746 .nodeFilters(nodeFilters),
+747newExecutionParser("result/executions/execution"));
+748 }
+749
+750/**
+751 * Run a RunDeck job (identified by the given ID), and wait until its execution is finished (or aborted) to return.
+752 * We will poll the RunDeck server at regular interval (every 5 seconds) to know if the execution is finished (or
+753 * aborted) or is still running.
+754 *
+755 * @param jobId identifier of the job - mandatory
+756 * @return a {@link RundeckExecution} instance for the (finished/aborted) execution - won't be null
+757 * @throws RundeckApiException in case of error when calling the API (non-existent job with this ID)
+758 * @throws RundeckApiLoginException if the login failed
+759 * @throws IllegalArgumentException if the jobId is blank (null, empty or whitespace)
+760 * @see #triggerJob(String)
+761 * @see #runJob(String, Properties, Properties, long, TimeUnit)
+762 */
+763publicRundeckExecution runJob(String jobId) throws RundeckApiException, RundeckApiLoginException,
+764 IllegalArgumentException {
+765return runJob(jobId, null);
+766 }
+767
+768/**
+769 * Run a RunDeck job (identified by the given ID), and wait until its execution is finished (or aborted) to return.
+770 * We will poll the RunDeck server at regular interval (every 5 seconds) to know if the execution is finished (or
+771 * aborted) or is still running.
+772 *
+773 * @param jobId identifier of the job - mandatory
+774 * @param options of the job - optional. See {@link OptionsBuilder}.
+775 * @return a {@link RundeckExecution} instance for the (finished/aborted) execution - won't be null
+776 * @throws RundeckApiException in case of error when calling the API (non-existent job with this ID)
+777 * @throws RundeckApiLoginException if the login failed
+778 * @throws IllegalArgumentException if the jobId is blank (null, empty or whitespace)
+779 * @see #triggerJob(String, Properties)
+780 * @see #runJob(String, Properties, Properties, long, TimeUnit)
+781 */
+782publicRundeckExecution runJob(String jobId, Properties options) throws RundeckApiException,
+783 RundeckApiLoginException, IllegalArgumentException {
+784return runJob(jobId, options, null);
+785 }
+786
+787/**
+788 * Run a RunDeck job (identified by the given ID), and wait until its execution is finished (or aborted) to return.
+789 * We will poll the RunDeck server at regular interval (every 5 seconds) to know if the execution is finished (or
+790 * aborted) or is still running.
+791 *
+792 * @param jobId identifier of the job - mandatory
+793 * @param options of the job - optional. See {@link OptionsBuilder}.
+794 * @param nodeFilters for overriding the nodes on which the job will be executed - optional. See
+795 * {@link NodeFiltersBuilder}
+796 * @return a {@link RundeckExecution} instance for the (finished/aborted) execution - won't be null
+797 * @throws RundeckApiException in case of error when calling the API (non-existent job with this ID)
+798 * @throws RundeckApiLoginException if the login failed
+799 * @throws IllegalArgumentException if the jobId is blank (null, empty or whitespace)
+800 * @see #triggerJob(String, Properties, Properties)
+801 * @see #runJob(String, Properties, Properties, long, TimeUnit)
+802 */
+803publicRundeckExecution runJob(String jobId, Properties options, Properties nodeFilters)
+804throws RundeckApiException, RundeckApiLoginException, IllegalArgumentException {
+805return runJob(jobId, options, nodeFilters, 5, TimeUnit.SECONDS);
+806 }
+807
+808/**
+809 * Run a RunDeck job (identified by the given ID), and wait until its execution is finished (or aborted) to return.
+810 * We will poll the RunDeck server at regular interval (configured by the poolingInterval/poolingUnit couple) to
+811 * know if the execution is finished (or aborted) or is still running.812 * 813 * @param jobId identifier of the job - mandatory
-814 * @return a {@link List} of {@link RundeckExecution} : might be empty, but won't be null
-815 * @throws RundeckApiException in case of error when calling the API (non-existent job with this ID)
-816 * @throws RundeckApiLoginException if the login failed
-817 * @throws IllegalArgumentException if the jobId is blank (null, empty or whitespace)
-818 */
-819public List<RundeckExecution> getJobExecutions(String jobId) throws RundeckApiException, RundeckApiLoginException,
-820 IllegalArgumentException {
-821return getJobExecutions(jobId, null);
-822 }
-823
-824/**
-825 * Get the executions of the given job
-826 *
-827 * @param jobId identifier of the job - mandatory
-828 * @param status of the executions - optional (null for all)
-829 * @return a {@link List} of {@link RundeckExecution} : might be empty, but won't be null
-830 * @throws RundeckApiException in case of error when calling the API (non-existent job with this ID)
-831 * @throws RundeckApiLoginException if the login failed
-832 * @throws IllegalArgumentException if the jobId is blank (null, empty or whitespace)
-833 */
-834public List<RundeckExecution> getJobExecutions(String jobId, ExecutionStatus status) throws RundeckApiException,
-835 RundeckApiLoginException, IllegalArgumentException {
-836return getJobExecutions(jobId, status, null, null);
-837 }
-838
-839/**
-840 * Get the executions of the given job
-841 *
-842 * @param jobId identifier of the job - mandatory
-843 * @param status of the executions - optional (null for all)
-844 * @param max number of results to return - optional (null for all)
-845 * @param offset the 0-indexed offset for the first result to return - optional
-846 * @return a {@link List} of {@link RundeckExecution} : might be empty, but won't be null
-847 * @throws RundeckApiException in case of error when calling the API (non-existent job with this ID)
-848 * @throws RundeckApiLoginException if the login failed
-849 * @throws IllegalArgumentException if the jobId is blank (null, empty or whitespace)
-850 */
-851public List<RundeckExecution> getJobExecutions(String jobId, ExecutionStatus status, Long max, Long offset)
-852throws RundeckApiException, RundeckApiLoginException, IllegalArgumentException {
-853 AssertUtil.notBlank(jobId, "jobId is mandatory to get the executions of a job !");
-854returnnewApiCall(this).get(newApiPathBuilder("/job/", jobId, "/executions").param("status",
-855 status != null ? StringUtils.lowerCase(status.toString()) : null)
-856 .param("max", max)
-857 .param("offset", offset),
-858new ListParser<RundeckExecution>(newExecutionParser(),
-859"result/executions/execution"));
-860 }
-861
-862/**
-863 * Get a single execution, identified by the given ID
-864 *
-865 * @param executionId identifier of the execution - mandatory
-866 * @return a {@link RundeckExecution} instance - won't be null
-867 * @throws RundeckApiException in case of error when calling the API (non-existent execution with this ID)
-868 * @throws RundeckApiLoginException if the login failed
-869 * @throws IllegalArgumentException if the executionId is null
-870 */
-871publicRundeckExecution getExecution(Long executionId) throws RundeckApiException, RundeckApiLoginException,
-872 IllegalArgumentException {
-873 AssertUtil.notNull(executionId, "executionId is mandatory to get the details of an execution !");
-874returnnewApiCall(this).get(newApiPathBuilder("/execution/", executionId.toString()),
-875newExecutionParser("result/executions/execution"));
-876 }
-877
-878/**
-879 * Abort an execution (identified by the given ID). The execution should be running...
-880 *
-881 * @param executionId identifier of the execution - mandatory
-882 * @return a {@link RundeckAbort} instance - won't be null
-883 * @throws RundeckApiException in case of error when calling the API (non-existent execution with this ID)
-884 * @throws RundeckApiLoginException if the login failed
-885 * @throws IllegalArgumentException if the executionId is null
-886 */
-887publicRundeckAbort abortExecution(Long executionId) throws RundeckApiException, RundeckApiLoginException,
-888 IllegalArgumentException {
-889 AssertUtil.notNull(executionId, "executionId is mandatory to abort an execution !");
-890returnnewApiCall(this).get(newApiPathBuilder("/execution/", executionId.toString(), "/abort"),
-891newAbortParser("result/abort"));
-892 }
-893
-894/*
-895 * Nodes
-896 */
-897
-898/**
-899 * List all nodes (for all projects)
-900 *
-901 * @return a {@link List} of {@link RundeckNode} : might be empty, but won't be null
-902 * @throws RundeckApiException in case of error when calling the API
-903 * @throws RundeckApiLoginException if the login failed
+814 * @param options of the job - optional. See {@link OptionsBuilder}.
+815 * @param poolingInterval for checking the status of the execution. Must be > 0.
+816 * @param poolingUnit unit (seconds, milli-seconds, ...) of the interval. Default to seconds.
+817 * @return a {@link RundeckExecution} instance for the (finished/aborted) execution - won't be null
+818 * @throws RundeckApiException in case of error when calling the API (non-existent job with this ID)
+819 * @throws RundeckApiLoginException if the login failed
+820 * @throws IllegalArgumentException if the jobId is blank (null, empty or whitespace)
+821 * @see #triggerJob(String, Properties)
+822 * @see #runJob(String, Properties, Properties, long, TimeUnit)
+823 */
+824publicRundeckExecution runJob(String jobId, Properties options, long poolingInterval, TimeUnit poolingUnit)
+825throws RundeckApiException, RundeckApiLoginException, IllegalArgumentException {
+826return runJob(jobId, options, null, poolingInterval, poolingUnit);
+827 }
+828
+829/**
+830 * Run a RunDeck job (identified by the given ID), and wait until its execution is finished (or aborted) to return.
+831 * We will poll the RunDeck server at regular interval (configured by the poolingInterval/poolingUnit couple) to
+832 * know if the execution is finished (or aborted) or is still running.
+833 *
+834 * @param jobId identifier of the job - mandatory
+835 * @param options of the job - optional. See {@link OptionsBuilder}.
+836 * @param nodeFilters for overriding the nodes on which the job will be executed - optional. See
+837 * {@link NodeFiltersBuilder}
+838 * @param poolingInterval for checking the status of the execution. Must be > 0.
+839 * @param poolingUnit unit (seconds, milli-seconds, ...) of the interval. Default to seconds.
+840 * @return a {@link RundeckExecution} instance for the (finished/aborted) execution - won't be null
+841 * @throws RundeckApiException in case of error when calling the API (non-existent job with this ID)
+842 * @throws RundeckApiLoginException if the login failed
+843 * @throws IllegalArgumentException if the jobId is blank (null, empty or whitespace)
+844 * @see #triggerJob(String, Properties)
+845 * @see #runJob(String, Properties, Properties, long, TimeUnit)
+846 */
+847publicRundeckExecution runJob(String jobId, Properties options, Properties nodeFilters, long poolingInterval,
+848 TimeUnit poolingUnit) throws RundeckApiException, RundeckApiLoginException, IllegalArgumentException {
+849if (poolingInterval <= 0) {
+850 poolingInterval = 5;
+851 poolingUnit = TimeUnit.SECONDS;
+852 }
+853if (poolingUnit == null) {
+854 poolingUnit = TimeUnit.SECONDS;
+855 }
+856
+857RundeckExecution execution = triggerJob(jobId, options, nodeFilters);
+858while (ExecutionStatus.RUNNING.equals(execution.getStatus())) {
+859try {
+860 Thread.sleep(poolingUnit.toMillis(poolingInterval));
+861 } catch (InterruptedException e) {
+862break;
+863 }
+864 execution = getExecution(execution.getId());
+865 }
+866return execution;
+867 }
+868
+869/*
+870 * Ad-hoc commands
+871 */
+872
+873/**
+874 * Trigger the execution of an ad-hoc command, and return immediately (without waiting the end of the execution).
+875 * The command will not be dispatched to nodes, but be executed on the RunDeck server.
+876 *
+877 * @param project name of the project - mandatory
+878 * @param command to be executed - mandatory
+879 * @return a {@link RundeckExecution} instance for the newly created (and running) execution - won't be null
+880 * @throws RundeckApiException in case of error when calling the API (non-existent project with this name)
+881 * @throws RundeckApiLoginException if the login failed
+882 * @throws IllegalArgumentException if the project or command is blank (null, empty or whitespace)
+883 * @see #triggerAdhocCommand(String, String, Properties, Integer, Boolean)
+884 * @see #runAdhocCommand(String, String)
+885 */
+886publicRundeckExecution triggerAdhocCommand(String project, String command) throws RundeckApiException,
+887 RundeckApiLoginException, IllegalArgumentException {
+888return triggerAdhocCommand(project, command, null);
+889 }
+890
+891/**
+892 * Trigger the execution of an ad-hoc command, and return immediately (without waiting the end of the execution).
+893 * The command will be dispatched to nodes, accordingly to the nodeFilters parameter.
+894 *
+895 * @param project name of the project - mandatory
+896 * @param command to be executed - mandatory
+897 * @param nodeFilters for selecting nodes on which the command will be executed. See {@link NodeFiltersBuilder}
+898 * @return a {@link RundeckExecution} instance for the newly created (and running) execution - won't be null
+899 * @throws RundeckApiException in case of error when calling the API (non-existent project with this name)
+900 * @throws RundeckApiLoginException if the login failed
+901 * @throws IllegalArgumentException if the project or command is blank (null, empty or whitespace)
+902 * @see #triggerAdhocCommand(String, String, Properties, Integer, Boolean)
+903 * @see #runAdhocCommand(String, String, Properties)904 */
-905public List<RundeckNode> getNodes() throws RundeckApiException, RundeckApiLoginException {
-906 List<RundeckNode> nodes = new ArrayList<RundeckNode>();
-907for (RundeckProject project : getProjects()) {
-908 nodes.addAll(getNodes(project.getName()));
-909 }
-910return nodes;
-911 }
-912
-913/**
-914 * List all nodes that belongs to the given project
-915 *
-916 * @param project name of the project - mandatory
-917 * @return a {@link List} of {@link RundeckNode} : might be empty, but won't be null
-918 * @throws RundeckApiException in case of error when calling the API (non-existent project with this name)
-919 * @throws RundeckApiLoginException if the login failed
-920 * @throws IllegalArgumentException if the project is blank (null, empty or whitespace)
-921 * @see #getNodes(String, Properties)
-922 */
-923public List<RundeckNode> getNodes(String project) throws RundeckApiException, RundeckApiLoginException,
-924 IllegalArgumentException {
-925return getNodes(project, null);
-926 }
-927
-928/**
-929 * List nodes that belongs to the given project
-930 *
-931 * @param project name of the project - mandatory
-932 * @param nodeFilters for filtering the nodes - optional. See {@link NodeFiltersBuilder}
-933 * @return a {@link List} of {@link RundeckNode} : might be empty, but won't be null
-934 * @throws RundeckApiException in case of error when calling the API (non-existent project with this name)
-935 * @throws RundeckApiLoginException if the login failed
-936 * @throws IllegalArgumentException if the project is blank (null, empty or whitespace)
-937 */
-938public List<RundeckNode> getNodes(String project, Properties nodeFilters) throws RundeckApiException,
-939 RundeckApiLoginException, IllegalArgumentException {
-940 AssertUtil.notBlank(project, "project is mandatory to get all nodes !");
-941returnnewApiCall(this).get(newApiPathBuilder("/resources").param("project", project)
-942 .nodeFilters(nodeFilters),
-943new ListParser<RundeckNode>(newNodeParser(), "project/node"));
-944 }
-945
-946/**
-947 * Get the definition of a single node
-948 *
-949 * @param name of the node - mandatory
-950 * @param project name of the project - mandatory
-951 * @return a {@link RundeckNode} instance - won't be null
-952 * @throws RundeckApiException in case of error when calling the API (non-existent name or project with this name)
-953 * @throws RundeckApiLoginException if the login failed
-954 * @throws IllegalArgumentException if the name or project is blank (null, empty or whitespace)
-955 */
-956publicRundeckNode getNode(String name, String project) throws RundeckApiException, RundeckApiLoginException,
-957 IllegalArgumentException {
-958 AssertUtil.notBlank(name, "the name of the node is mandatory to get a node !");
-959 AssertUtil.notBlank(project, "project is mandatory to get a node !");
-960returnnewApiCall(this).get(newApiPathBuilder("/resource/", name).param("project", project),
-961newNodeParser("project/node"));
-962 }
-963
-964/*
-965 * System Info
-966 */
-967
-968/**
-969 * Get system informations about the RunDeck server
-970 *
-971 * @return a {@link RundeckSystemInfo} instance - won't be null
-972 * @throws RundeckApiException in case of error when calling the API
-973 * @throws RundeckApiException if the login failed
-974 */
-975publicRundeckSystemInfo getSystemInfo() throws RundeckApiException, RundeckApiLoginException {
-976returnnewApiCall(this).get(newApiPathBuilder("/system/info"), newSystemInfoParser("result/system"));
-977 }
-978
-979public String getUrl() {
-980return url;
-981 }
-982
-983public String getLogin() {
-984return login;
-985 }
-986
-987public String getPassword() {
-988return password;
-989 }
-990
-991 @Override
-992public String toString() {
-993return"RundeckClient [url=" + url + ", login=" + login + ", password=" + password + "]";
-994 }
-995
-996 @Override
-997publicint hashCode() {
-998finalint prime = 31;
-999int result = 1;
-1000 result = prime * result + ((login == null) ? 0 : login.hashCode());
-1001 result = prime * result + ((password == null) ? 0 : password.hashCode());
-1002 result = prime * result + ((url == null) ? 0 : url.hashCode());
-1003return result;
-1004 }
-1005
-1006 @Override
-1007publicboolean equals(Object obj) {
-1008if (this == obj)
-1009returntrue;
-1010if (obj == null)
-1011return false;
-1012if (getClass() != obj.getClass())
-1013return false;
-1014RundeckClient other = (RundeckClient) obj;
-1015if (login == null) {
-1016if (other.login != null)
-1017return false;
-1018 } elseif (!login.equals(other.login))
-1019return false;
-1020if (password == null) {
-1021if (other.password != null)
-1022return false;
-1023 } elseif (!password.equals(other.password))
-1024return false;
-1025if (url == null) {
-1026if (other.url != null)
-1027return false;
-1028 } elseif (!url.equals(other.url))
-1029return false;
-1030returntrue;
-1031 }
-1032
-1033 }
+905publicRundeckExecution triggerAdhocCommand(String project, String command, Properties nodeFilters)
+906throws RundeckApiException, RundeckApiLoginException, IllegalArgumentException {
+907return triggerAdhocCommand(project, command, nodeFilters, null, null);
+908 }
+909
+910/**
+911 * Trigger the execution of an ad-hoc command, and return immediately (without waiting the end of the execution).
+912 * The command will be dispatched to nodes, accordingly to the nodeFilters parameter.
+913 *
+914 * @param project name of the project - mandatory
+915 * @param command to be executed - mandatory
+916 * @param nodeFilters for selecting nodes on which the command will be executed. See {@link NodeFiltersBuilder}
+917 * @param nodeThreadcount thread count to use (for parallelizing when running on multiple nodes) - optional
+918 * @param nodeKeepgoing if true, continue executing on other nodes even if some fail - optional
+919 * @return a {@link RundeckExecution} instance for the newly created (and running) execution - won't be null
+920 * @throws RundeckApiException in case of error when calling the API (non-existent project with this name)
+921 * @throws RundeckApiLoginException if the login failed
+922 * @throws IllegalArgumentException if the project or command is blank (null, empty or whitespace)
+923 * @see #triggerAdhocCommand(String, String)
+924 * @see #runAdhocCommand(String, String, Properties)
+925 */
+926publicRundeckExecution triggerAdhocCommand(String project, String command, Properties nodeFilters,
+927 Integer nodeThreadcount, Boolean nodeKeepgoing) throws RundeckApiException, RundeckApiLoginException,
+928 IllegalArgumentException {
+929 AssertUtil.notBlank(project, "project is mandatory to trigger an ad-hoc command !");
+930 AssertUtil.notBlank(command, "command is mandatory to trigger an ad-hoc command !");
+931RundeckExecution execution = newApiCall(this).get(newApiPathBuilder("/run/command").param("project", project)
+932 .param("exec", command)
+933 .param("nodeThreadcount",
+934 nodeThreadcount)
+935 .param("nodeKeepgoing",
+936 nodeKeepgoing)
+937 .nodeFilters(nodeFilters),
+938newExecutionParser("result/execution"));
+939// the first call just returns the ID of the execution, so we need another call to get a "real" execution
+940return getExecution(execution.getId());
+941 }
+942
+943/**
+944 * Run an ad-hoc command, and wait until its execution is finished (or aborted) to return. We will poll the RunDeck
+945 * server at regular interval (every 5 seconds) to know if the execution is finished (or aborted) or is still
+946 * running. The command will not be dispatched to nodes, but be executed on the RunDeck server.
+947 *
+948 * @param project name of the project - mandatory
+949 * @param command to be executed - mandatory
+950 * @return a {@link RundeckExecution} instance for the (finished/aborted) execution - won't be null
+951 * @throws RundeckApiException in case of error when calling the API (non-existent project with this name)
+952 * @throws RundeckApiLoginException if the login failed
+953 * @throws IllegalArgumentException if the project or command is blank (null, empty or whitespace)
+954 * @see #runAdhocCommand(String, String, Properties, Integer, Boolean, long, TimeUnit)
+955 * @see #triggerAdhocCommand(String, String)
+956 */
+957publicRundeckExecution runAdhocCommand(String project, String command) throws RundeckApiException,
+958 RundeckApiLoginException, IllegalArgumentException {
+959return runAdhocCommand(project, command, null);
+960 }
+961
+962/**
+963 * Run an ad-hoc command, and wait until its execution is finished (or aborted) to return. We will poll the RunDeck
+964 * server at regular interval (configured by the poolingInterval/poolingUnit couple) to know if the execution is
+965 * finished (or aborted) or is still running. The command will not be dispatched to nodes, but be executed on the
+966 * RunDeck server.
+967 *
+968 * @param project name of the project - mandatory
+969 * @param command to be executed - mandatory
+970 * @param poolingInterval for checking the status of the execution. Must be > 0.
+971 * @param poolingUnit unit (seconds, milli-seconds, ...) of the interval. Default to seconds.
+972 * @return a {@link RundeckExecution} instance for the (finished/aborted) execution - won't be null
+973 * @throws RundeckApiException in case of error when calling the API (non-existent project with this name)
+974 * @throws RundeckApiLoginException if the login failed
+975 * @throws IllegalArgumentException if the project or command is blank (null, empty or whitespace)
+976 * @see #runAdhocCommand(String, String, Properties, Integer, Boolean, long, TimeUnit)
+977 * @see #triggerAdhocCommand(String, String)
+978 */
+979publicRundeckExecution runAdhocCommand(String project, String command, long poolingInterval, TimeUnit poolingUnit)
+980throws RundeckApiException, RundeckApiLoginException, IllegalArgumentException {
+981return runAdhocCommand(project, command, null, poolingInterval, poolingUnit);
+982 }
+983
+984/**
+985 * Run an ad-hoc command, and wait until its execution is finished (or aborted) to return. We will poll the RunDeck
+986 * server at regular interval (every 5 seconds) to know if the execution is finished (or aborted) or is still
+987 * running. The command will be dispatched to nodes, accordingly to the nodeFilters parameter.
+988 *
+989 * @param project name of the project - mandatory
+990 * @param command to be executed - mandatory
+991 * @param nodeFilters for selecting nodes on which the command will be executed. See {@link NodeFiltersBuilder}
+992 * @return a {@link RundeckExecution} instance for the (finished/aborted) execution - won't be null
+993 * @throws RundeckApiException in case of error when calling the API (non-existent project with this name)
+994 * @throws RundeckApiLoginException if the login failed
+995 * @throws IllegalArgumentException if the project or command is blank (null, empty or whitespace)
+996 * @see #runAdhocCommand(String, String, Properties, Integer, Boolean, long, TimeUnit)
+997 * @see #triggerAdhocCommand(String, String, Properties)
+998 */
+999publicRundeckExecution runAdhocCommand(String project, String command, Properties nodeFilters)
+1000throws RundeckApiException, RundeckApiLoginException, IllegalArgumentException {
+1001return runAdhocCommand(project, command, nodeFilters, null, null);
+1002 }
+1003
+1004/**
+1005 * Run an ad-hoc command, and wait until its execution is finished (or aborted) to return. We will poll the RunDeck
+1006 * server at regular interval (configured by the poolingInterval/poolingUnit couple) to know if the execution is
+1007 * finished (or aborted) or is still running. The command will be dispatched to nodes, accordingly to the
+1008 * nodeFilters parameter.
+1009 *
+1010 * @param project name of the project - mandatory
+1011 * @param command to be executed - mandatory
+1012 * @param nodeFilters for selecting nodes on which the command will be executed. See {@link NodeFiltersBuilder}
+1013 * @param poolingInterval for checking the status of the execution. Must be > 0.
+1014 * @param poolingUnit unit (seconds, milli-seconds, ...) of the interval. Default to seconds.
+1015 * @return a {@link RundeckExecution} instance for the (finished/aborted) execution - won't be null
+1016 * @throws RundeckApiException in case of error when calling the API (non-existent project with this name)
+1017 * @throws RundeckApiLoginException if the login failed
+1018 * @throws IllegalArgumentException if the project or command is blank (null, empty or whitespace)
+1019 * @see #runAdhocCommand(String, String, Properties, Integer, Boolean, long, TimeUnit)
+1020 * @see #triggerAdhocCommand(String, String, Properties)
+1021 */
+1022publicRundeckExecution runAdhocCommand(String project, String command, Properties nodeFilters,
+1023long poolingInterval, TimeUnit poolingUnit) throws RundeckApiException, RundeckApiLoginException,
+1024 IllegalArgumentException {
+1025return runAdhocCommand(project, command, nodeFilters, null, null, poolingInterval, poolingUnit);
+1026 }
+1027
+1028/**
+1029 * Run an ad-hoc command, and wait until its execution is finished (or aborted) to return. We will poll the RunDeck
+1030 * server at regular interval (every 5 seconds) to know if the execution is finished (or aborted) or is still
+1031 * running. The command will be dispatched to nodes, accordingly to the nodeFilters parameter.
+1032 *
+1033 * @param project name of the project - mandatory
+1034 * @param command to be executed - mandatory
+1035 * @param nodeFilters for selecting nodes on which the command will be executed. See {@link NodeFiltersBuilder}
+1036 * @param nodeThreadcount thread count to use (for parallelizing when running on multiple nodes) - optional
+1037 * @param nodeKeepgoing if true, continue executing on other nodes even if some fail - optional
+1038 * @return a {@link RundeckExecution} instance for the (finished/aborted) execution - won't be null
+1039 * @throws RundeckApiException in case of error when calling the API (non-existent project with this name)
+1040 * @throws RundeckApiLoginException if the login failed
+1041 * @throws IllegalArgumentException if the project or command is blank (null, empty or whitespace)
+1042 * @see #runAdhocCommand(String, String, Properties, Integer, Boolean, long, TimeUnit)
+1043 * @see #triggerAdhocCommand(String, String, Properties, Integer, Boolean)
+1044 */
+1045publicRundeckExecution runAdhocCommand(String project, String command, Properties nodeFilters,
+1046 Integer nodeThreadcount, Boolean nodeKeepgoing) throws RundeckApiException, RundeckApiLoginException,
+1047 IllegalArgumentException {
+1048return runAdhocCommand(project, command, nodeFilters, nodeThreadcount, nodeKeepgoing, 5, TimeUnit.SECONDS);
+1049 }
+1050
+1051/**
+1052 * Run an ad-hoc command, and wait until its execution is finished (or aborted) to return. We will poll the RunDeck
+1053 * server at regular interval (configured by the poolingInterval/poolingUnit couple) to know if the execution is
+1054 * finished (or aborted) or is still running. The command will be dispatched to nodes, accordingly to the
+1055 * nodeFilters parameter.
+1056 *
+1057 * @param project name of the project - mandatory
+1058 * @param command to be executed - mandatory
+1059 * @param nodeFilters for selecting nodes on which the command will be executed. See {@link NodeFiltersBuilder}
+1060 * @param nodeThreadcount thread count to use (for parallelizing when running on multiple nodes) - optional
+1061 * @param nodeKeepgoing if true, continue executing on other nodes even if some fail - optional
+1062 * @param poolingInterval for checking the status of the execution. Must be > 0.
+1063 * @param poolingUnit unit (seconds, milli-seconds, ...) of the interval. Default to seconds.
+1064 * @return a {@link RundeckExecution} instance for the (finished/aborted) execution - won't be null
+1065 * @throws RundeckApiException in case of error when calling the API (non-existent project with this name)
+1066 * @throws RundeckApiLoginException if the login failed
+1067 * @throws IllegalArgumentException if the project or command is blank (null, empty or whitespace)
+1068 * @see #triggerAdhocCommand(String, String, Properties, Integer, Boolean)
+1069 */
+1070publicRundeckExecution runAdhocCommand(String project, String command, Properties nodeFilters,
+1071 Integer nodeThreadcount, Boolean nodeKeepgoing, long poolingInterval, TimeUnit poolingUnit)
+1072throws RundeckApiException, RundeckApiLoginException, IllegalArgumentException {
+1073if (poolingInterval <= 0) {
+1074 poolingInterval = 5;
+1075 poolingUnit = TimeUnit.SECONDS;
+1076 }
+1077if (poolingUnit == null) {
+1078 poolingUnit = TimeUnit.SECONDS;
+1079 }
+1080
+1081RundeckExecution execution = triggerAdhocCommand(project, command, nodeFilters, nodeThreadcount, nodeKeepgoing);
+1082while (ExecutionStatus.RUNNING.equals(execution.getStatus())) {
+1083try {
+1084 Thread.sleep(poolingUnit.toMillis(poolingInterval));
+1085 } catch (InterruptedException e) {
+1086break;
+1087 }
+1088 execution = getExecution(execution.getId());
+1089 }
+1090return execution;
+1091 }
+1092
+1093/*
+1094 * Executions
+1095 */
+1096
+1097/**
+1098 * Get all running executions (for all projects)
+1099 *
+1100 * @return a {@link List} of {@link RundeckExecution} : might be empty, but won't be null
+1101 * @throws RundeckApiException in case of error when calling the API
+1102 * @throws RundeckApiLoginException if the login failed
+1103 * @see #getRunningExecutions(String)
+1104 */
+1105public List<RundeckExecution> getRunningExecutions() throws RundeckApiException, RundeckApiLoginException {
+1106 List<RundeckExecution> executions = new ArrayList<RundeckExecution>();
+1107for (RundeckProject project : getProjects()) {
+1108 executions.addAll(getRunningExecutions(project.getName()));
+1109 }
+1110return executions;
+1111 }
+1112
+1113/**
+1114 * Get the running executions for the given project
+1115 *
+1116 * @param project name of the project - mandatory
+1117 * @return a {@link List} of {@link RundeckExecution} : might be empty, but won't be null
+1118 * @throws RundeckApiException in case of error when calling the API (non-existent project with this name)
+1119 * @throws RundeckApiLoginException if the login failed
+1120 * @throws IllegalArgumentException if the project is blank (null, empty or whitespace)
+1121 * @see #getRunningExecutions()
+1122 */
+1123public List<RundeckExecution> getRunningExecutions(String project) throws RundeckApiException,
+1124 RundeckApiLoginException, IllegalArgumentException {
+1125 AssertUtil.notBlank(project, "project is mandatory get all running executions !");
+1126returnnewApiCall(this).get(newApiPathBuilder("/executions/running").param("project", project),
+1127new ListParser<RundeckExecution>(newExecutionParser(),
+1128"result/executions/execution"));
+1129 }
+1130
+1131/**
+1132 * Get the executions of the given job
+1133 *
+1134 * @param jobId identifier of the job - mandatory
+1135 * @return a {@link List} of {@link RundeckExecution} : might be empty, but won't be null
+1136 * @throws RundeckApiException in case of error when calling the API (non-existent job with this ID)
+1137 * @throws RundeckApiLoginException if the login failed
+1138 * @throws IllegalArgumentException if the jobId is blank (null, empty or whitespace)
+1139 * @see #getJobExecutions(String, ExecutionStatus, Long, Long)
+1140 */
+1141public List<RundeckExecution> getJobExecutions(String jobId) throws RundeckApiException, RundeckApiLoginException,
+1142 IllegalArgumentException {
+1143return getJobExecutions(jobId, (ExecutionStatus) null);
+1144 }
+1145
+1146/**
+1147 * Get the executions of the given job
+1148 *
+1149 * @param jobId identifier of the job - mandatory
+1150 * @param status of the executions, see {@link ExecutionStatus} - optional (null for all)
+1151 * @return a {@link List} of {@link RundeckExecution} : might be empty, but won't be null
+1152 * @throws RundeckApiException in case of error when calling the API (non-existent job with this ID)
+1153 * @throws RundeckApiLoginException if the login failed
+1154 * @throws IllegalArgumentException if the jobId is blank (null, empty or whitespace), or the executionStatus is
+1155 * invalid
+1156 * @see #getJobExecutions(String, ExecutionStatus, Long, Long)
+1157 */
+1158public List<RundeckExecution> getJobExecutions(String jobId, String status) throws RundeckApiException,
+1159 RundeckApiLoginException, IllegalArgumentException {
+1160return getJobExecutions(jobId,
+1161 StringUtils.isBlank(status) ? null : ExecutionStatus.valueOf(StringUtils.upperCase(status)));
+1162 }
+1163
+1164/**
+1165 * Get the executions of the given job
+1166 *
+1167 * @param jobId identifier of the job - mandatory
+1168 * @param status of the executions, see {@link ExecutionStatus} - optional (null for all)
+1169 * @return a {@link List} of {@link RundeckExecution} : might be empty, but won't be null
+1170 * @throws RundeckApiException in case of error when calling the API (non-existent job with this ID)
+1171 * @throws RundeckApiLoginException if the login failed
+1172 * @throws IllegalArgumentException if the jobId is blank (null, empty or whitespace)
+1173 * @see #getJobExecutions(String, ExecutionStatus, Long, Long)
+1174 */
+1175public List<RundeckExecution> getJobExecutions(String jobId, ExecutionStatus status) throws RundeckApiException,
+1176 RundeckApiLoginException, IllegalArgumentException {
+1177return getJobExecutions(jobId, status, null, null);
+1178 }
+1179
+1180/**
+1181 * Get the executions of the given job
+1182 *
+1183 * @param jobId identifier of the job - mandatory
+1184 * @param status of the executions, see {@link ExecutionStatus} - optional (null for all)
+1185 * @param max number of results to return - optional (null for all)
+1186 * @param offset the 0-indexed offset for the first result to return - optional
+1187 * @return a {@link List} of {@link RundeckExecution} : might be empty, but won't be null
+1188 * @throws RundeckApiException in case of error when calling the API (non-existent job with this ID)
+1189 * @throws RundeckApiLoginException if the login failed
+1190 * @throws IllegalArgumentException if the jobId is blank (null, empty or whitespace), or the executionStatus is
+1191 * invalid
+1192 * @see #getJobExecutions(String, ExecutionStatus, Long, Long)
+1193 */
+1194public List<RundeckExecution> getJobExecutions(String jobId, String status, Long max, Long offset)
+1195throws RundeckApiException, RundeckApiLoginException, IllegalArgumentException {
+1196return getJobExecutions(jobId,
+1197 StringUtils.isBlank(status) ? null : ExecutionStatus.valueOf(StringUtils.upperCase(status)),
+1198 max,
+1199 offset);
+1200 }
+1201
+1202/**
+1203 * Get the executions of the given job
+1204 *
+1205 * @param jobId identifier of the job - mandatory
+1206 * @param status of the executions, see {@link ExecutionStatus} - optional (null for all)
+1207 * @param max number of results to return - optional (null for all)
+1208 * @param offset the 0-indexed offset for the first result to return - optional
+1209 * @return a {@link List} of {@link RundeckExecution} : might be empty, but won't be null
+1210 * @throws RundeckApiException in case of error when calling the API (non-existent job with this ID)
+1211 * @throws RundeckApiLoginException if the login failed
+1212 * @throws IllegalArgumentException if the jobId is blank (null, empty or whitespace)
+1213 */
+1214public List<RundeckExecution> getJobExecutions(String jobId, ExecutionStatus status, Long max, Long offset)
+1215throws RundeckApiException, RundeckApiLoginException, IllegalArgumentException {
+1216 AssertUtil.notBlank(jobId, "jobId is mandatory to get the executions of a job !");
+1217returnnewApiCall(this).get(newApiPathBuilder("/job/", jobId, "/executions").param("status", status)
+1218 .param("max", max)
+1219 .param("offset", offset),
+1220new ListParser<RundeckExecution>(newExecutionParser(),
+1221"result/executions/execution"));
+1222 }
+1223
+1224/**
+1225 * Get a single execution, identified by the given ID
+1226 *
+1227 * @param executionId identifier of the execution - mandatory
+1228 * @return a {@link RundeckExecution} instance - won't be null
+1229 * @throws RundeckApiException in case of error when calling the API (non-existent execution with this ID)
+1230 * @throws RundeckApiLoginException if the login failed
+1231 * @throws IllegalArgumentException if the executionId is null
+1232 */
+1233publicRundeckExecution getExecution(Long executionId) throws RundeckApiException, RundeckApiLoginException,
+1234 IllegalArgumentException {
+1235 AssertUtil.notNull(executionId, "executionId is mandatory to get the details of an execution !");
+1236returnnewApiCall(this).get(newApiPathBuilder("/execution/", executionId.toString()),
+1237newExecutionParser("result/executions/execution"));
+1238 }
+1239
+1240/**
+1241 * Abort an execution (identified by the given ID). The execution should be running...
+1242 *
+1243 * @param executionId identifier of the execution - mandatory
+1244 * @return a {@link RundeckAbort} instance - won't be null
+1245 * @throws RundeckApiException in case of error when calling the API (non-existent execution with this ID)
+1246 * @throws RundeckApiLoginException if the login failed
+1247 * @throws IllegalArgumentException if the executionId is null
+1248 */
+1249publicRundeckAbort abortExecution(Long executionId) throws RundeckApiException, RundeckApiLoginException,
+1250 IllegalArgumentException {
+1251 AssertUtil.notNull(executionId, "executionId is mandatory to abort an execution !");
+1252returnnewApiCall(this).get(newApiPathBuilder("/execution/", executionId.toString(), "/abort"),
+1253newAbortParser("result/abort"));
+1254 }
+1255
+1256/*
+1257 * Nodes
+1258 */
+1259
+1260/**
+1261 * List all nodes (for all projects)
+1262 *
+1263 * @return a {@link List} of {@link RundeckNode} : might be empty, but won't be null
+1264 * @throws RundeckApiException in case of error when calling the API
+1265 * @throws RundeckApiLoginException if the login failed
+1266 */
+1267public List<RundeckNode> getNodes() throws RundeckApiException, RundeckApiLoginException {
+1268 List<RundeckNode> nodes = new ArrayList<RundeckNode>();
+1269for (RundeckProject project : getProjects()) {
+1270 nodes.addAll(getNodes(project.getName()));
+1271 }
+1272return nodes;
+1273 }
+1274
+1275/**
+1276 * List all nodes that belongs to the given project
+1277 *
+1278 * @param project name of the project - mandatory
+1279 * @return a {@link List} of {@link RundeckNode} : might be empty, but won't be null
+1280 * @throws RundeckApiException in case of error when calling the API (non-existent project with this name)
+1281 * @throws RundeckApiLoginException if the login failed
+1282 * @throws IllegalArgumentException if the project is blank (null, empty or whitespace)
+1283 * @see #getNodes(String, Properties)
+1284 */
+1285public List<RundeckNode> getNodes(String project) throws RundeckApiException, RundeckApiLoginException,
+1286 IllegalArgumentException {
+1287return getNodes(project, null);
+1288 }
+1289
+1290/**
+1291 * List nodes that belongs to the given project
+1292 *
+1293 * @param project name of the project - mandatory
+1294 * @param nodeFilters for filtering the nodes - optional. See {@link NodeFiltersBuilder}
+1295 * @return a {@link List} of {@link RundeckNode} : might be empty, but won't be null
+1296 * @throws RundeckApiException in case of error when calling the API (non-existent project with this name)
+1297 * @throws RundeckApiLoginException if the login failed
+1298 * @throws IllegalArgumentException if the project is blank (null, empty or whitespace)
+1299 */
+1300public List<RundeckNode> getNodes(String project, Properties nodeFilters) throws RundeckApiException,
+1301 RundeckApiLoginException, IllegalArgumentException {
+1302 AssertUtil.notBlank(project, "project is mandatory to get all nodes !");
+1303returnnewApiCall(this).get(newApiPathBuilder("/resources").param("project", project)
+1304 .nodeFilters(nodeFilters),
+1305new ListParser<RundeckNode>(newNodeParser(), "project/node"));
+1306 }
+1307
+1308/**
+1309 * Get the definition of a single node
+1310 *
+1311 * @param name of the node - mandatory
+1312 * @param project name of the project - mandatory
+1313 * @return a {@link RundeckNode} instance - won't be null
+1314 * @throws RundeckApiException in case of error when calling the API (non-existent name or project with this name)
+1315 * @throws RundeckApiLoginException if the login failed
+1316 * @throws IllegalArgumentException if the name or project is blank (null, empty or whitespace)
+1317 */
+1318publicRundeckNode getNode(String name, String project) throws RundeckApiException, RundeckApiLoginException,
+1319 IllegalArgumentException {
+1320 AssertUtil.notBlank(name, "the name of the node is mandatory to get a node !");
+1321 AssertUtil.notBlank(project, "project is mandatory to get a node !");
+1322returnnewApiCall(this).get(newApiPathBuilder("/resource/", name).param("project", project),
+1323newNodeParser("project/node"));
+1324 }
+1325
+1326/*
+1327 * System Info
+1328 */
+1329
+1330/**
+1331 * Get system informations about the RunDeck server
+1332 *
+1333 * @return a {@link RundeckSystemInfo} instance - won't be null
+1334 * @throws RundeckApiException in case of error when calling the API
+1335 * @throws RundeckApiException if the login failed
+1336 */
+1337publicRundeckSystemInfo getSystemInfo() throws RundeckApiException, RundeckApiLoginException {
+1338returnnewApiCall(this).get(newApiPathBuilder("/system/info"), newSystemInfoParser("result/system"));
+1339 }
+1340
+1341public String getUrl() {
+1342return url;
+1343 }
+1344
+1345public String getLogin() {
+1346return login;
+1347 }
+1348
+1349public String getPassword() {
+1350return password;
+1351 }
+1352
+1353 @Override
+1354public String toString() {
+1355return"RundeckClient [url=" + url + ", login=" + login + ", password=" + password + "]";
+1356 }
+1357
+1358 @Override
+1359publicint hashCode() {
+1360finalint prime = 31;
+1361int result = 1;
+1362 result = prime * result + ((login == null) ? 0 : login.hashCode());
+1363 result = prime * result + ((password == null) ? 0 : password.hashCode());
+1364 result = prime * result + ((url == null) ? 0 : url.hashCode());
+1365return result;
+1366 }
+1367
+1368 @Override
+1369publicboolean equals(Object obj) {
+1370if (this == obj)
+1371returntrue;
+1372if (obj == null)
+1373return false;
+1374if (getClass() != obj.getClass())
+1375return false;
+1376RundeckClient other = (RundeckClient) obj;
+1377if (login == null) {
+1378if (other.login != null)
+1379return false;
+1380 } elseif (!login.equals(other.login))
+1381return false;
+1382if (password == null) {
+1383if (other.password != null)
+1384return false;
+1385 } elseif (!password.equals(other.password))
+1386return false;
+1387if (url == null) {
+1388if (other.url != null)
+1389return false;
+1390 } elseif (!url.equals(other.url))
+1391return false;
+1392returntrue;
+1393 }
+1394
+1395 }
diff --git a/xref/org/rundeck/api/domain/RundeckJobsImportMethod.html b/xref/org/rundeck/api/domain/RundeckJobsImportMethod.html
new file mode 100644
index 0000000..4d7d288
--- /dev/null
+++ b/xref/org/rundeck/api/domain/RundeckJobsImportMethod.html
@@ -0,0 +1,39 @@
+
+
+
+
+RundeckJobsImportMethod xref
+
+
+
+
+
+1/*
+2 * Copyright 2011 Vincent Behar
+3 *
+4 * Licensed under the Apache License, Version 2.0 (the "License");
+5 * you may not use this file except in compliance with the License.
+6 * You may obtain a copy of the License at
+7 *
+8 * http://www.apache.org/licenses/LICENSE-2.0
+9 *
+10 * Unless required by applicable law or agreed to in writing, software
+11 * distributed under the License is distributed on an "AS IS" BASIS,
+12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+13 * See the License for the specific language governing permissions and
+14 * limitations under the License.
+15 */
+16package org.rundeck.api.domain;
+17
+18/**
+19 * The behavior when importing jobs (which may already exist).
+20 *
+21 * @author Vincent Behar
+22 */
+23public enum RundeckJobsImportMethod {
+24 CREATE, UPDATE, SKIP;
+25 }
+