add support for exporting jobs (to file)

This commit is contained in:
Vincent Behar 2011-07-07 17:05:50 +02:00
parent 64e0c346d3
commit 1594773a79
4 changed files with 131 additions and 33 deletions

View file

@ -397,6 +397,11 @@
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.0.1</version>
</dependency>
<!-- XML Parsing -->
<dependency>
<groupId>dom4j</groupId>

View file

@ -15,7 +15,9 @@
*/
package org.rundeck.api;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
@ -117,6 +119,25 @@ class ApiCall {
return execute(new HttpGet(client.getUrl() + RundeckClient.API_ENDPOINT + apiPath), parser);
}
/**
* Execute an HTTP GET request to the RunDeck instance, on the given path. We will login first, and then execute the
* API call.
*
* @param apiPath on which we will make the HTTP request - see {@link ApiPathBuilder}
* @return a new {@link InputStream} instance, not linked with network resources
* @throws RundeckApiException in case of error when calling the API
* @throws RundeckApiLoginException if the login fails
*/
public InputStream get(ApiPathBuilder apiPath) throws RundeckApiException, RundeckApiLoginException {
ByteArrayInputStream response = execute(new HttpGet(client.getUrl() + RundeckClient.API_ENDPOINT + apiPath));
// try to load the document, to throw an exception in case of error
ParserHelper.loadDocument(response);
response.reset();
return response;
}
/**
* Execute an HTTP DELETE request to the RunDeck instance, on the given path. We will login first, and then execute
* the API call. At the end, the given parser will be used to convert the response to a more useful result object.
@ -144,6 +165,23 @@ class ApiCall {
*/
private <T> T execute(HttpRequestBase request, XmlNodeParser<T> parser) throws RundeckApiException,
RundeckApiLoginException {
// execute the request
InputStream response = execute(request);
// read and parse the response
Document xmlDocument = ParserHelper.loadDocument(response);
return parser.parseXmlNode(xmlDocument);
}
/**
* Execute an HTTP request to the RunDeck instance. We will login first, and then execute the API call.
*
* @param request to execute. see {@link HttpGet}, {@link HttpDelete}, and so on...
* @return a new {@link InputStream} instance, not linked with network resources
* @throws RundeckApiException in case of error when calling the API
* @throws RundeckApiLoginException if the login fails
*/
private ByteArrayInputStream execute(HttpRequestBase request) throws RundeckApiException, RundeckApiLoginException {
HttpClient httpClient = instantiateHttpClient();
try {
login(httpClient);
@ -176,6 +214,7 @@ class ApiCall {
}
}
// check the response code (should be 2xx, even in case of error : error message is in the XML result)
if (response.getStatusLine().getStatusCode() / 100 != 2) {
throw new RundeckApiException("Invalid HTTP response '" + response.getStatusLine() + "' for "
+ request.getURI());
@ -185,17 +224,12 @@ class ApiCall {
+ response.getStatusLine());
}
// read and parse the response
Document xmlDocument = ParserHelper.loadDocument(response);
T result = parser.parseXmlNode(xmlDocument);
// release the connection
// return a new inputStream, so that we can close all network resources
try {
EntityUtils.consume(response.getEntity());
return new ByteArrayInputStream(EntityUtils.toByteArray(response.getEntity()));
} catch (IOException e) {
throw new RundeckApiException("Failed to consume entity (release connection)", e);
throw new RundeckApiException("Failed to consume entity and convert the inputStream", e);
}
return result;
} finally {
httpClient.getConnectionManager().shutdown();
}

View file

