start working on a new-style api (fluent api / method chaining)

This commit is contained in:
Vincent Behar 2011-08-01 17:29:58 +02:00
parent d28b7d7064
commit 564a3ce90f
13 changed files with 1208 additions and 92 deletions

View file

@ -66,6 +66,7 @@ import org.rundeck.api.util.AssertUtil;
*
* @author Vincent Behar
*/
@Deprecated
class ApiCall {
/** RunDeck HTTP header for the auth-token (in case of token-based authentication) */
@ -158,14 +159,14 @@ class ApiCall {
* Execute an HTTP GET 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.
*
* @param apiPath on which we will make the HTTP request - see {@link ApiPathBuilder}
* @param apiPath on which we will make the HTTP request - see {@link OldApiPathBuilder}
* @param parser used to parse the response
* @return the result of the call, as formatted by the parser
* @throws RundeckApiException in case of error when calling the API
* @throws RundeckApiLoginException if the login fails (in case of login-based authentication)
* @throws RundeckApiTokenException if the token is invalid (in case of token-based authentication)
*/
public <T> T get(ApiPathBuilder apiPath, XmlNodeParser<T> parser) throws RundeckApiException,
public <T> T get(OldApiPathBuilder apiPath, XmlNodeParser<T> parser) throws RundeckApiException,
RundeckApiLoginException, RundeckApiTokenException {
return execute(new HttpGet(client.getUrl() + RundeckClient.API_ENDPOINT + apiPath), parser);
}
@ -174,13 +175,13 @@ class ApiCall {
* 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}
* @param apiPath on which we will make the HTTP request - see {@link OldApiPathBuilder}
* @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 (in case of login-based authentication)
* @throws RundeckApiTokenException if the token is invalid (in case of token-based authentication)
*/
public InputStream get(ApiPathBuilder apiPath) throws RundeckApiException, RundeckApiLoginException,
public InputStream get(OldApiPathBuilder apiPath) throws RundeckApiException, RundeckApiLoginException,
RundeckApiTokenException {
ByteArrayInputStream response = execute(new HttpGet(client.getUrl() + RundeckClient.API_ENDPOINT + apiPath));
@ -195,14 +196,14 @@ class ApiCall {
* Execute an HTTP POST 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.
*
* @param apiPath on which we will make the HTTP request - see {@link ApiPathBuilder}
* @param apiPath on which we will make the HTTP request - see {@link OldApiPathBuilder}
* @param parser used to parse the response
* @return the result of the call, as formatted by the parser
* @throws RundeckApiException in case of error when calling the API
* @throws RundeckApiLoginException if the login fails (in case of login-based authentication)
* @throws RundeckApiTokenException if the token is invalid (in case of token-based authentication)
*/
public <T> T post(ApiPathBuilder apiPath, XmlNodeParser<T> parser) throws RundeckApiException,
public <T> T post(OldApiPathBuilder apiPath, XmlNodeParser<T> parser) throws RundeckApiException,
RundeckApiLoginException, RundeckApiTokenException {
HttpPost httpPost = new HttpPost(client.getUrl() + RundeckClient.API_ENDPOINT + apiPath);
@ -220,14 +221,14 @@ class ApiCall {
* 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.
*
* @param apiPath on which we will make the HTTP request - see {@link ApiPathBuilder}
* @param apiPath on which we will make the HTTP request - see {@link OldApiPathBuilder}
* @param parser used to parse the response
* @return the result of the call, as formatted by the parser
* @throws RundeckApiException in case of error when calling the API
* @throws RundeckApiLoginException if the login fails (in case of login-based authentication)
* @throws RundeckApiTokenException if the token is invalid (in case of token-based authentication)
*/
public <T> T delete(ApiPathBuilder apiPath, XmlNodeParser<T> parser) throws RundeckApiException,
public <T> T delete(OldApiPathBuilder apiPath, XmlNodeParser<T> parser) throws RundeckApiException,
RundeckApiLoginException, RundeckApiTokenException {
return execute(new HttpDelete(client.getUrl() + RundeckClient.API_ENDPOINT + apiPath), parser);
}

View file

@ -0,0 +1,220 @@
/*
* Copyright 2011 Vincent Behar
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.rundeck.api;
import java.io.InputStream;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import org.apache.commons.lang.StringUtils;
import org.rundeck.api.util.ParametersUtil;
/**
* Builder for API paths
*
* @author Vincent Behar
*/
@Deprecated
class OldApiPathBuilder {
/** Internally, we store everything in a {@link StringBuilder} */
private final StringBuilder apiPath;
/** When POSTing, we can add attachments */
private final Map<String, InputStream> attachments;
/** Marker for using the right separator between parameters ("?" or "&") */
private boolean firstParamDone = false;
/**
* Build a new instance, for the given "path" (the "path" is the part before the parameters. The path and the
* parameters are separated by a "?")
*
* @param paths elements of the path
*/
public OldApiPathBuilder(String... paths) {
apiPath = new StringBuilder();
attachments = new HashMap<String, InputStream>();
if (paths != null) {
for (String path : paths) {
if (StringUtils.isNotBlank(path)) {
append(path);
}
}
}
}
/**
* Append the given parameter (key and value). This will only append the parameter if it is not blank (null, empty
* or whitespace), and make sure to add the right separator ("?" or "&") before. The key and value will be separated
* by the "=" character. Also, the value will be url-encoded.
*
* @param key of the parameter. Must not be null or empty
* @param value of the parameter. May be null/empty/blank. Will be url-encoded.
* @return this, for method chaining
*/
public OldApiPathBuilder param(String key, String value) {
if (StringUtils.isNotBlank(value)) {
appendSeparator();
append(key);
append("=");
append(ParametersUtil.urlEncode(value));
}
return this;
}
/**
* Append the given parameter (key and value). This will only append the parameter if it is not null, and make sure
* to add the right separator ("?" or "&") before. The key and value will be separated by the "=" character. Also,
* the value will be converted to lower-case.
*
* @param key of the parameter. Must not be null or empty
* @param value of the parameter. May be null
* @return this, for method chaining
*/
public OldApiPathBuilder param(String key, Enum<?> value) {
if (value != null) {
param(key, StringUtils.lowerCase(value.toString()));
}
return this;
}
/**
* Append the given parameter (key and value). This will only append the parameter if it is not null, and make sure
* to add the right separator ("?" or "&") before. The key and value will be separated by the "=" character.
*
* @param key of the parameter. Must not be null or empty
* @param value of the parameter. May be null
* @return this, for method chaining
*/
public OldApiPathBuilder param(String key, Date value) {
if (value != null) {
param(key, value.getTime());
}
return this;
}
/**
* Append the given parameter (key and value). This will only append the parameter if it is not null, and make sure
* to add the right separator ("?" or "&") before. The key and value will be separated by the "=" character.
*
* @param key of the parameter. Must not be null or empty
* @param value of the parameter. May be null
* @return this, for method chaining
*/
public OldApiPathBuilder param(String key, Long value) {
if (value != null) {
param(key, value.toString());
}
return this;
}
/**
* Append the given parameter (key and value). This will only append the parameter if it is not null, and make sure
* to add the right separator ("?" or "&") before. The key and value will be separated by the "=" character.
*
* @param key of the parameter. Must not be null or empty
* @param value of the parameter. May be null
* @return this, for method chaining
*/
public OldApiPathBuilder param(String key, Integer value) {
if (value != null) {
param(key, value.toString());
}
return this;
}
/**
* Append the given parameter (key and value). This will only append the parameter if it is not null, and make sure
* to add the right separator ("?" or "&") before. The key and value will be separated by the "=" character.
*
* @param key of the parameter. Must not be null or empty
* @param value of the parameter. May be null
* @return this, for method chaining
*/
public OldApiPathBuilder param(String key, Boolean value) {
if (value != null) {
param(key, value.toString());
}
return this;
}
/**
* Append the given node filters, only if it is not null/empty
*
* @param nodeFilters may be null/empty
* @return this, for method chaining
* @see ParametersUtil#generateNodeFiltersString(Properties)
*/
public OldApiPathBuilder nodeFilters(Properties nodeFilters) {
String filters = ParametersUtil.generateNodeFiltersString(nodeFilters);
if (StringUtils.isNotBlank(filters)) {
appendSeparator();
append(filters);
}
return this;
}
/**
* When POSTing a request, add the given {@link InputStream} as an attachment to the content of the request. This
* will only add the stream if it is not null.
*
* @param name of the attachment. Must not be null or empty
* @param stream. May be null
* @return this, for method chaining
*/
public OldApiPathBuilder attach(String name, InputStream stream) {
if (stream != null) {
attachments.put(name, stream);
}
return this;
}
/**
* @return all attachments to be POSTed, with their names
*/
public Map<String, InputStream> getAttachments() {
return attachments;
}
@Override
public String toString() {
return apiPath.toString();
}
/**
* Append the given string
*
* @param str to append
*/
private void append(String str) {
apiPath.append(str);
}
/**
* Append the right separator "?" or "&" between 2 parameters
*/
private void appendSeparator() {
if (firstParamDone) {
append("&");
} else {
append("?");
firstParamDone = true;
}
}
}

View file

@ -47,9 +47,16 @@ import org.rundeck.api.parser.JobParser;
import org.rundeck.api.parser.JobsImportResultParser;
import org.rundeck.api.parser.ListParser;
import org.rundeck.api.parser.NodeParser;
import org.rundeck.api.parser.ProjectParser;
import org.rundeck.api.parser.StringParser;
import org.rundeck.api.parser.SystemInfoParser;
import org.rundeck.api.request.AuthRequest;
import org.rundeck.api.request.HistoryRequest;
import org.rundeck.api.request.JobRunRequest;
import org.rundeck.api.request.JobTriggerRequest;
import org.rundeck.api.request.JobsListingRequest;
import org.rundeck.api.request.PingRequest;
import org.rundeck.api.request.ProjectDetailsRequest;
import org.rundeck.api.request.ProjectsListingRequest;
import org.rundeck.api.util.AssertUtil;
import org.rundeck.api.util.ParametersUtil;
@ -154,7 +161,7 @@ public class RundeckClient implements Serializable {
* @throws RundeckApiException if the ping fails
*/
public void ping() throws RundeckApiException {
new ApiCall(this).ping();
new PingRequest(this).execute();
}
/**
@ -164,7 +171,7 @@ public class RundeckClient implements Serializable {
* @throws RundeckApiTokenException if the token is invalid (in case of token-based authentication)
*/
public void testAuth() throws RundeckApiLoginException, RundeckApiTokenException {
new ApiCall(this).testAuth();
new AuthRequest(this).execute();
}
/**
@ -176,6 +183,38 @@ public class RundeckClient implements Serializable {
testAuth();
}
/*
* New-style API
*/
public ProjectsListingRequest newProjectsListingRequest() {
return new ProjectsListingRequest(this);
}
public ProjectDetailsRequest newProjectDetailsRequest(String projectName) {
return new ProjectDetailsRequest(this, projectName);
}
public JobsListingRequest newJobsListingRequest(String project) {
return new JobsListingRequest(this, project);
}
public JobTriggerRequest newJobTriggerRequest(String jobId) {
return new JobTriggerRequest(this, jobId);
}
public JobRunRequest newJobRunRequest(String jobId) {
return new JobRunRequest(this, jobId);
}
public HistoryRequest newHistoryRequest(String project) {
return new HistoryRequest(this, project);
}
/*
* Old-style API
*/
/*
* Projects
*/
@ -190,8 +229,7 @@ public class RundeckClient implements Serializable {
*/
public List<RundeckProject> getProjects() throws RundeckApiException, RundeckApiLoginException,
RundeckApiTokenException {
return new ApiCall(this).get(new ApiPathBuilder("/projects"),
new ListParser<RundeckProject>(new ProjectParser(), "result/projects/project"));
return newProjectsListingRequest().execute();
}
/**
@ -206,9 +244,7 @@ public class RundeckClient implements Serializable {
*/
public RundeckProject getProject(String projectName) throws RundeckApiException, RundeckApiLoginException,
RundeckApiTokenException, IllegalArgumentException {
AssertUtil.notBlank(projectName, "projectName is mandatory to get the details of a project !");
return new ApiCall(this).get(new ApiPathBuilder("/project/", projectName),
new ProjectParser("result/projects/project"));
return newProjectDetailsRequest(projectName).execute();
}
/*
@ -244,7 +280,7 @@ public class RundeckClient implements Serializable {
*/
public List<RundeckJob> getJobs(String project) throws RundeckApiException, RundeckApiLoginException,
RundeckApiTokenException, IllegalArgumentException {
return getJobs(project, null, null, new String[0]);
return newJobsListingRequest(project).execute();
}
/**
@ -264,10 +300,10 @@ public class RundeckClient implements Serializable {
public List<RundeckJob> getJobs(String project, String jobFilter, String groupPath, String... jobIds)
throws RundeckApiException, RundeckApiLoginException, RundeckApiTokenException, IllegalArgumentException {
AssertUtil.notBlank(project, "project is mandatory to get all jobs !");
return new ApiCall(this).get(new ApiPathBuilder("/jobs").param("project", project)
.param("jobFilter", jobFilter)
.param("groupPath", groupPath)
.param("idlist", StringUtils.join(jobIds, ",")),
return new ApiCall(this).get(new OldApiPathBuilder("/jobs").param("project", project)
.param("jobFilter", jobFilter)
.param("groupPath", groupPath)
.param("idlist", StringUtils.join(jobIds, ",")),
new ListParser<RundeckJob>(new JobParser(), "result/jobs/job"));
}
@ -450,11 +486,12 @@ public class RundeckClient implements Serializable {
throws RundeckApiException, RundeckApiLoginException, RundeckApiTokenException, IllegalArgumentException {
AssertUtil.notNull(format, "format is mandatory to export jobs !");
AssertUtil.notBlank(project, "project is mandatory to export jobs !");
return new ApiCall(this).get(new ApiPathBuilder("/jobs/export").param("format", format)
.param("project", project)
.param("jobFilter", jobFilter)
.param("groupPath", groupPath)
.param("idlist", StringUtils.join(jobIds, ",")));
return new ApiCall(this).get(new OldApiPathBuilder("/jobs/export").param("format", format)
.param("project", project)
.param("jobFilter", jobFilter)
.param("groupPath", groupPath)
.param("idlist",
StringUtils.join(jobIds, ",")));
}
/**
@ -538,7 +575,7 @@ public class RundeckClient implements Serializable {
RundeckApiTokenException, IllegalArgumentException {
AssertUtil.notNull(format, "format is mandatory to export a job !");
AssertUtil.notBlank(jobId, "jobId is mandatory to export a job !");
return new ApiCall(this).get(new ApiPathBuilder("/job/", jobId).param("format", format));
return new ApiCall(this).get(new OldApiPathBuilder("/job/", jobId).param("format", format));
}
/**
@ -712,9 +749,9 @@ public class RundeckClient implements Serializable {
RundeckApiTokenException, IllegalArgumentException {
AssertUtil.notNull(stream, "inputStream of jobs is mandatory to import jobs !");
AssertUtil.notNull(fileType, "fileType is mandatory to import jobs !");
return new ApiCall(this).post(new ApiPathBuilder("/jobs/import").param("format", fileType)
.param("dupeOption", importBehavior)
.attach("xmlBatch", stream),
return new ApiCall(this).post(new OldApiPathBuilder("/jobs/import").param("format", fileType)
.param("dupeOption", importBehavior)
.attach("xmlBatch", stream),
new JobsImportResultParser("result"));
}
@ -755,7 +792,7 @@ public class RundeckClient implements Serializable {
public RundeckJob getJob(String jobId) throws RundeckApiException, RundeckApiLoginException,
RundeckApiTokenException, IllegalArgumentException {
AssertUtil.notBlank(jobId, "jobId is mandatory to get the details of a job !");
return new ApiCall(this).get(new ApiPathBuilder("/job/", jobId), new JobParser("joblist/job"));
return new ApiCall(this).get(new OldApiPathBuilder("/job/", jobId), new JobParser("joblist/job"));
}
/**
@ -771,7 +808,8 @@ public class RundeckClient implements Serializable {
public String deleteJob(String jobId) throws RundeckApiException, RundeckApiLoginException,
RundeckApiTokenException, IllegalArgumentException {
AssertUtil.notBlank(jobId, "jobId is mandatory to delete a job !");
return new ApiCall(this).delete(new ApiPathBuilder("/job/", jobId), new StringParser("result/success/message"));
return new ApiCall(this).delete(new OldApiPathBuilder("/job/", jobId),
new StringParser("result/success/message"));
}
/**
@ -829,11 +867,7 @@ public class RundeckClient implements Serializable {
*/
public RundeckExecution triggerJob(String jobId, Properties options, Properties nodeFilters)
throws RundeckApiException, RundeckApiLoginException, RundeckApiTokenException, IllegalArgumentException {
AssertUtil.notBlank(jobId, "jobId is mandatory to trigger a job !");
return new ApiCall(this).get(new ApiPathBuilder("/job/", jobId, "/run").param("argString",
ParametersUtil.generateArgString(options))
.nodeFilters(nodeFilters),
new ExecutionParser("result/executions/execution"));
return newJobTriggerRequest(jobId).addOptions(options).filterNodes(nodeFilters).execute();
}
/**
@ -941,24 +975,10 @@ public class RundeckClient implements Serializable {
public RundeckExecution runJob(String jobId, Properties options, Properties nodeFilters, long poolingInterval,
TimeUnit poolingUnit) throws RundeckApiException, RundeckApiLoginException, RundeckApiTokenException,
IllegalArgumentException {
if (poolingInterval <= 0) {
poolingInterval = DEFAULT_POOLING_INTERVAL;
poolingUnit = DEFAULT_POOLING_UNIT;
}
if (poolingUnit == null) {
poolingUnit = DEFAULT_POOLING_UNIT;
}
RundeckExecution execution = triggerJob(jobId, options, nodeFilters);
while (ExecutionStatus.RUNNING.equals(execution.getStatus())) {
try {
Thread.sleep(poolingUnit.toMillis(poolingInterval));
} catch (InterruptedException e) {
break;
}
execution = getExecution(execution.getId());
}
return execution;
return newJobRunRequest(jobId).addOptions(options)
.filterNodes(nodeFilters)
.poolingInterval(poolingInterval, poolingUnit)
.execute();
}
/*
@ -1026,13 +1046,14 @@ public class RundeckClient implements Serializable {
RundeckApiTokenException, IllegalArgumentException {
AssertUtil.notBlank(project, "project is mandatory to trigger an ad-hoc command !");
AssertUtil.notBlank(command, "command is mandatory to trigger an ad-hoc command !");
RundeckExecution execution = new ApiCall(this).get(new ApiPathBuilder("/run/command").param("project", project)
.param("exec", command)
.param("nodeThreadcount",
nodeThreadcount)
.param("nodeKeepgoing",
nodeKeepgoing)
.nodeFilters(nodeFilters),
RundeckExecution execution = new ApiCall(this).get(new OldApiPathBuilder("/run/command").param("project",
project)
.param("exec", command)
.param("nodeThreadcount",
nodeThreadcount)
.param("nodeKeepgoing",
nodeKeepgoing)
.nodeFilters(nodeFilters),
new ExecutionParser("result/execution"));
// the first call just returns the ID of the execution, so we need another call to get a "real" execution
return getExecution(execution.getId());
@ -1385,16 +1406,17 @@ public class RundeckClient implements Serializable {
RundeckApiLoginException, RundeckApiTokenException, IllegalArgumentException {
AssertUtil.notBlank(project, "project is mandatory to trigger an ad-hoc script !");
AssertUtil.notNull(script, "script is mandatory to trigger an ad-hoc script !");
RundeckExecution execution = new ApiCall(this).post(new ApiPathBuilder("/run/script").param("project", project)
.attach("scriptFile",
script)
.param("argString",
ParametersUtil.generateArgString(options))
.param("nodeThreadcount",
nodeThreadcount)
.param("nodeKeepgoing",
nodeKeepgoing)
.nodeFilters(nodeFilters),
RundeckExecution execution = new ApiCall(this).post(new OldApiPathBuilder("/run/script").param("project",
project)
.attach("scriptFile",
script)
.param("argString",
ParametersUtil.generateArgString(options))
.param("nodeThreadcount",
nodeThreadcount)
.param("nodeKeepgoing",
nodeKeepgoing)
.nodeFilters(nodeFilters),
new ExecutionParser("result/execution"));
// the first call just returns the ID of the execution, so we need another call to get a "real" execution
return getExecution(execution.getId());
@ -1890,7 +1912,7 @@ public class RundeckClient implements Serializable {
public List<RundeckExecution> getRunningExecutions(String project) throws RundeckApiException,
RundeckApiLoginException, RundeckApiTokenException, IllegalArgumentException {
AssertUtil.notBlank(project, "project is mandatory get all running executions !");
return new ApiCall(this).get(new ApiPathBuilder("/executions/running").param("project", project),
return new ApiCall(this).get(new OldApiPathBuilder("/executions/running").param("project", project),
new ListParser<RundeckExecution>(new ExecutionParser(),
"result/executions/execution"));
}
@ -1986,9 +2008,9 @@ public class RundeckClient implements Serializable {
public List<RundeckExecution> getJobExecutions(String jobId, ExecutionStatus status, Long max, Long offset)
throws RundeckApiException, RundeckApiLoginException, RundeckApiTokenException, IllegalArgumentException {
AssertUtil.notBlank(jobId, "jobId is mandatory to get the executions of a job !");
return new ApiCall(this).get(new ApiPathBuilder("/job/", jobId, "/executions").param("status", status)
.param("max", max)
.param("offset", offset),
return new ApiCall(this).get(new OldApiPathBuilder("/job/", jobId, "/executions").param("status", status)
.param("max", max)
.param("offset", offset),
new ListParser<RundeckExecution>(new ExecutionParser(),
"result/executions/execution"));
}
@ -2006,7 +2028,7 @@ public class RundeckClient implements Serializable {
public RundeckExecution getExecution(Long executionId) throws RundeckApiException, RundeckApiLoginException,
RundeckApiTokenException, IllegalArgumentException {
AssertUtil.notNull(executionId, "executionId is mandatory to get the details of an execution !");
return new ApiCall(this).get(new ApiPathBuilder("/execution/", executionId.toString()),
return new ApiCall(this).get(new OldApiPathBuilder("/execution/", executionId.toString()),
new ExecutionParser("result/executions/execution"));
}
@ -2023,7 +2045,7 @@ public class RundeckClient implements Serializable {
public RundeckAbort abortExecution(Long executionId) throws RundeckApiException, RundeckApiLoginException,
RundeckApiTokenException, IllegalArgumentException {
AssertUtil.notNull(executionId, "executionId is mandatory to abort an execution !");
return new ApiCall(this).get(new ApiPathBuilder("/execution/", executionId.toString(), "/abort"),
return new ApiCall(this).get(new OldApiPathBuilder("/execution/", executionId.toString(), "/abort"),
new AbortParser("result/abort"));
}
@ -2207,15 +2229,15 @@ public class RundeckClient implements Serializable {
Date begin, Date end, Long max, Long offset) throws RundeckApiException, RundeckApiLoginException,
RundeckApiTokenException, IllegalArgumentException {
AssertUtil.notBlank(project, "project is mandatory to get the history !");
return new ApiCall(this).get(new ApiPathBuilder("/history").param("project", project)
.param("jobIdFilter", jobId)
.param("reportIdFilter", reportId)
.param("userFilter", user)
.param("recentFilter", recent)
.param("begin", begin)
.param("end", end)
.param("max", max)
.param("offset", offset),
return new ApiCall(this).get(new OldApiPathBuilder("/history").param("project", project)
.param("jobIdFilter", jobId)
.param("reportIdFilter", reportId)
.param("userFilter", user)
.param("recentFilter", recent)
.param("begin", begin)
.param("end", end)
.param("max", max)
.param("offset", offset),
new HistoryParser("result/events"));
}
@ -2269,8 +2291,8 @@ public class RundeckClient implements Serializable {
public List<RundeckNode> getNodes(String project, Properties nodeFilters) throws RundeckApiException,
RundeckApiLoginException, RundeckApiTokenException, IllegalArgumentException {
AssertUtil.notBlank(project, "project is mandatory to get all nodes !");
return new ApiCall(this).get(new ApiPathBuilder("/resources").param("project", project)
.nodeFilters(nodeFilters),
return new ApiCall(this).get(new OldApiPathBuilder("/resources").param("project", project)
.nodeFilters(nodeFilters),
new ListParser<RundeckNode>(new NodeParser(), "project/node"));
}
@ -2289,7 +2311,7 @@ public class RundeckClient implements Serializable {
RundeckApiTokenException, IllegalArgumentException {
AssertUtil.notBlank(name, "the name of the node is mandatory to get a node !");
AssertUtil.notBlank(project, "project is mandatory to get a node !");
return new ApiCall(this).get(new ApiPathBuilder("/resource/", name).param("project", project),
return new ApiCall(this).get(new OldApiPathBuilder("/resource/", name).param("project", project),
new NodeParser("project/node"));
}
@ -2307,7 +2329,7 @@ public class RundeckClient implements Serializable {
*/
public RundeckSystemInfo getSystemInfo() throws RundeckApiException, RundeckApiLoginException,
RundeckApiTokenException {
return new ApiCall(this).get(new ApiPathBuilder("/system/info"), new SystemInfoParser("result/system"));
return new ApiCall(this).get(new OldApiPathBuilder("/system/info"), new SystemInfoParser("result/system"));
}
/**

View file

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.rundeck.api;
package org.rundeck.api.request;
import java.io.InputStream;
import java.util.Date;

View file

@ -0,0 +1,444 @@
/*
* Copyright 2011 Vincent Behar
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.rundeck.api.request;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ProxySelector;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;
import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpException;
import org.apache.http.HttpRequest;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.ParseException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.InputStreamBody;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.ProxySelectorRoutePlanner;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.HttpProtocolParams;
import org.apache.http.protocol.HTTP;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;
import org.dom4j.Document;
import org.rundeck.api.RundeckApiException;
import org.rundeck.api.RundeckClient;
import org.rundeck.api.RundeckApiException.RundeckApiLoginException;
import org.rundeck.api.RundeckApiException.RundeckApiTokenException;
import org.rundeck.api.parser.ParserHelper;
import org.rundeck.api.parser.XmlNodeParser;
import org.rundeck.api.util.AssertUtil;
/**
* TODO
*
* @author Vincent Behar
*/
abstract class ApiRequest<T> {
/** RunDeck HTTP header for the auth-token (in case of token-based authentication) */
private static final transient String AUTH_TOKEN_HEADER = "X-RunDeck-Auth-Token";
/** {@link RundeckClient} instance holding the RunDeck url and the credentials */
protected final RundeckClient client;
/**
* TODO
*
* @param client
*/
protected ApiRequest(RundeckClient client) {
super();
this.client = client;
AssertUtil.notNull(client, "The RunDeck Client must not be null !");
}
/**
* TODO
*
* @return
*/
public abstract T execute();
/**
* Try to "ping" the RunDeck instance to see if it is alive
*
* @throws RundeckApiException if the ping fails
*/
protected void ping() throws RundeckApiException {
HttpClient httpClient = instantiateHttpClient();
try {
HttpResponse response = httpClient.execute(new HttpGet(client.getUrl()));
if (response.getStatusLine().getStatusCode() / 100 != 2) {
throw new RundeckApiException("Invalid HTTP response '" + response.getStatusLine() + "' when pinging "
+ client.getUrl());
}
} catch (IOException e) {
throw new RundeckApiException("Failed to ping RunDeck instance at " + client.getUrl(), e);
} finally {
httpClient.getConnectionManager().shutdown();
}
}
/**
* Test the authentication on the RunDeck instance. Will delegate to either {@link #testLoginAuth()} (in case of
* login-based auth) or {@link #testTokenAuth()} (in case of token-based auth).
*
* @throws RundeckApiLoginException if the login fails (in case of login-based authentication)
* @throws RundeckApiTokenException if the token is invalid (in case of token-based authentication)
* @see #testLoginAuth()
* @see #testTokenAuth()
*/
protected void testAuth() throws RundeckApiLoginException, RundeckApiTokenException {
if (client.getToken() != null) {
testTokenAuth();
} else {
testLoginAuth();
}
}
/**
* Test the login-based authentication on the RunDeck instance
*
* @throws RundeckApiLoginException if the login fails
* @see #testAuth()
*/
protected void testLoginAuth() throws RundeckApiLoginException {
HttpClient httpClient = instantiateHttpClient();
try {
login(httpClient);
} finally {
httpClient.getConnectionManager().shutdown();
}
}
/**
* Test the token-based authentication on the RunDeck instance
*
* @throws RundeckApiTokenException if the token is invalid
* @see #testAuth()
*/
protected void testTokenAuth() throws RundeckApiTokenException {
try {
execute(new HttpGet(client.getUrl() + RundeckClient.API_ENDPOINT + "/system/info"));
} catch (RundeckApiTokenException e) {
throw e;
} catch (RundeckApiException e) {
throw new RundeckApiTokenException("Failed to verify token", e);
}
}
/**
* Execute an HTTP GET 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.
*
* @param apiPath on which we will make the HTTP request - see {@link ApiPathBuilder}
* @param parser used to parse the response
* @return the result of the call, as formatted by the parser
* @throws RundeckApiException in case of error when calling the API
* @throws RundeckApiLoginException if the login fails (in case of login-based authentication)
* @throws RundeckApiTokenException if the token is invalid (in case of token-based authentication)
*/
protected T get(ApiPathBuilder apiPath, XmlNodeParser<T> parser) throws RundeckApiException,
RundeckApiLoginException, RundeckApiTokenException {
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 (in case of login-based authentication)
* @throws RundeckApiTokenException if the token is invalid (in case of token-based authentication)
*/
protected InputStream get(ApiPathBuilder apiPath) throws RundeckApiException, RundeckApiLoginException,
RundeckApiTokenException {
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 POST 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.
*
* @param apiPath on which we will make the HTTP request - see {@link ApiPathBuilder}
* @param parser used to parse the response
* @return the result of the call, as formatted by the parser
* @throws RundeckApiException in case of error when calling the API
* @throws RundeckApiLoginException if the login fails (in case of login-based authentication)
* @throws RundeckApiTokenException if the token is invalid (in case of token-based authentication)
*/
protected T post(ApiPathBuilder apiPath, XmlNodeParser<T> parser) throws RundeckApiException,
RundeckApiLoginException, RundeckApiTokenException {
HttpPost httpPost = new HttpPost(client.getUrl() + RundeckClient.API_ENDPOINT + apiPath);
// POST a multi-part request, with all attachments
MultipartEntity entity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE);
for (Entry<String, InputStream> attachment : apiPath.getAttachments().entrySet()) {
entity.addPart(attachment.getKey(), new InputStreamBody(attachment.getValue(), attachment.getKey()));
}
httpPost.setEntity(entity);
return execute(httpPost, parser);
}
/**
* 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.
*
* @param apiPath on which we will make the HTTP request - see {@link ApiPathBuilder}
* @param parser used to parse the response
* @return the result of the call, as formatted by the parser
* @throws RundeckApiException in case of error when calling the API
* @throws RundeckApiLoginException if the login fails (in case of login-based authentication)
* @throws RundeckApiTokenException if the token is invalid (in case of token-based authentication)
*/
protected T delete(ApiPathBuilder apiPath, XmlNodeParser<T> parser) throws RundeckApiException,
RundeckApiLoginException, RundeckApiTokenException {
return execute(new HttpDelete(client.getUrl() + RundeckClient.API_ENDPOINT + apiPath), parser);
}
/**
* Execute an HTTP request to the RunDeck instance. 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.
*
* @param request to execute. see {@link HttpGet}, {@link HttpDelete}, and so on...
* @param parser used to parse the response
* @return the result of the call, as formatted by the parser
* @throws RundeckApiException in case of error when calling the API
* @throws RundeckApiLoginException if the login fails (in case of login-based authentication)
* @throws RundeckApiTokenException if the token is invalid (in case of token-based authentication)
*/
private T execute(HttpRequestBase request, XmlNodeParser<T> parser) throws RundeckApiException,
RundeckApiLoginException, RundeckApiTokenException {
// 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 (in case of login-based authentication)
* @throws RundeckApiTokenException if the token is invalid (in case of token-based authentication)
*/
private ByteArrayInputStream execute(HttpRequestBase request) throws RundeckApiException, RundeckApiLoginException,
RundeckApiTokenException {
HttpClient httpClient = instantiateHttpClient();
try {
// we only need to manually login in case of login-based authentication
// note that in case of token-based auth, the auth (via an HTTP header) is managed by an interceptor.
if (client.getToken() == null) {
login(httpClient);
}
// execute the HTTP request
HttpResponse response = null;
try {
response = httpClient.execute(request);
} catch (IOException e) {
throw new RundeckApiException("Failed to execute an HTTP " + request.getMethod() + " on url : "
+ request.getURI(), e);
}
// in case of error, we get a redirect to /api/error
// that we need to follow manually for POST and DELETE requests (as GET)
if (response.getStatusLine().getStatusCode() / 100 == 3) {
String newLocation = response.getFirstHeader("Location").getValue();
try {
EntityUtils.consume(response.getEntity());
} catch (IOException e) {
throw new RundeckApiException("Failed to consume entity (release connection)", e);
}
request = new HttpGet(newLocation);
try {
response = httpClient.execute(request);
} catch (IOException e) {
throw new RundeckApiException("Failed to execute an HTTP GET on url : " + request.getURI(), e);
}
}
// 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) {
if (response.getStatusLine().getStatusCode() == 403 && client.getToken() != null) {
throw new RundeckApiTokenException("Invalid Token ! Got HTTP response '" + response.getStatusLine()
+ "' for " + request.getURI());
} else {
throw new RundeckApiException("Invalid HTTP response '" + response.getStatusLine() + "' for "
+ request.getURI());
}
}
if (response.getEntity() == null) {
throw new RundeckApiException("Empty RunDeck response ! HTTP status line is : "
+ response.getStatusLine());
}
// return a new inputStream, so that we can close all network resources
try {
return new ByteArrayInputStream(EntityUtils.toByteArray(response.getEntity()));
} catch (IOException e) {
throw new RundeckApiException("Failed to consume entity and convert the inputStream", e);
}
} finally {
httpClient.getConnectionManager().shutdown();
}
}
/**
* Do the actual work of login, using the given {@link HttpClient} instance. You'll need to re-use this instance
* when making API calls (such as running a job). Only use this in case of login-based authentication.
*
* @param httpClient pre-instantiated
* @throws RundeckApiLoginException if the login failed
*/
private void login(HttpClient httpClient) throws RundeckApiLoginException {
String location = client.getUrl() + "/j_security_check";
while (true) {
HttpPost postLogin = new HttpPost(location);
List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair("j_username", client.getLogin()));
params.add(new BasicNameValuePair("j_password", client.getPassword()));
params.add(new BasicNameValuePair("action", "login"));
HttpResponse response = null;
try {
postLogin.setEntity(new UrlEncodedFormEntity(params, HTTP.UTF_8));
response = httpClient.execute(postLogin);
} catch (IOException e) {
throw new RundeckApiLoginException("Failed to post login form on " + location, e);
}
if (response.getStatusLine().getStatusCode() / 100 == 3) {
// HTTP client refuses to handle redirects (code 3xx) for POST, so we have to do it manually...
location = response.getFirstHeader("Location").getValue();
try {
EntityUtils.consume(response.getEntity());
} catch (IOException e) {
throw new RundeckApiLoginException("Failed to consume entity (release connection)", e);
}
continue;
}
if (response.getStatusLine().getStatusCode() / 100 != 2) {
throw new RundeckApiLoginException("Invalid HTTP response '" + response.getStatusLine() + "' for "
+ location);
}
try {
String content = EntityUtils.toString(response.getEntity(), HTTP.UTF_8);
if (StringUtils.contains(content, "j_security_check")) {
throw new RundeckApiLoginException("Login failed for user " + client.getLogin());
}
try {
EntityUtils.consume(response.getEntity());
} catch (IOException e) {
throw new RundeckApiLoginException("Failed to consume entity (release connection)", e);
}
} catch (IOException io) {
throw new RundeckApiLoginException("Failed to read RunDeck result", io);
} catch (ParseException p) {
throw new RundeckApiLoginException("Failed to parse RunDeck response", p);
}
break;
}
}
/**
* Instantiate a new {@link HttpClient} instance, configured to accept all SSL certificates
*
* @return an {@link HttpClient} instance - won't be null
*/
private HttpClient instantiateHttpClient() {
DefaultHttpClient httpClient = new DefaultHttpClient();
// configure user-agent
HttpProtocolParams.setUserAgent(httpClient.getParams(), "RunDeck API Java Client " + RundeckClient.API_VERSION);
// configure SSL
SSLSocketFactory socketFactory = null;
try {
socketFactory = new SSLSocketFactory(new TrustStrategy() {
@Override
public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
return true;
}
}, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
} catch (KeyManagementException e) {
throw new RuntimeException(e);
} catch (UnrecoverableKeyException e) {
throw new RuntimeException(e);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
} catch (KeyStoreException e) {
throw new RuntimeException(e);
}
httpClient.getConnectionManager().getSchemeRegistry().register(new Scheme("https", 443, socketFactory));
// configure proxy (use system env : http.proxyHost / http.proxyPort)
System.setProperty("java.net.useSystemProxies", "true");
httpClient.setRoutePlanner(new ProxySelectorRoutePlanner(httpClient.getConnectionManager().getSchemeRegistry(),
ProxySelector.getDefault()));
// in case of token-based authentication, add the correct HTTP header to all requests via an interceptor
httpClient.addRequestInterceptor(new HttpRequestInterceptor() {
@Override
public void process(HttpRequest request, HttpContext context) throws HttpException, IOException {
if (client.getToken() != null) {
request.addHeader(AUTH_TOKEN_HEADER, client.getToken());
}
}
});
return httpClient;
}
}

View file

@ -0,0 +1,36 @@
/*
* Copyright 2011 Vincent Behar
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.rundeck.api.request;
import org.rundeck.api.RundeckClient;
/**
* TODO
*
* @author Vincent Behar
*/
public class AuthRequest extends ApiRequest<Void> {
public AuthRequest(RundeckClient client) {
super(client);
}
@Override
public Void execute() {
testAuth();
return null;
}
}

View file

@ -0,0 +1,83 @@
/*
* Copyright 2011 Vincent Behar
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.rundeck.api.request;
import java.util.Date;
import org.rundeck.api.RundeckClient;
import org.rundeck.api.domain.RundeckHistory;
import org.rundeck.api.parser.HistoryParser;
import org.rundeck.api.util.AssertUtil;
/**
* TODO
*
* @author Vincent Behar
*/
public class HistoryRequest extends ApiRequest<RundeckHistory> {
private final String project;
private String jobId;
private String reportId;
private String user;
private String recent;
private Date beginAt;
private Date end;
private Long max;
private Long offset;
public HistoryRequest(RundeckClient client, String project) {
super(client);
this.project = project;
AssertUtil.notBlank(project, "project is mandatory to get the history !");
}
public HistoryRequest beginAt(Date beginAt) {
this.beginAt = beginAt;
return this;
}
public HistoryRequest max(Long max) {
this.max = max;
return this;
}
public HistoryRequest offset(Long offset) {
this.offset = offset;
return this;
}
@Override
public RundeckHistory execute() {
return get(new ApiPathBuilder("/history").param("project", project)
.param("jobIdFilter", jobId)
.param("reportIdFilter", reportId)
.param("userFilter", user)
.param("recentFilter", recent)
.param("begin", beginAt)
.param("end", end)
.param("max", max)
.param("offset", offset), new HistoryParser("result/events"));
}
}

View file

@ -0,0 +1,72 @@
/*
* Copyright 2011 Vincent Behar
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.rundeck.api.request;
import java.util.concurrent.TimeUnit;
import org.rundeck.api.RundeckClient;
import org.rundeck.api.domain.RundeckExecution;
import org.rundeck.api.domain.RundeckExecution.ExecutionStatus;
/**
* TODO
*
* @author Vincent Behar
*/
public class JobRunRequest extends JobTriggerRequest<JobRunRequest> {
/** Default value for the "pooling interval" used when running jobs/commands/scripts */
public static final transient long DEFAULT_POOLING_INTERVAL = 5;
/** Default unit of the "pooling interval" used when running jobs/commands/scripts */
public static final transient TimeUnit DEFAULT_POOLING_UNIT = TimeUnit.SECONDS;
private Long poolingInterval;
private TimeUnit poolingUnit;
public JobRunRequest(RundeckClient client, String project) {
super(client, project);
}
public JobRunRequest poolingInterval(Long poolingInterval, TimeUnit poolingUnit) {
this.poolingInterval = poolingInterval;
this.poolingUnit = poolingUnit;
return this;
}
@Override
public RundeckExecution execute() {
if (poolingInterval <= 0) {
poolingInterval = DEFAULT_POOLING_INTERVAL;
poolingUnit = DEFAULT_POOLING_UNIT;
}
if (poolingUnit == null) {
poolingUnit = DEFAULT_POOLING_UNIT;
}
RundeckExecution execution = super.execute();
while (ExecutionStatus.RUNNING.equals(execution.getStatus())) {
try {
Thread.sleep(poolingUnit.toMillis(poolingInterval));
} catch (InterruptedException e) {
break;
}
execution = client.getExecution(execution.getId());
}
return execution;
}
}

View file

@ -0,0 +1,64 @@
/*
* Copyright 2011 Vincent Behar
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.rundeck.api.request;
import java.util.Properties;
import org.rundeck.api.RundeckClient;
import org.rundeck.api.domain.RundeckExecution;
import org.rundeck.api.parser.ExecutionParser;
import org.rundeck.api.util.AssertUtil;
import org.rundeck.api.util.ParametersUtil;
/**
* TODO
*
* @author Vincent Behar
*/
public class JobTriggerRequest<T extends JobTriggerRequest<?>> extends ApiRequest<RundeckExecution> {
private final String jobId;
private Properties options;
private Properties nodeFilters;
public JobTriggerRequest(RundeckClient client, String jobId) {
super(client);
this.jobId = jobId;
AssertUtil.notBlank(jobId, "jobId is mandatory !");
}
@SuppressWarnings("unchecked")
public T addOptions(Properties options) {
this.options = options;
return (T) this;
}
@SuppressWarnings("unchecked")
public T filterNodes(Properties nodeFilters) {
this.nodeFilters = nodeFilters;
return (T) this;
}
@Override
public RundeckExecution execute() {
return get(new ApiPathBuilder("/job/", jobId, "/run").param("argString",
ParametersUtil.generateArgString(options))
.nodeFilters(nodeFilters),
new ExecutionParser("result/executions/execution"));
}
}

View file

@ -0,0 +1,53 @@
/*
* Copyright 2011 Vincent Behar
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.rundeck.api.request;
import java.util.List;
import org.rundeck.api.RundeckClient;
import org.rundeck.api.domain.RundeckJob;
import org.rundeck.api.parser.JobParser;
import org.rundeck.api.parser.ListParser;
import org.rundeck.api.util.AssertUtil;
/**
* TODO
*
* @author Vincent Behar
*/
public class JobsListingRequest extends ApiRequest<List<RundeckJob>> {
private final String project;
private String jobFilter;
public JobsListingRequest(RundeckClient client, String project) {
super(client);
this.project = project;
AssertUtil.notBlank(project, "project is mandatory to get all jobs !");
}
public JobsListingRequest jobFilter(String jobFilter) {
this.jobFilter = jobFilter;
return this;
}
@Override
public List<RundeckJob> execute() {
return get(new ApiPathBuilder("/jobs").param("project", project).param("jobFilter", jobFilter),
new ListParser<RundeckJob>(new JobParser(), "result/jobs/job"));
}
}

View file

@ -0,0 +1,37 @@
/*
* Copyright 2011 Vincent Behar
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.rundeck.api.request;
import org.rundeck.api.RundeckClient;
/**
* TODO
*
* @author Vincent Behar
*/
public class PingRequest extends ApiRequest<Void> {
public PingRequest(RundeckClient client) {
super(client);
}
@Override
public Void execute() {
ping();
return null;
}
}

View file

@ -0,0 +1,43 @@
/*
* Copyright 2011 Vincent Behar
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.rundeck.api.request;
import org.rundeck.api.RundeckClient;
import org.rundeck.api.domain.RundeckProject;
import org.rundeck.api.parser.ProjectParser;
import org.rundeck.api.util.AssertUtil;
/**
* TODO
*
* @author Vincent Behar
*/
public class ProjectDetailsRequest extends ApiRequest<RundeckProject> {
private final String projectName;
public ProjectDetailsRequest(RundeckClient client, String projectName) {
super(client);
this.projectName = projectName;
AssertUtil.notBlank(projectName, "projectName is mandatory to get the details of a project !");
}
@Override
public RundeckProject execute() {
return get(new ApiPathBuilder("/project/", projectName), new ProjectParser("result/projects/project"));
}
}

View file

@ -0,0 +1,41 @@
/*
* Copyright 2011 Vincent Behar
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.rundeck.api.request;
import java.util.List;
import org.rundeck.api.RundeckClient;
import org.rundeck.api.domain.RundeckProject;
import org.rundeck.api.parser.ListParser;
import org.rundeck.api.parser.ProjectParser;
/**
* TODO
*
* @author Vincent Behar
*/
public class ProjectsListingRequest extends ApiRequest<List<RundeckProject>> {
public ProjectsListingRequest(RundeckClient client) {
super(client);
}
@Override
public List<RundeckProject> execute() {
return get(new ApiPathBuilder("/projects"), new ListParser<RundeckProject>(new ProjectParser(),
"result/projects/project"));
}
}