@@ -45,42 +25,7 @@
*/
public class RallyRestApi implements Closeable {
- private enum Header {
- Library,
- Name,
- Vendor,
- Version
- }
-
- /**
- * The default version of the WSAPI to target.
- */
- public static final String DEFAULT_WSAPI_VERSION = "v2.0";
- public static final String CREATE_RESULT_KEY = "CreateResult";
- public static final String OPERATION_RESULT_KEY = "OperationResult";
- public static final String QUERY_RESULT_KEY = "QueryResult";
-
- protected static final String SECURITY_ENDPOINT_DOES_NOT_EXIST = "SECURITY_ENDPOINT_DOES_NOT_EXIST";
- protected static final String SECURITY_TOKEN_PARAM_KEY = "key";
- private static final String SECURITY_TOKEN_URL = "/security/authorize";
- protected static final String SECURITY_TOKEN_KEY = "SecurityToken";
-
- protected URI server;
- protected URI proxy;
-
- protected String wsapiVersion = DEFAULT_WSAPI_VERSION;
- protected DefaultHttpClient httpClient;
- private UsernamePasswordCredentials usernamePasswordCredentials;
- private String securityToken;
-
- protected Map headers = new HashMap() {
- {
- put(Header.Library, "Rally Rest API for Java v2.0.3");
- put(Header.Name, "Rally Rest API for Java");
- put(Header.Vendor, "Rally Software, Inc.");
- put(Header.Version, "2.0.3");
- }
- };
+ protected HttpClient client;
/**
* Creates a new instance for the specified server using the specified credentials.
@@ -88,11 +33,24 @@ private enum Header {
* @param server The server to connect to, e.g. {@code new URI("https://rally1.rallydev.com")}
* @param userName The username to be used for authentication.
* @param password The password to be used for authentication.
+ * @deprecated Use the api key constructor instead.
*/
public RallyRestApi(URI server, String userName, String password) {
- this.server = server;
- httpClient = new DefaultHttpClient();
- setClientCredentials(server, userName, password);
+ this(new BasicAuthClient(server, userName, password));
+ }
+
+ /**
+ * Creates a new instance for the specified server using the specified API Key.
+ *
+ * @param server The server to connect to, e.g. {@code new URI("https://rally1.rallydev.com")}
+ * @param apiKey The API Key to be used for authentication.
+ */
+ public RallyRestApi(URI server, String apiKey) {
+ this(new ApiKeyClient(server, apiKey));
+ }
+
+ protected RallyRestApi(HttpClient httpClient) {
+ this.client = httpClient;
}
/**
@@ -101,8 +59,7 @@ public RallyRestApi(URI server, String userName, String password) {
* @param proxy The proxy server, e.g. {@code new URI("http://my.proxy.com:8000")}
*/
public void setProxy(URI proxy) {
- this.proxy = proxy;
- httpClient.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY, new HttpHost(proxy.getHost(), proxy.getPort(), proxy.getScheme()));
+ client.setProxy(proxy);
}
/**
@@ -113,15 +70,7 @@ public void setProxy(URI proxy) {
* @param password The password to be used for authentication.
*/
public void setProxy(URI proxy, String userName, String password) {
- setProxy(proxy);
- setClientCredentials(proxy, userName, password);
- }
-
- private void setClientCredentials(URI server, String userName, String password) {
- usernamePasswordCredentials = new UsernamePasswordCredentials(userName, password);
- httpClient.getCredentialsProvider().setCredentials(
- new AuthScope(server.getHost(), server.getPort()),
- usernamePasswordCredentials);
+ client.setProxy(proxy, userName, password);
}
/**
@@ -131,7 +80,7 @@ private void setClientCredentials(URI server, String userName, String password)
* @param value The vendor header to be included on all requests.
*/
public void setApplicationVendor(String value) {
- headers.put(Header.Vendor, value);
+ client.setApplicationVendor(value);
}
/**
@@ -141,7 +90,7 @@ public void setApplicationVendor(String value) {
* @param value The vendor header to be included on all requests.
*/
public void setApplicationVersion(String value) {
- headers.put(Header.Version, value);
+ client.setApplicationVersion(value);
}
/**
@@ -151,7 +100,7 @@ public void setApplicationVersion(String value) {
* @param value The vendor header to be included on all requests.
*/
public void setApplicationName(String value) {
- headers.put(Header.Name, value);
+ client.setApplicationName(value);
}
/**
@@ -162,16 +111,7 @@ public void setApplicationName(String value) {
* @throws IOException if an error occurs during the creation.
*/
public CreateResponse create(CreateRequest request) throws IOException {
- return create(request, true);
- }
-
- private CreateResponse create(CreateRequest request, boolean retryOnFail) throws IOException {
- CreateResponse createResponse = new CreateResponse(doPost(buildWsapiUrl() + request.toUrl(), request.getBody()));
- if (retryOnFail && createResponse.getErrors().length != 0) {
- setSecurityToken(null);
- return create(request, false);
- }
- return createResponse;
+ return new CreateResponse(client.doPost(request.toUrl(), request.getBody()));
}
/**
@@ -182,16 +122,7 @@ private CreateResponse create(CreateRequest request, boolean retryOnFail) throws
* @throws IOException if an error occurs during the update.
*/
public UpdateResponse update(UpdateRequest request) throws IOException {
- return update(request, true);
- }
-
- private UpdateResponse update(UpdateRequest request, boolean retryOnFail) throws IOException {
- UpdateResponse updateResponse = new UpdateResponse(doPost(buildWsapiUrl() + request.toUrl(), request.getBody()));
- if (retryOnFail && updateResponse.getErrors().length != 0) {
- setSecurityToken(null);
- return update(request, false);
- }
- return updateResponse;
+ return new UpdateResponse(client.doPost(request.toUrl(), request.getBody()));
}
/**
@@ -202,16 +133,7 @@ private UpdateResponse update(UpdateRequest request, boolean retryOnFail) throws
* @throws IOException if an error occurs during the deletion.
*/
public DeleteResponse delete(DeleteRequest request) throws IOException {
- return delete(request, true);
- }
-
- private DeleteResponse delete(DeleteRequest request, boolean retryOnFail) throws IOException {
- DeleteResponse deleteResponse = new DeleteResponse(doDelete(buildWsapiUrl() + request.toUrl()));
- if (retryOnFail && deleteResponse.getErrors().length != 0) {
- setSecurityToken(null);
- return delete(request, false);
- }
- return deleteResponse;
+ return new DeleteResponse(client.doDelete(request.toUrl()));
}
/**
@@ -224,14 +146,14 @@ private DeleteResponse delete(DeleteRequest request, boolean retryOnFail) throws
* @throws IOException if an error occurs during the query.
*/
public QueryResponse query(QueryRequest request) throws IOException {
- QueryResponse queryResponse = new QueryResponse(doGet(buildWsapiUrl() + request.toUrl(), false));
+ QueryResponse queryResponse = new QueryResponse(client.doGet(request.toUrl()));
if (queryResponse.wasSuccessful()) {
int receivedRecords = request.getPageSize();
while (receivedRecords < request.getLimit() &&
(receivedRecords + request.getStart() - 1) < queryResponse.getTotalResultCount()) {
QueryRequest pageRequest = request.clone();
pageRequest.setStart(receivedRecords + request.getStart());
- QueryResponse pageResponse = new QueryResponse(doGet(buildWsapiUrl() + pageRequest.toUrl(), false));
+ QueryResponse pageResponse = new QueryResponse(client.doGet(pageRequest.toUrl()));
if (pageResponse.wasSuccessful()) {
JsonArray results = queryResponse.getResults();
results.addAll(pageResponse.getResults());
@@ -251,19 +173,7 @@ public QueryResponse query(QueryRequest request) throws IOException {
* @throws IOException if an error occurs during the retrieval.
*/
public GetResponse get(GetRequest request) throws IOException {
- return get(request, false);
- }
-
- /**
- * Get the specified object. Forces basic auth as part of the request if specified
- *
- * @param request the {@link GetRequest} specifying the object to be retrieved.
- * @param forceReauth boolean indicating if basic auth should be conducted as part of the request
- * @return the resulting {@link GetResponse}
- * @throws IOException if an error occurs during the retrieval.
- */
- protected GetResponse get(GetRequest request, boolean forceReauth) throws IOException {
- return new GetResponse(doGet(buildWsapiUrl() + request.toUrl(), forceReauth));
+ return new GetResponse(client.doGet(request.toUrl()));
}
/**
@@ -272,200 +182,17 @@ protected GetResponse get(GetRequest request, boolean forceReauth) throws IOExce
* @throws IOException if an error occurs releasing resources
*/
public void close() throws IOException {
- httpClient.getConnectionManager().shutdown();
- }
-
- /**
- * Get the current version of the WSAPI being targeted.
- * Defaults to {@link RallyRestApi#DEFAULT_WSAPI_VERSION}
- *
- * @return the current WSAPI version.
- */
- public String getWsapiVersion() {
- return wsapiVersion;
- }
-
- /**
- * Set the current version of the WSAPI being targeted.
- *
- * @param wsapiVersion the new version, e.g. {@code "1.30"}
- */
- public void setWsapiVersion(String wsapiVersion) {
- this.wsapiVersion = wsapiVersion;
- }
-
- /**
- * Execute a request against the WSAPI
- *
- * @param request the request to be executed
- * @return the JSON encoded string response
- * @throws IOException if a non-200 response code is returned or if some other
- * problem occurs while executing the request
- */
- protected String doRequest(HttpRequestBase request) throws IOException {
- for (Map.Entry header : headers.entrySet()) {
- request.setHeader("X-RallyIntegration" + header.getKey().name(), header.getValue());
- }
-
- HttpResponse response = httpClient.execute(request);
- HttpEntity entity = response.getEntity();
- if (response.getStatusLine().getStatusCode() == 200) {
- return EntityUtils.toString(entity, "utf-8");
- } else if (response.getStatusLine().getStatusCode() == 500) {
- ((BasicManagedEntity) entity).releaseConnection();
- throw new InvalidURLException(request.getURI().toString() + " is an Invalid URL");
- } else {
- throw new IOException(response.getStatusLine().toString());
- }
- }
-
- /**
- * Execute a request against the WSAPI with a basic auth header to force reauthorization
- *
- * @param request the request to be executed
- * @return the JSON encoded string response
- * @throws IOException if a non-200 response code is returned or if some other
- * problem occurs while executing the request
- */
- protected String doForceReauthRequest(HttpRequestBase request) throws IOException {
- request.addHeader(BasicScheme.authenticate(usernamePasswordCredentials, "UTF-8", false));
- return doRequest(request);
- }
-
- /**
- * Execute a request against WSAPI that requires a security token due to data change/creation:
- * 1. Post
- * 2. Put
- * 3. Delete
- *
- * @param request the request to be executed
- * @return the JSON encoded string response
- * @throws IOException if a non-200 response code is returned or if some other
- * problem occurs while executing the request
- */
- private String doSecuredRequest(HttpRequestBase request) throws IOException {
- if (!request.getMethod().equals(HttpGet.METHOD_NAME) &&
- !this.getWsapiVersion().matches("^1[.]\\d+")) {
- try {
- attachSecurityInfo(request);
- } catch (URISyntaxException e) {
- throw new IOException("Unable to build URI with security token", e);
- }
- }
-
- return doRequest(request);
- }
-
- /**
- * Attach the security token parameter to the request.
- *
- * Response Structure:
- * {"OperationResult": {"SecurityToken": "UUID"}}
- *
- * @param request the request to be modified
- * @throws IOException if a non-200 response code is returned or if some other
- * problem occurs while executing the request
- */
- protected void attachSecurityInfo(HttpRequestBase request) throws IOException, URISyntaxException {
- if (!SECURITY_ENDPOINT_DOES_NOT_EXIST.equals(securityToken)) {
- try {
- if (securityToken == null) {
- GetResponse getResponse = get(new GetRequest(SECURITY_TOKEN_URL), true);
- JsonObject operationResult = getResponse.getObject();
- JsonPrimitive securityTokenPrimitive = operationResult.getAsJsonPrimitive(SECURITY_TOKEN_KEY);
- securityToken = securityTokenPrimitive.getAsString();
- }
- request.setURI(new URIBuilder(request.getURI()).addParameter(SECURITY_TOKEN_PARAM_KEY, securityToken).build());
- } catch (InvalidURLException e) {
- //swallow the exception in this case as url does not exist indicates running and old version of
- //ALM without the security endpoint
- securityToken = SECURITY_ENDPOINT_DOES_NOT_EXIST;
- }
- }
- }
-
- /**
- * Sets the value on the security token for security enabled requests
- *
- * @param securityToken value fo the security token
- */
- protected void setSecurityToken(String securityToken) {
- this.securityToken = securityToken;
- }
-
- /**
- * Returns the current value of the security token.
- *
- * @return string value of security token
- */
- protected String getSecurityToken() {
- return securityToken;
- }
-
- /**
- * Perform a post against the WSAPI
- *
- * @param url the request url
- * @param body the body of the post
- * @return the JSON encoded string response
- * @throws IOException if a non-200 response code is returned or if some other
- * problem occurs while executing the request
- */
- protected String doPost(String url, String body) throws IOException {
- HttpPost httpPost = new HttpPost(url);
- httpPost.setEntity(new StringEntity(body, "utf-8"));
- return doSecuredRequest(httpPost);
- }
-
-
- /**
- * Perform a put against the WSAPI
- *
- * @param url the request url
- * @param body the body of the put
- * @return the JSON encoded string response
- * @throws IOException if a non-200 response code is returned or if some other
- * problem occurs while executing the request
- */
- protected String doPut(String url, String body) throws IOException {
- HttpPut httpPut = new HttpPut(url);
- httpPut.setEntity(new StringEntity(body, "utf-8"));
- return doSecuredRequest(httpPut);
- }
-
- /**
- * Perform a delete against the WSAPI
- *
- * @param url the request url
- * @return the JSON encoded string response
- * @throws IOException if a non-200 response code is returned or if some other
- * problem occurs while executing the request
- */
- protected String doDelete(String url) throws IOException {
- HttpDelete httpDelete = new HttpDelete(url);
- return doSecuredRequest(httpDelete);
- }
-
- /**
- * Perform a get against the WSAPI
- *
- * @param url the request url
- * @param forceReauth
- * @return the JSON encoded string response
- * @throws IOException if a non-200 response code is returned or if some other
- * problem occurs while executing the request
- */
- protected String doGet(String url, boolean forceReauth) throws IOException {
- HttpGet httpGet = new HttpGet(url);
- return forceReauth ? doForceReauthRequest(httpGet) : doRequest(httpGet);
+ client.close();
}
/**
- * Get the WSAPI base url based on the current server and WSAPI version
+ * Get the underlying http client implementation.
+ * This is exposed with the intent of providing the ability to supply additional configuration to the client.
+ * It should not be used to directly make i/o calls.
*
- * @return the fully qualified WSAPI base url, e.g. https://rally1.rallydev.com/slm/webservice/1.33
+ * @return the raw http client
*/
- protected String buildWsapiUrl() {
- return server.toString() + "/slm/webservice/" + wsapiVersion;
+ public HttpClient getClient() {
+ return client;
}
}
\ No newline at end of file
diff --git a/src/main/java/com/rallydev/rest/client/ApiKeyClient.java b/src/main/java/com/rallydev/rest/client/ApiKeyClient.java
new file mode 100644
index 0000000..615047f
--- /dev/null
+++ b/src/main/java/com/rallydev/rest/client/ApiKeyClient.java
@@ -0,0 +1,31 @@
+package com.rallydev.rest.client;
+
+import org.apache.http.client.methods.HttpRequestBase;
+
+import java.io.IOException;
+import java.net.URI;
+
+public class ApiKeyClient extends HttpClient {
+
+ protected String apiKey;
+ protected static final String API_KEY_HEADER = "zsessionid";
+
+ public ApiKeyClient(URI server, String apiKey) {
+ super(server);
+ this.apiKey = apiKey;
+ }
+
+ /**
+ * Execute a request against the WSAPI
+ *
+ * @param request the request to be executed
+ * @return the JSON encoded string response
+ * @throws java.io.IOException if a non-200 response code is returned or if some other
+ * problem occurs while executing the request
+ */
+ @Override
+ protected String doRequest(HttpRequestBase request) throws IOException {
+ request.setHeader(API_KEY_HEADER, this.apiKey);
+ return super.doRequest(request);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/rallydev/rest/client/BasicAuthClient.java b/src/main/java/com/rallydev/rest/client/BasicAuthClient.java
new file mode 100644
index 0000000..4c20252
--- /dev/null
+++ b/src/main/java/com/rallydev/rest/client/BasicAuthClient.java
@@ -0,0 +1,80 @@
+package com.rallydev.rest.client;
+
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+import com.rallydev.rest.response.GetResponse;
+import com.rallydev.rest.util.InvalidURLException;
+import org.apache.http.auth.Credentials;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpRequestBase;
+import org.apache.http.client.utils.URIBuilder;
+import org.apache.http.impl.auth.BasicScheme;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+public class BasicAuthClient extends HttpClient {
+
+ protected static final String SECURITY_ENDPOINT_DOES_NOT_EXIST = "SECURITY_ENDPOINT_DOES_NOT_EXIST";
+ protected static final String SECURITY_TOKEN_PARAM_KEY = "key";
+ private static final String SECURITY_TOKEN_URL = "/security/authorize";
+ protected static final String SECURITY_TOKEN_KEY = "SecurityToken";
+ protected String securityToken;
+ private Credentials credentials;
+
+ public BasicAuthClient(URI server, String userName, String password) {
+ super(server);
+ credentials = setClientCredentials(server, userName, password);
+ }
+
+ /**
+ * Execute a request against the WSAPI
+ *
+ * @param request the request to be executed
+ * @return the JSON encoded string response
+ * @throws java.io.IOException if a non-200 response code is returned or if some other
+ * problem occurs while executing the request
+ */
+ @Override
+ protected String doRequest(HttpRequestBase request) throws IOException {
+ if(!request.getMethod().equals(HttpGet.METHOD_NAME) &&
+ !this.getWsapiVersion().matches("^1[.]\\d+")) {
+ try {
+ attachSecurityInfo(request);
+ } catch (URISyntaxException e) {
+ throw new IOException("Unable to build URI with security token", e);
+ }
+ }
+ return super.doRequest(request);
+ }
+
+ /**
+ * Attach the security token parameter to the request.
+ *
+ * Response Structure:
+ * {"OperationResult": {"SecurityToken": "UUID"}}
+ *
+ * @param request the request to be modified
+ * @throws IOException if a non-200 response code is returned or if some other
+ * problem occurs while executing the request
+ */
+ protected void attachSecurityInfo(HttpRequestBase request) throws IOException, URISyntaxException {
+ if (!SECURITY_ENDPOINT_DOES_NOT_EXIST.equals(securityToken)) {
+ try {
+ if (securityToken == null) {
+ HttpGet httpGet = new HttpGet(getWsapiUrl() + SECURITY_TOKEN_URL);
+ httpGet.addHeader(BasicScheme.authenticate(credentials, "utf-8", false));
+ GetResponse getResponse = new GetResponse(doRequest(httpGet));
+ JsonObject operationResult = getResponse.getObject();
+ JsonPrimitive securityTokenPrimitive = operationResult.getAsJsonPrimitive(SECURITY_TOKEN_KEY);
+ securityToken = securityTokenPrimitive.getAsString();
+ }
+ request.setURI(new URIBuilder(request.getURI()).addParameter(SECURITY_TOKEN_PARAM_KEY, securityToken).build());
+ } catch (InvalidURLException e) {
+ //swallow the exception in this case as url does not exist indicates running and old version of
+ //ALM without the security endpoint
+ securityToken = SECURITY_ENDPOINT_DOES_NOT_EXIST;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/rallydev/rest/client/HttpClient.java b/src/main/java/com/rallydev/rest/client/HttpClient.java
new file mode 100644
index 0000000..31f3241
--- /dev/null
+++ b/src/main/java/com/rallydev/rest/client/HttpClient.java
@@ -0,0 +1,244 @@
+package com.rallydev.rest.client;
+
+import com.rallydev.rest.util.InvalidURLException;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpResponse;
+import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.Credentials;
+import org.apache.http.auth.UsernamePasswordCredentials;
+import org.apache.http.client.methods.*;
+import org.apache.http.conn.BasicManagedEntity;
+import org.apache.http.conn.params.ConnRoutePNames;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.util.EntityUtils;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.net.URI;
+import java.util.HashMap;
+import java.util.Map;
+
+public abstract class HttpClient extends DefaultHttpClient
+ implements Closeable {
+
+ protected URI server;
+ protected String wsapiVersion = "v2.0";
+
+ private enum Header {
+ Library,
+ Name,
+ Vendor,
+ Version
+ }
+
+ private Map headers = new HashMap() {
+ {
+ put(Header.Library, "Rally Rest API for Java v2.1.0");
+ put(Header.Name, "Rally Rest API for Java");
+ put(Header.Vendor, "Rally Software, Inc.");
+ put(Header.Version, "2.1.0");
+ }
+ };
+
+ protected HttpClient(URI server) {
+ this.server = server;
+ }
+
+ /**
+ * Set the unauthenticated proxy server to use. By default no proxy is configured.
+ *
+ * @param proxy The proxy server, e.g. {@code new URI("http://my.proxy.com:8000")}
+ */
+ public void setProxy(URI proxy) {
+ this.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY, new HttpHost(proxy.getHost(), proxy.getPort(), proxy.getScheme()));
+ }
+
+ /**
+ * Set the authenticated proxy server to use. By default no proxy is configured.
+ *
+ * @param proxy The proxy server, e.g. {@code new URI("http://my.proxy.com:8000")}
+ * @param userName The username to be used for authentication.
+ * @param password The password to be used for authentication.
+ */
+ public void setProxy(URI proxy, String userName, String password) {
+ setProxy(proxy);
+ setClientCredentials(proxy, userName, password);
+ }
+
+ /**
+ * Set the value of the X-RallyIntegrationVendor header included on all requests.
+ * This should be set to your company name.
+ *
+ * @param value The vendor header to be included on all requests.
+ */
+ public void setApplicationVendor(String value) {
+ headers.put(Header.Vendor, value);
+ }
+
+ /**
+ * Set the value of the X-RallyIntegrationVersion header included on all requests.
+ * This should be set to the version of your application.
+ *
+ * @param value The vendor header to be included on all requests.
+ */
+ public void setApplicationVersion(String value) {
+ headers.put(Header.Version, value);
+ }
+
+ /**
+ * Set the value of the X-RallyIntegrationName header included on all requests.
+ * This should be set to the name of your application.
+ *
+ * @param value The vendor header to be included on all requests.
+ */
+ public void setApplicationName(String value) {
+ headers.put(Header.Name, value);
+ }
+
+ /**
+ * Get the current server being targeted.
+ *
+ * @return the current server.
+ */
+ public String getServer() {
+ return server.toString();
+ }
+
+ /**
+ * Get the current version of the WSAPI being targeted.
+ *
+ * @return the current WSAPI version.
+ */
+ public String getWsapiVersion() {
+ return wsapiVersion;
+ }
+
+ /**
+ * Set the current version of the WSAPI being targeted.
+ *
+ * @param wsapiVersion the new version, e.g. {@code "1.30"}
+ */
+ public void setWsapiVersion(String wsapiVersion) {
+ this.wsapiVersion = wsapiVersion;
+ }
+
+ /**
+ * Execute a request against the WSAPI
+ *
+ * @param request the request to be executed
+ * @return the JSON encoded string response
+ * @throws IOException if a non-200 response code is returned or if some other
+ * problem occurs while executing the request
+ */
+ protected String doRequest(HttpRequestBase request) throws IOException {
+ for (Map.Entry header : headers.entrySet()) {
+ request.setHeader("X-RallyIntegration" + header.getKey().name(), header.getValue());
+ }
+
+ return this.executeRequest(request);
+ }
+
+ /**
+ * Execute a request against the WSAPI
+ *
+ * @param request the request to be executed
+ * @return the JSON encoded string response
+ * @throws IOException if a non-200 response code is returned or if some other
+ * problem occurs while executing the request
+ */
+ protected String executeRequest(HttpRequestBase request) throws IOException {
+ HttpResponse response = this.execute(request);
+ HttpEntity entity = response.getEntity();
+ if (response.getStatusLine().getStatusCode() == 200) {
+ return EntityUtils.toString(entity, "utf-8");
+ } else if (response.getStatusLine().getStatusCode() == 500) {
+ ((BasicManagedEntity) entity).releaseConnection();
+ throw new InvalidURLException(request.getURI().toString() + " is an Invalid URL");
+ } else {
+ throw new IOException(response.getStatusLine().toString());
+ }
+ }
+
+ /**
+ * Perform a post against the WSAPI
+ *
+ * @param url the request url
+ * @param body the body of the post
+ * @return the JSON encoded string response
+ * @throws IOException if a non-200 response code is returned or if some other
+ * problem occurs while executing the request
+ */
+ public String doPost(String url, String body) throws IOException {
+ HttpPost httpPost = new HttpPost(getWsapiUrl() + url);
+ httpPost.setEntity(new StringEntity(body, "utf-8"));
+ return doRequest(httpPost);
+ }
+
+
+ /**
+ * Perform a put against the WSAPI
+ *
+ * @param url the request url
+ * @param body the body of the put
+ * @return the JSON encoded string response
+ * @throws IOException if a non-200 response code is returned or if some other
+ * problem occurs while executing the request
+ */
+ public String doPut(String url, String body) throws IOException {
+ HttpPut httpPut = new HttpPut(getWsapiUrl() + url);
+ httpPut.setEntity(new StringEntity(body, "utf-8"));
+ return doRequest(httpPut);
+ }
+
+ /**
+ * Perform a delete against the WSAPI
+ *
+ * @param url the request url
+ * @return the JSON encoded string response
+ * @throws IOException if a non-200 response code is returned or if some other
+ * problem occurs while executing the request
+ */
+ public String doDelete(String url) throws IOException {
+ HttpDelete httpDelete = new HttpDelete(getWsapiUrl() + url);
+ return doRequest(httpDelete);
+ }
+
+ /**
+ * Perform a get against the WSAPI
+ *
+ * @param url the request url
+ * @return the JSON encoded string response
+ * @throws IOException if a non-200 response code is returned or if some other
+ * problem occurs while executing the request
+ */
+ public String doGet(String url) throws IOException {
+ HttpGet httpGet = new HttpGet(getWsapiUrl() + url);
+ return doRequest(httpGet);
+ }
+
+ /**
+ * Release all resources associated with this instance.
+ *
+ * @throws IOException if an error occurs releasing resources
+ */
+ public void close() throws IOException {
+ this.getConnectionManager().shutdown();
+ }
+
+ protected Credentials setClientCredentials(URI server, String userName, String password) {
+ UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(userName, password);
+ this.getCredentialsProvider().setCredentials(new AuthScope(server.getHost(), server.getPort()), credentials);
+ return credentials;
+ }
+
+ /**
+ * Get the WSAPI base url based on the current server and WSAPI version
+ *
+ * @return the fully qualified WSAPI base url, e.g. https://rally1.rallydev.com/slm/webservice/1.33
+ */
+ public String getWsapiUrl() {
+ return getServer() + "/slm/webservice/" + getWsapiVersion();
+ }
+}
diff --git a/src/main/java/com/rallydev/rest/client/package-info.java b/src/main/java/com/rallydev/rest/client/package-info.java
new file mode 100644
index 0000000..2b38cbd
--- /dev/null
+++ b/src/main/java/com/rallydev/rest/client/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Provides the underlying http client implementations.
+ */
+package com.rallydev.rest.client;
\ No newline at end of file
diff --git a/src/main/java/com/rallydev/rest/response/CreateResponse.java b/src/main/java/com/rallydev/rest/response/CreateResponse.java
index 373797a..1a60045 100644
--- a/src/main/java/com/rallydev/rest/response/CreateResponse.java
+++ b/src/main/java/com/rallydev/rest/response/CreateResponse.java
@@ -4,13 +4,13 @@
import com.rallydev.rest.RallyRestApi;
/**
- * Represents a WSAPI response from creating an object
+ * Represents a WSAPI response from creating an object
*/
public class CreateResponse extends Response {
/**
* Create a new create response from the specified JSON encoded string.
- *
+ *
* @param createResponse the JSON encoded string
*/
public CreateResponse(String createResponse) {
@@ -19,17 +19,17 @@ public CreateResponse(String createResponse) {
/**
* Get the name of the root JSON result
- *
+ *
* @return the root element name
*/
@Override
protected String getRoot() {
- return RallyRestApi.CREATE_RESULT_KEY;
+ return "CreateResult";
}
/**
* Get the created object.
- *
Returns null if the operation was not successful
+ *
Returns null if the operation was not successful
* @return the created object
*/
public JsonObject getObject() {
diff --git a/src/main/java/com/rallydev/rest/response/DeleteResponse.java b/src/main/java/com/rallydev/rest/response/DeleteResponse.java
index a4e98c7..477efff 100644
--- a/src/main/java/com/rallydev/rest/response/DeleteResponse.java
+++ b/src/main/java/com/rallydev/rest/response/DeleteResponse.java
@@ -1,7 +1,5 @@
package com.rallydev.rest.response;
-import static com.rallydev.rest.RallyRestApi.OPERATION_RESULT_KEY;
-
/**
* Represents a WSAPI response from deleting an object
*/
@@ -23,6 +21,6 @@ public DeleteResponse(String deleteResponse) {
*/
@Override
protected String getRoot() {
- return OPERATION_RESULT_KEY;
+ return "OperationResult";
}
}
diff --git a/src/main/java/com/rallydev/rest/response/QueryResponse.java b/src/main/java/com/rallydev/rest/response/QueryResponse.java
index 8dfecd3..148f92e 100644
--- a/src/main/java/com/rallydev/rest/response/QueryResponse.java
+++ b/src/main/java/com/rallydev/rest/response/QueryResponse.java
@@ -2,8 +2,6 @@
import com.google.gson.JsonArray;
-import static com.rallydev.rest.RallyRestApi.QUERY_RESULT_KEY;
-
/**
* Represents a WSAPI response from querying for objects.
*/
@@ -25,12 +23,12 @@ public QueryResponse(String queryResponse) {
*/
@Override
protected String getRoot() {
- return QUERY_RESULT_KEY;
+ return "QueryResult";
}
/**
* Get the total number of objects that matched the query
- *
+ *
* @return the total number of objects
*/
public int getTotalResultCount() {
@@ -40,7 +38,7 @@ public int getTotalResultCount() {
/**
* Get the results of the query
*
Depending on the limit of the original request this may include one or more pages.
- *
+ *
* @return the results
*/
public JsonArray getResults() {
@@ -49,7 +47,7 @@ public JsonArray getResults() {
/**
* Get the page size of the results
- *
+ *
* @return the page size
*/
public int getPageSize() {
@@ -58,7 +56,7 @@ public int getPageSize() {
/**
* Get the start index of the results
- *
+ *
* @return the start index
*/
public int getStart() {
diff --git a/src/main/java/com/rallydev/rest/response/UpdateResponse.java b/src/main/java/com/rallydev/rest/response/UpdateResponse.java
index df4043e..0e12292 100644
--- a/src/main/java/com/rallydev/rest/response/UpdateResponse.java
+++ b/src/main/java/com/rallydev/rest/response/UpdateResponse.java
@@ -2,8 +2,6 @@
import com.google.gson.JsonObject;
-import static com.rallydev.rest.RallyRestApi.OPERATION_RESULT_KEY;
-
/**
* Represents a WSAPI response from updating an object.
*/
@@ -25,12 +23,12 @@ public UpdateResponse(String updateResponse) {
*/
@Override
protected String getRoot() {
- return OPERATION_RESULT_KEY;
+ return "OperationResult";
}
/**
* Get the updated object.
- *
Returns null if the operation was not successful
+ *
Returns null if the operation was not successful
* @return the updated object
*/
public JsonObject getObject() {
diff --git a/src/main/resources/doc/allclasses-frame.html b/src/main/resources/doc/allclasses-frame.html
index 95c9880..c533fd9 100644
--- a/src/main/resources/doc/allclasses-frame.html
+++ b/src/main/resources/doc/allclasses-frame.html
@@ -2,9 +2,9 @@
-
+
All Classes (Rally Rest API for Java 2.0)
-
+
diff --git a/src/main/resources/doc/allclasses-noframe.html b/src/main/resources/doc/allclasses-noframe.html
index 28857f4..a8998b0 100644
--- a/src/main/resources/doc/allclasses-noframe.html
+++ b/src/main/resources/doc/allclasses-noframe.html
@@ -2,9 +2,9 @@
-
+
All Classes (Rally Rest API for Java 2.0)
-
+
diff --git a/src/main/resources/doc/com/rallydev/rest/RallyRestApi.html b/src/main/resources/doc/com/rallydev/rest/RallyRestApi.html
index c354c4f..6972313 100644
--- a/src/main/resources/doc/com/rallydev/rest/RallyRestApi.html
+++ b/src/main/resources/doc/com/rallydev/rest/RallyRestApi.html
@@ -2,9 +2,9 @@
-
+
RallyRestApi (Rally Rest API for Java 2.0)
-
+
@@ -33,6 +33,7 @@