@ -15,11 +15,16 @@
*/
package org.rundeck.api;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.rundeck.api.RundeckApiException.RundeckApiLoginException;
import org.rundeck.api.domain.RundeckAbort;
@ -195,6 +200,85 @@ public class RundeckClient implements Serializable {
new ListParser<RundeckJob>(new JobParser(), "result/jobs/job"));
}
/**
* Export the definitions of all jobs that belongs to the given project, as an XML file
*
* @param filename path of the file where the content should be saved
* @param project name of the project - mandatory
* @throws RundeckApiException in case of error when calling the API (non-existent project with this name)
* @throws RundeckApiLoginException if the login failed
* @throws IllegalArgumentException if the project is blank (null, empty or whitespace)
* @throws IOException if we failed to write to the file
* @see #exportJobsToFile(String, String, String, String, String...)
* @see #exportJobs(String)
*/
public void exportJobsToFile(String filename, String project) throws RundeckApiException, RundeckApiLoginException,
IllegalArgumentException, IOException {
exportJobsToFile(filename, project, null, null, new String[0]);
}
/**
* 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
*
* @param filename path of the file where the content should be saved
* @param project name of the project - mandatory
* @param jobFilter a filter for the job Name - optional
* @param groupPath a group or partial group path to include all jobs within that group path - optional
* @param jobIds a list of Job IDs to include - optional
* @throws RundeckApiException in case of error when calling the API (non-existent project with this name)
* @throws RundeckApiLoginException if the login failed
* @throws IllegalArgumentException if the project is blank (null, empty or whitespace)
* @throws IOException if we failed to write to the file
* @see #exportJobsToFile(String, String)
* @see #exportJobs(String, String, String, String...)
*/
public void exportJobsToFile(String filename, String project, String jobFilter, String groupPath, String... jobIds)
throws RundeckApiException, RundeckApiLoginException, IllegalArgumentException, IOException {
InputStream inputStream = exportJobs(project, jobFilter, groupPath, jobIds);
FileUtils.writeByteArrayToFile(new File(filename), IOUtils.toByteArray(inputStream));
}
/**
* Export the definitions of all jobs that belongs to the given project
*
* @param project name of the project - mandatory
* @return an {@link InputStream} instance, not linked to any network resources - won't be null
* @throws RundeckApiException in case of error when calling the API (non-existent project with this name)
* @throws RundeckApiLoginException if the login failed
* @throws IllegalArgumentException if the project is blank (null, empty or whitespace)
* @see #exportJobs(String, String, String, String...)
* @see #exportJobsToFile(String, String)
*/
public InputStream exportJobs(String project) throws RundeckApiException, RundeckApiLoginException,
IllegalArgumentException {
return exportJobs(project, null, null, new String[0]);
}
/**
* Export the definitions of the jobs that belongs to the given project, and matches the given criteria (jobFilter,
* groupPath and jobIds)
*
* @param project name of the project - mandatory
* @param jobFilter a filter for the job Name - optional
* @param groupPath a group or partial group path to include all jobs within that group path - optional
* @param jobIds a list of Job IDs to include - optional
* @return an {@link InputStream} instance, not linked to any network resources - won't be null
* @throws RundeckApiException in case of error when calling the API (non-existent project with this name)
* @throws RundeckApiLoginException if the login failed
* @throws IllegalArgumentException if the project is blank (null, empty or whitespace)
* @see #exportJobs(String)
* @see #exportJobsToFile(String, String, String, String, String...)
*/
public InputStream exportJobs(String project, String jobFilter, String groupPath, String... jobIds)
throws RundeckApiException, RundeckApiLoginException, IllegalArgumentException {
AssertUtil.notBlank(project, "project is mandatory to export all jobs !");
return new ApiCall(this).get(new ApiPathBuilder("/jobs/export").param("project", project)
.param("jobFilter", jobFilter)
.param("groupPath", groupPath)
.param("idlist", StringUtils.join(jobIds, ",")));
}
/**
* Find a job, identified by its project, group and name. Note that the groupPath is optional, as a job does not
* need to belong to a group (either pass null, or an empty string).

View file

@ -15,9 +15,7 @@
*/
package org.rundeck.api.parser;
import java.io.IOException;
import java.io.InputStream;
import org.apache.http.HttpResponse;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Node;
@ -31,35 +29,12 @@ import org.rundeck.api.RundeckApiException;
*/
public class ParserHelper {
/**
* Load an XML {@link Document} from the given RunDeck {@link HttpResponse}.
*
* @param httpResponse from an API call to RunDeck
* @return an XML {@link Document}
* @throws RundeckApiException if we failed to read the response, or if the response is an error
* @see #loadDocument(InputStream)
*/
public static Document loadDocument(HttpResponse httpResponse) throws RundeckApiException {
InputStream inputStream = null;
try {
inputStream = httpResponse.getEntity().getContent();
} catch (IllegalStateException e) {
throw new RundeckApiException("Failed to read RunDeck reponse", e);
} catch (IOException e) {
throw new RundeckApiException("Failed to read RunDeck reponse", e);
}
return loadDocument(inputStream);
}
/**
* Load an XML {@link Document} from the given {@link InputStream}
*
* @param inputStream from an API call to RunDeck
* @return an XML {@link Document}
* @throws RundeckApiException if we failed to read the response, or if the response is an error
* @see #loadDocument(HttpResponse)
*/
public static Document loadDocument(InputStream inputStream) throws RundeckApiException {
SAXReader reader = new SAXReader();