mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
Delegate HTTP protocol activity in RESTServiceConnector to RestClient
- All HTTP protocol activities are now handled by RestClient - This service is now only responsible for creating requests, and dispatching them to the client - Provides a Simple API for creating, updating, retrieving and deleting objects
This commit is contained in:
parent
8a93bb8d2d
commit
de63b94f2d
@ -1,66 +0,0 @@
|
||||
//
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you 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 com.cloud.utils.rest;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.commons.httpclient.HttpClient;
|
||||
import org.apache.commons.httpclient.HttpException;
|
||||
import org.apache.commons.httpclient.HttpMethodBase;
|
||||
|
||||
/**
|
||||
* Base 64 encoded authorization strategy. This implementation as opposed to
|
||||
* {@link RESTValidationStrategy} doesn't do a login after auth error, but instead
|
||||
* includes the encoded credentials in each request, instead of a cookie.
|
||||
*/
|
||||
public class BasicEncodedRESTValidationStrategy extends RESTValidationStrategy {
|
||||
|
||||
public BasicEncodedRESTValidationStrategy(final String host, final String adminuser, final String adminpass) {
|
||||
super();
|
||||
this.host = host;
|
||||
user = adminuser;
|
||||
password = adminpass;
|
||||
}
|
||||
|
||||
public BasicEncodedRESTValidationStrategy() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void executeMethod(final HttpMethodBase method, final HttpClient client,
|
||||
final String protocol)
|
||||
throws CloudstackRESTException, HttpException, IOException {
|
||||
if (host == null || host.isEmpty() || user == null || user.isEmpty() || password == null || password.isEmpty()) {
|
||||
throw new CloudstackRESTException("Hostname/credentials are null or empty");
|
||||
}
|
||||
|
||||
final String encodedCredentials = encodeCredentials();
|
||||
method.setRequestHeader("Authorization", "Basic " + encodedCredentials);
|
||||
client.executeMethod(method);
|
||||
}
|
||||
|
||||
private String encodeCredentials() {
|
||||
final String authString = user + ":" + password;
|
||||
final byte[] authEncBytes = Base64.encodeBase64(authString.getBytes());
|
||||
final String authStringEnc = new String(authEncBytes);
|
||||
return authStringEnc;
|
||||
}
|
||||
|
||||
}
|
||||
@ -20,371 +20,146 @@
|
||||
package com.cloud.utils.rest;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.lang.reflect.Type;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.Socket;
|
||||
import java.net.URL;
|
||||
import java.net.UnknownHostException;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLSocket;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
|
||||
import org.apache.cloudstack.utils.security.SSLUtils;
|
||||
import org.apache.cloudstack.utils.security.SecureSSLSocketFactory;
|
||||
import org.apache.commons.httpclient.ConnectTimeoutException;
|
||||
import org.apache.commons.httpclient.HttpClient;
|
||||
import org.apache.commons.httpclient.HttpException;
|
||||
import org.apache.commons.httpclient.HttpMethod;
|
||||
import org.apache.commons.httpclient.HttpMethodBase;
|
||||
import org.apache.commons.httpclient.HttpStatus;
|
||||
import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
|
||||
import org.apache.commons.httpclient.NameValuePair;
|
||||
import org.apache.commons.httpclient.cookie.CookiePolicy;
|
||||
import org.apache.commons.httpclient.methods.DeleteMethod;
|
||||
import org.apache.commons.httpclient.methods.GetMethod;
|
||||
import org.apache.commons.httpclient.methods.PostMethod;
|
||||
import org.apache.commons.httpclient.methods.PutMethod;
|
||||
import org.apache.commons.httpclient.methods.StringRequestEntity;
|
||||
import org.apache.commons.httpclient.params.HttpConnectionParams;
|
||||
import org.apache.commons.httpclient.protocol.Protocol;
|
||||
import org.apache.commons.httpclient.protocol.ProtocolSocketFactory;
|
||||
import org.apache.commons.httpclient.protocol.SecureProtocolSocketFactory;
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpUriRequest;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.gson.FieldNamingPolicy;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
/**
|
||||
* This abstraction encapsulates client side code for REST service communication. It encapsulates access in a delegate validation strategy. There may different implementations
|
||||
* extending {@link RESTValidationStrategy}, and any of them should mention the needed data to work.
|
||||
* This abstraction encapsulates client side code for REST service communication. It encapsulates access in a REST client. There can be different implementations of that client
|
||||
* implementing {@link RestClient}, and any of them should mention the needed data to work.
|
||||
*
|
||||
* This connector allows the use of {@link JsonDeserializer} for specific classes. You can provide in the constructor a list of classes and a list of deserializers for these
|
||||
* classes. These should be a correlated so that Nth deserializer is correctly mapped to Nth class.
|
||||
* This connector allows the use of {@link JsonDeserializer} for specific classes. You can provide in the constructor a map of classes to deserializers for these classes.
|
||||
*/
|
||||
public class RESTServiceConnector {
|
||||
private static final String HTTPS = "https";
|
||||
protected static final String GET_METHOD_TYPE = "get";
|
||||
protected static final String DELETE_METHOD_TYPE = "delete";
|
||||
protected static final String PUT_METHOD_TYPE = "put";
|
||||
protected static final String POST_METHOD_TYPE = "post";
|
||||
private static final String TEXT_HTML_CONTENT_TYPE = "text/html";
|
||||
private static final String JSON_CONTENT_TYPE = "application/json";
|
||||
private static final String CONTENT_TYPE = "Content-Type";
|
||||
private static final int BODY_RESP_MAX_LEN = 1024;
|
||||
private static final int HTTPS_PORT = 443;
|
||||
|
||||
private static final Logger s_logger = Logger.getLogger(RESTServiceConnector.class);
|
||||
|
||||
protected final static String protocol = HTTPS;
|
||||
|
||||
private final static MultiThreadedHttpConnectionManager s_httpClientManager = new MultiThreadedHttpConnectionManager();
|
||||
|
||||
protected RESTValidationStrategy validation;
|
||||
|
||||
private final HttpClient client;
|
||||
private static final Optional<String> ABSENT = Optional.absent();
|
||||
|
||||
private final RestClient client;
|
||||
private final Gson gson;
|
||||
|
||||
/**
|
||||
* Getter that may be needed only for test purpose
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Gson getGson() {
|
||||
return gson;
|
||||
private RESTServiceConnector(final Builder builder) {
|
||||
client = builder.client;
|
||||
gson = setGsonDeserializer(builder.classToDeserializerMap);
|
||||
}
|
||||
|
||||
public RESTServiceConnector(final RESTValidationStrategy validationStrategy) {
|
||||
this(validationStrategy, null, null);
|
||||
}
|
||||
|
||||
public RESTServiceConnector(final RESTValidationStrategy validationStrategy, final List<Class<?>> classList, final List<JsonDeserializer<?>> deserializerList) {
|
||||
validation = validationStrategy;
|
||||
client = createHttpClient();
|
||||
client.getParams().setCookiePolicy(CookiePolicy.BROWSER_COMPATIBILITY);
|
||||
|
||||
try {
|
||||
// Cast to ProtocolSocketFactory to avoid the deprecated constructor with the SecureProtocolSocketFactory parameter
|
||||
Protocol.registerProtocol(HTTPS, new Protocol(HTTPS, (ProtocolSocketFactory) new TrustingProtocolSocketFactory(), HTTPS_PORT));
|
||||
} catch (final IOException e) {
|
||||
s_logger.warn("Failed to register the TrustingProtocolSocketFactory, falling back to default SSLSocketFactory", e);
|
||||
}
|
||||
|
||||
private static Gson setGsonDeserializer(final Map<Class<?>, JsonDeserializer<?>> classToDeserializerMap) {
|
||||
final GsonBuilder gsonBuilder = new GsonBuilder();
|
||||
if (classList != null && deserializerList != null) {
|
||||
for (int i = 0; i < classList.size() && i < deserializerList.size(); i++) {
|
||||
gsonBuilder.registerTypeAdapter(classList.get(i), deserializerList.get(i));
|
||||
}
|
||||
for (final Map.Entry<Class<?>, JsonDeserializer<?>> entry : classToDeserializerMap.entrySet()) {
|
||||
gsonBuilder.registerTypeAdapter(entry.getKey(), entry.getValue());
|
||||
}
|
||||
gson = gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create();
|
||||
return gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create();
|
||||
}
|
||||
|
||||
public HttpClient createHttpClient() {
|
||||
return new HttpClient(s_httpClientManager);
|
||||
public static Builder create() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public HttpMethod createMethod(final String type, final String uri) throws CloudstackRESTException {
|
||||
String url;
|
||||
try {
|
||||
url = new URL(protocol, validation.getHost(), uri).toString();
|
||||
} catch (final MalformedURLException e) {
|
||||
s_logger.error("Unable to build REST Service URL", e);
|
||||
throw new CloudstackRESTException("Unable to build Nicira API URL", e);
|
||||
}
|
||||
|
||||
if (POST_METHOD_TYPE.equalsIgnoreCase(type)) {
|
||||
return new PostMethod(url);
|
||||
} else if (GET_METHOD_TYPE.equalsIgnoreCase(type)) {
|
||||
return new GetMethod(url);
|
||||
} else if (DELETE_METHOD_TYPE.equalsIgnoreCase(type)) {
|
||||
return new DeleteMethod(url);
|
||||
} else if (PUT_METHOD_TYPE.equalsIgnoreCase(type)) {
|
||||
return new PutMethod(url);
|
||||
} else {
|
||||
throw new CloudstackRESTException("Requesting unknown method type");
|
||||
}
|
||||
public <T> void executeUpdateObject(final T newObject, final String path, final Map<String, String> parameters) throws CloudstackRESTException {
|
||||
s_logger.debug("Executing update object on " + path);
|
||||
client.closeResponse(createAndExecuteRequest(HttpMethods.PUT, path, parameters, Optional.fromNullable(gson.toJson(newObject))));
|
||||
}
|
||||
|
||||
public void setControllerAddress(final String address) {
|
||||
validation.setHost(address);
|
||||
}
|
||||
|
||||
public void setAdminCredentials(final String username, final String password) {
|
||||
validation.setUser(username);
|
||||
validation.setPassword(password);
|
||||
}
|
||||
|
||||
public <T> void executeUpdateObject(final T newObject, final String uri, final Map<String, String> parameters) throws CloudstackRESTException {
|
||||
|
||||
final PutMethod pm = (PutMethod) createMethod(PUT_METHOD_TYPE, uri);
|
||||
pm.setRequestHeader(CONTENT_TYPE, JSON_CONTENT_TYPE);
|
||||
try {
|
||||
pm.setRequestEntity(new StringRequestEntity(gson.toJson(newObject), JSON_CONTENT_TYPE, null));
|
||||
} catch (final UnsupportedEncodingException e) {
|
||||
throw new CloudstackRESTException("Failed to encode json request body", e);
|
||||
}
|
||||
|
||||
executeMethod(pm);
|
||||
|
||||
if (pm.getStatusCode() != HttpStatus.SC_OK) {
|
||||
final String errorMessage = responseToErrorMessage(pm);
|
||||
pm.releaseConnection();
|
||||
s_logger.error("Failed to update object : " + errorMessage);
|
||||
throw new CloudstackRESTException("Failed to update object : " + errorMessage);
|
||||
}
|
||||
pm.releaseConnection();
|
||||
public <T> void executeUpdateObject(final T newObject, final String path) throws CloudstackRESTException {
|
||||
executeUpdateObject(newObject, path, new HashMap<String, String>());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T executeCreateObject(final T newObject, final Type returnObjectType, final String uri, final Map<String, String> parameters) throws CloudstackRESTException {
|
||||
public <T> T executeCreateObject(final T newObject, final String path, final Map<String, String> parameters) throws CloudstackRESTException {
|
||||
s_logger.debug("Executing create object on " + path);
|
||||
final CloseableHttpResponse response = createAndExecuteRequest(HttpMethods.POST, path, parameters, Optional.fromNullable(gson.toJson(newObject)));
|
||||
return (T) readResponseBody(response, newObject.getClass());
|
||||
}
|
||||
|
||||
final PostMethod pm = (PostMethod) createMethod(POST_METHOD_TYPE, uri);
|
||||
pm.setRequestHeader(CONTENT_TYPE, JSON_CONTENT_TYPE);
|
||||
try {
|
||||
pm.setRequestEntity(new StringRequestEntity(gson.toJson(newObject), JSON_CONTENT_TYPE, null));
|
||||
} catch (final UnsupportedEncodingException e) {
|
||||
throw new CloudstackRESTException("Failed to encode json request body", e);
|
||||
public <T> T executeCreateObject(final T newObject, final String uri) throws CloudstackRESTException {
|
||||
return executeCreateObject(newObject, uri, new HashMap<String, String>());
|
||||
}
|
||||
|
||||
public void executeDeleteObject(final String path) throws CloudstackRESTException {
|
||||
s_logger.debug("Executing delete object on " + path);
|
||||
client.closeResponse(createAndExecuteRequest(HttpMethods.DELETE, path, new HashMap<String, String>(), ABSENT));
|
||||
}
|
||||
|
||||
public <T> T executeRetrieveObject(final Type returnObjectType, final String path, final Map<String, String> parameters) throws CloudstackRESTException {
|
||||
s_logger.debug("Executing retrieve object on " + path);
|
||||
final CloseableHttpResponse response = createAndExecuteRequest(HttpMethods.GET, path, parameters, ABSENT);
|
||||
return readResponseBody(response, returnObjectType);
|
||||
}
|
||||
|
||||
public <T> T executeRetrieveObject(final Type returnObjectType, final String path) throws CloudstackRESTException {
|
||||
return executeRetrieveObject(returnObjectType, path, new HashMap<String, String>());
|
||||
}
|
||||
|
||||
private CloseableHttpResponse createAndExecuteRequest(final HttpMethods method, final String path, final Map<String, String> parameters, final Optional<String> jsonPayLoad)
|
||||
throws CloudstackRESTException {
|
||||
final HttpUriRequest httpRequest = HttpUriRequestBuilder.create()
|
||||
.path(path)
|
||||
.parameters(parameters)
|
||||
.jsonPayload(jsonPayLoad)
|
||||
.method(method)
|
||||
.build();
|
||||
if (jsonPayLoad.isPresent()) {
|
||||
s_logger.debug("Built request '" + httpRequest + "' with payload: " + jsonPayLoad);
|
||||
}
|
||||
return executeRequest(httpRequest);
|
||||
}
|
||||
|
||||
executeMethod(pm);
|
||||
private CloseableHttpResponse executeRequest(final HttpUriRequest httpRequest) throws CloudstackRESTException {
|
||||
final CloseableHttpResponse response = client.execute(httpRequest);
|
||||
s_logger.debug("Executed request: " + httpRequest + " status was " + response.getStatusLine().toString());
|
||||
return response;
|
||||
}
|
||||
|
||||
if (pm.getStatusCode() != HttpStatus.SC_CREATED) {
|
||||
final String errorMessage = responseToErrorMessage(pm);
|
||||
pm.releaseConnection();
|
||||
s_logger.error("Failed to create object : " + errorMessage);
|
||||
throw new CloudstackRESTException("Failed to create object : " + errorMessage);
|
||||
}
|
||||
|
||||
T result;
|
||||
private <T> T readResponseBody(final CloseableHttpResponse response, final Type type) throws CloudstackRESTException {
|
||||
final HttpEntity entity = response.getEntity();
|
||||
try {
|
||||
result = (T) gson.fromJson(pm.getResponseBodyAsString(), TypeToken.get(newObject.getClass()).getType());
|
||||
final String stringEntity = EntityUtils.toString(entity);
|
||||
s_logger.debug("Response entity: " + stringEntity);
|
||||
EntityUtils.consumeQuietly(entity);
|
||||
return gson.fromJson(stringEntity, type);
|
||||
} catch (final IOException e) {
|
||||
throw new CloudstackRESTException("Failed to decode json response body", e);
|
||||
throw new CloudstackRESTException("Could not deserialize response to JSON. Entity: " + entity, e);
|
||||
} finally {
|
||||
pm.releaseConnection();
|
||||
client.closeResponse(response);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public void executeDeleteObject(final String uri) throws CloudstackRESTException {
|
||||
final DeleteMethod dm = (DeleteMethod) createMethod(DELETE_METHOD_TYPE, uri);
|
||||
dm.setRequestHeader(CONTENT_TYPE, JSON_CONTENT_TYPE);
|
||||
public static class Builder {
|
||||
private RestClient client;
|
||||
final private Map<Class<?>, JsonDeserializer<?>> classToDeserializerMap = new HashMap<Class<?>, JsonDeserializer<?>>();
|
||||
|
||||
executeMethod(dm);
|
||||
|
||||
if (dm.getStatusCode() != HttpStatus.SC_NO_CONTENT) {
|
||||
final String errorMessage = responseToErrorMessage(dm);
|
||||
dm.releaseConnection();
|
||||
s_logger.error("Failed to delete object : " + errorMessage);
|
||||
throw new CloudstackRESTException("Failed to delete object : " + errorMessage);
|
||||
}
|
||||
dm.releaseConnection();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T executeRetrieveObject(final Type returnObjectType, final String uri, final Map<String, String> parameters) throws CloudstackRESTException {
|
||||
final GetMethod gm = (GetMethod) createMethod(GET_METHOD_TYPE, uri);
|
||||
gm.setRequestHeader(CONTENT_TYPE, JSON_CONTENT_TYPE);
|
||||
if (parameters != null && !parameters.isEmpty()) {
|
||||
final List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(parameters.size());
|
||||
for (final Entry<String, String> e : parameters.entrySet()) {
|
||||
nameValuePairs.add(new NameValuePair(e.getKey(), e.getValue()));
|
||||
}
|
||||
gm.setQueryString(nameValuePairs.toArray(new NameValuePair[0]));
|
||||
public Builder client(final RestClient client) {
|
||||
this.client = client;
|
||||
return this;
|
||||
}
|
||||
|
||||
executeMethod(gm);
|
||||
|
||||
if (gm.getStatusCode() != HttpStatus.SC_OK) {
|
||||
final String errorMessage = responseToErrorMessage(gm);
|
||||
gm.releaseConnection();
|
||||
s_logger.error("Failed to retrieve object : " + errorMessage);
|
||||
throw new CloudstackRESTException("Failed to retrieve object : " + errorMessage);
|
||||
public Builder classToDeserializerMap(final Map<Class<?>, JsonDeserializer<?>> classToDeserializerMap) {
|
||||
this.classToDeserializerMap.clear();
|
||||
this.classToDeserializerMap.putAll(classToDeserializerMap);
|
||||
return this;
|
||||
}
|
||||
|
||||
T returnValue;
|
||||
try {
|
||||
returnValue = (T) gson.fromJson(gm.getResponseBodyAsString(), returnObjectType);
|
||||
} catch (final IOException e) {
|
||||
s_logger.error("IOException while retrieving response body", e);
|
||||
throw new CloudstackRESTException(e);
|
||||
} finally {
|
||||
gm.releaseConnection();
|
||||
}
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
public void executeMethod(final HttpMethodBase method) throws CloudstackRESTException {
|
||||
try {
|
||||
validation.executeMethod(method, client, protocol);
|
||||
} catch (final HttpException e) {
|
||||
s_logger.error("HttpException caught while trying to connect to the REST Service", e);
|
||||
method.releaseConnection();
|
||||
throw new CloudstackRESTException("API call to REST Service Failed", e);
|
||||
} catch (final IOException e) {
|
||||
s_logger.error("IOException caught while trying to connect to the REST Service", e);
|
||||
method.releaseConnection();
|
||||
throw new CloudstackRESTException("API call to Nicira REST Service Failed", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static String responseToErrorMessage(final HttpMethodBase method) {
|
||||
assert method.isRequestSent() : "no use getting an error message unless the request is sent";
|
||||
|
||||
if (TEXT_HTML_CONTENT_TYPE.equals(method.getResponseHeader(CONTENT_TYPE).getValue())) {
|
||||
// The error message is the response content
|
||||
// Safety margin of 1024 characters, anything longer is probably useless
|
||||
// and will clutter the logs
|
||||
try {
|
||||
return method.getResponseBodyAsString(BODY_RESP_MAX_LEN);
|
||||
} catch (final IOException e) {
|
||||
s_logger.debug("Error while loading response body", e);
|
||||
}
|
||||
public Builder classToDeserializerEntry(final Class<?> clazz, final JsonDeserializer<?> deserializer) {
|
||||
classToDeserializerMap.put(clazz, deserializer);
|
||||
return this;
|
||||
}
|
||||
|
||||
// The default
|
||||
return method.getStatusText();
|
||||
}
|
||||
|
||||
/*
|
||||
* Some controllers use a self-signed certificate. The TrustingProtocolSocketFactory will accept any provided certificate when making an SSL connection to the SDN Manager
|
||||
*/
|
||||
private class TrustingProtocolSocketFactory implements SecureProtocolSocketFactory {
|
||||
|
||||
private SSLSocketFactory ssf;
|
||||
|
||||
public TrustingProtocolSocketFactory() throws IOException {
|
||||
// Create a trust manager that does not validate certificate chains
|
||||
final TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
|
||||
@Override
|
||||
public X509Certificate[] getAcceptedIssuers() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkClientTrusted(final X509Certificate[] certs, final String authType) {
|
||||
// Trust always
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkServerTrusted(final X509Certificate[] certs, final String authType) {
|
||||
// Trust always
|
||||
}
|
||||
} };
|
||||
|
||||
try {
|
||||
// Install the all-trusting trust manager
|
||||
final SSLContext sc = SSLUtils.getSSLContext();
|
||||
sc.init(null, trustAllCerts, new java.security.SecureRandom());
|
||||
ssf = new SecureSSLSocketFactory(sc);
|
||||
} catch (final KeyManagementException e) {
|
||||
throw new IOException(e);
|
||||
} catch (final NoSuchAlgorithmException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(final String host, final int port) throws IOException {
|
||||
final SSLSocket socket = (SSLSocket) ssf.createSocket(host, port);
|
||||
socket.setEnabledProtocols(SSLUtils.getSupportedProtocols(socket.getEnabledProtocols()));
|
||||
return socket;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(final String address, final int port, final InetAddress localAddress, final int localPort) throws IOException, UnknownHostException {
|
||||
final Socket socket = ssf.createSocket(address, port, localAddress, localPort);
|
||||
if (socket instanceof SSLSocket) {
|
||||
((SSLSocket) socket).setEnabledProtocols(SSLUtils.getSupportedProtocols(((SSLSocket) socket).getEnabledProtocols()));
|
||||
}
|
||||
return socket;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(final Socket socket, final String host, final int port, final boolean autoClose) throws IOException, UnknownHostException {
|
||||
final Socket s = ssf.createSocket(socket, host, port, autoClose);
|
||||
if (s instanceof SSLSocket) {
|
||||
((SSLSocket) s).setEnabledProtocols(SSLUtils.getSupportedProtocols(((SSLSocket) s).getEnabledProtocols()));
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(final String host, final int port, final InetAddress localAddress, final int localPort, final HttpConnectionParams params) throws IOException,
|
||||
UnknownHostException, ConnectTimeoutException {
|
||||
final int timeout = params.getConnectionTimeout();
|
||||
if (timeout == 0) {
|
||||
final Socket socket = createSocket(host, port, localAddress, localPort);
|
||||
if (socket instanceof SSLSocket) {
|
||||
((SSLSocket) socket).setEnabledProtocols(SSLUtils.getSupportedProtocols(((SSLSocket) socket).getEnabledProtocols()));
|
||||
}
|
||||
return socket;
|
||||
} else {
|
||||
final Socket s = ssf.createSocket();
|
||||
if (s instanceof SSLSocket) {
|
||||
((SSLSocket) s).setEnabledProtocols(SSLUtils.getSupportedProtocols(((SSLSocket) s).getEnabledProtocols()));
|
||||
}
|
||||
s.bind(new InetSocketAddress(localAddress, localPort));
|
||||
s.connect(new InetSocketAddress(host, port), timeout);
|
||||
return s;
|
||||
}
|
||||
public RESTServiceConnector build() {
|
||||
return new RESTServiceConnector(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,159 +0,0 @@
|
||||
//
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you 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 com.cloud.utils.rest;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
|
||||
import org.apache.commons.httpclient.HttpClient;
|
||||
import org.apache.commons.httpclient.HttpException;
|
||||
import org.apache.commons.httpclient.HttpMethodBase;
|
||||
import org.apache.commons.httpclient.HttpStatus;
|
||||
import org.apache.commons.httpclient.methods.PostMethod;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
/**
|
||||
* Basic authentication strategy. This strategy needs user and password for authentication.
|
||||
*
|
||||
* A login URL is needed which will be used for login and getting the cookie to be used in next requests. If an executeMethod request fails due to authorization it will try to
|
||||
* login, get the cookie and repeat the attempt to execute the method.
|
||||
*/
|
||||
public class RESTValidationStrategy {
|
||||
|
||||
private static final Logger s_logger = Logger.getLogger(RESTValidationStrategy.class);
|
||||
|
||||
protected String host;
|
||||
protected String user;
|
||||
protected String password;
|
||||
protected String serverVersion;
|
||||
protected String loginUrl;
|
||||
|
||||
public RESTValidationStrategy(final String host, final String user, final String password, final String serverVersion, final String loginUrl) {
|
||||
super();
|
||||
this.host = host;
|
||||
this.user = user;
|
||||
this.password = password;
|
||||
this.serverVersion = serverVersion;
|
||||
this.loginUrl = loginUrl;
|
||||
}
|
||||
|
||||
public RESTValidationStrategy(final String loginUrl) {
|
||||
this.loginUrl = loginUrl;
|
||||
}
|
||||
|
||||
public RESTValidationStrategy() {
|
||||
}
|
||||
|
||||
public String getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
public void setUser(final String user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(final String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getLoginUrl() {
|
||||
return loginUrl;
|
||||
}
|
||||
|
||||
public void setLoginUrl(final String loginUrl) {
|
||||
this.loginUrl = loginUrl;
|
||||
}
|
||||
|
||||
public String getHost() {
|
||||
return host;
|
||||
}
|
||||
|
||||
public void setHost(final String host) {
|
||||
this.host = host;
|
||||
}
|
||||
|
||||
public void executeMethod(final HttpMethodBase method, final HttpClient client, final String protocol) throws CloudstackRESTException, HttpException, IOException {
|
||||
if (host == null || host.isEmpty() || user == null || user.isEmpty() || password == null || password.isEmpty()) {
|
||||
throw new CloudstackRESTException("Hostname/credentials are null or empty");
|
||||
}
|
||||
|
||||
client.executeMethod(method);
|
||||
if (method.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) {
|
||||
method.releaseConnection();
|
||||
// login and try again
|
||||
login(protocol, client);
|
||||
client.executeMethod(method);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs against the REST server. The cookie is stored in the <code>_authcookie<code> variable.
|
||||
* <p>
|
||||
* The method returns false if the login failed or the connection could not be made.
|
||||
*
|
||||
*/
|
||||
protected void login(final String protocol, final HttpClient client) throws CloudstackRESTException {
|
||||
String url;
|
||||
|
||||
if (host == null || host.isEmpty() || user == null || user.isEmpty() || password == null || password.isEmpty()) {
|
||||
throw new CloudstackRESTException("Hostname/credentials are null or empty");
|
||||
}
|
||||
|
||||
try {
|
||||
url = new URL(protocol, host, loginUrl).toString();
|
||||
} catch (final MalformedURLException e) {
|
||||
s_logger.error("Unable to build Nicira API URL", e);
|
||||
throw new CloudstackRESTException("Unable to build Nicira API URL", e);
|
||||
}
|
||||
|
||||
final PostMethod pm = new PostMethod(url);
|
||||
pm.addParameter("username", user);
|
||||
pm.addParameter("password", password);
|
||||
|
||||
try {
|
||||
client.executeMethod(pm);
|
||||
} catch (final HttpException e) {
|
||||
throw new CloudstackRESTException("REST Service API login failed ", e);
|
||||
} catch (final IOException e) {
|
||||
throw new CloudstackRESTException("REST Service API login failed ", e);
|
||||
} finally {
|
||||
pm.releaseConnection();
|
||||
}
|
||||
|
||||
if (pm.getStatusCode() != HttpStatus.SC_OK) {
|
||||
s_logger.error("REST Service API login failed : " + pm.getStatusText());
|
||||
throw new CloudstackRESTException("REST Service API login failed " + pm.getStatusText());
|
||||
}
|
||||
|
||||
// Extract the version for later use
|
||||
if (pm.getResponseHeader("Server") != null) {
|
||||
serverVersion = pm.getResponseHeader("Server").getValue();
|
||||
s_logger.debug("Server reports version " + serverVersion);
|
||||
}
|
||||
|
||||
// Success; the cookie required for login is kept in _client
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,44 @@
|
||||
//
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you 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 com.cloud.utils.rest;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.mockito.Matchers.argThat;
|
||||
|
||||
import org.apache.http.client.methods.HttpUriRequest;
|
||||
import org.hamcrest.FeatureMatcher;
|
||||
import org.hamcrest.Matcher;
|
||||
|
||||
public class HttpUriRequestMethodMatcher extends FeatureMatcher<HttpUriRequest, String> {
|
||||
|
||||
public static HttpUriRequest aMethod(final String method) {
|
||||
return argThat(new HttpUriRequestMethodMatcher(equalTo(method), "method", "method"));
|
||||
}
|
||||
|
||||
public HttpUriRequestMethodMatcher(final Matcher<? super String> subMatcher, final String featureDescription, final String featureName) {
|
||||
super(subMatcher, featureDescription, featureName);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String featureValueOf(final HttpUriRequest actual) {
|
||||
return actual.getMethod();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,62 @@
|
||||
//
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you 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 com.cloud.utils.rest;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.mockito.Matchers.argThat;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.http.HttpEntityEnclosingRequest;
|
||||
import org.apache.http.ParseException;
|
||||
import org.apache.http.client.methods.HttpUriRequest;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.hamcrest.FeatureMatcher;
|
||||
import org.hamcrest.Matcher;
|
||||
|
||||
public class HttpUriRequestPayloadMatcher extends FeatureMatcher<HttpUriRequest, String> {
|
||||
|
||||
public static HttpUriRequest aPayload(final String payload) {
|
||||
return argThat(hasPayload(payload));
|
||||
}
|
||||
|
||||
public static HttpUriRequestPayloadMatcher hasPayload(final String payload) {
|
||||
return new HttpUriRequestPayloadMatcher(equalTo(payload), "payload", "payload");
|
||||
}
|
||||
|
||||
public HttpUriRequestPayloadMatcher(final Matcher<? super String> subMatcher, final String featureDescription, final String featureName) {
|
||||
super(subMatcher, featureDescription, featureName);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String featureValueOf(final HttpUriRequest actual) {
|
||||
String payload = "";
|
||||
if (actual instanceof HttpEntityEnclosingRequest) {
|
||||
try {
|
||||
payload = EntityUtils.toString(((HttpEntityEnclosingRequest) actual).getEntity());
|
||||
} catch (final ParseException e) {
|
||||
throw new IllegalArgumentException("Couldn't read request's entity payload.", e);
|
||||
} catch (final IOException e) {
|
||||
throw new IllegalArgumentException("Couldn't read request's entity payload.", e);
|
||||
}
|
||||
}
|
||||
return payload;
|
||||
}
|
||||
}
|
||||
@ -19,376 +19,305 @@
|
||||
|
||||
package com.cloud.utils.rest;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.httpclient.Header;
|
||||
import org.apache.commons.httpclient.HttpClient;
|
||||
import org.apache.commons.httpclient.HttpException;
|
||||
import org.apache.commons.httpclient.HttpMethod;
|
||||
import org.apache.commons.httpclient.methods.DeleteMethod;
|
||||
import org.apache.commons.httpclient.methods.GetMethod;
|
||||
import org.apache.commons.httpclient.methods.PostMethod;
|
||||
import org.apache.commons.httpclient.methods.PutMethod;
|
||||
import org.apache.commons.httpclient.params.HttpClientParams;
|
||||
import org.apache.http.HttpStatus;
|
||||
import org.junit.Before;
|
||||
import org.apache.commons.lang.builder.EqualsBuilder;
|
||||
import org.apache.commons.lang.builder.HashCodeBuilder;
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.HttpHost;
|
||||
import org.apache.http.HttpRequest;
|
||||
import org.apache.http.ProtocolVersion;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.protocol.HttpClientContext;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.message.BasicStatusLine;
|
||||
import org.codehaus.jackson.map.ObjectMapper;
|
||||
import org.codehaus.jackson.map.type.CollectionType;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.google.gson.FieldNamingPolicy;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
|
||||
public class RESTServiceConnectorTest {
|
||||
protected static final String UUID = "aaaa";
|
||||
protected static final String UUID_JSON_RESPONSE = "{\"uuid\" : \"aaaa\"}";
|
||||
private static final BasicStatusLine HTTP_200_STATUS_LINE = new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK");
|
||||
private static final Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create();
|
||||
private static final Map<String, String> DEFAULT_TEST_PARAMETERS = new HashMap<String, String>();
|
||||
static {
|
||||
DEFAULT_TEST_PARAMETERS.put("arg1", "val1");
|
||||
DEFAULT_TEST_PARAMETERS.put("arg2", "val2");
|
||||
}
|
||||
|
||||
RESTServiceConnector connector;
|
||||
HttpClient client = mock(HttpClient.class);
|
||||
HttpMethod method;
|
||||
String type;
|
||||
String uri;
|
||||
@Test
|
||||
public void testExecuteUpdateObject() throws Exception {
|
||||
final TestPojo newObject = new TestPojo();
|
||||
newObject.setField("newValue");
|
||||
final String newObjectJson = gson.toJson(newObject);
|
||||
final CloseableHttpResponse response = mock(CloseableHttpResponse.class);
|
||||
when(response.getStatusLine()).thenReturn(HTTP_200_STATUS_LINE);
|
||||
final CloseableHttpClient httpClient = mock(CloseableHttpClient.class);
|
||||
when(httpClient.execute(any(HttpHost.class), any(HttpRequest.class), any(HttpClientContext.class))).thenReturn(response);
|
||||
final RestClient restClient = new BasicRestClient(httpClient, HttpClientContext.create(), "localhost");
|
||||
final RESTServiceConnector connector = new RESTServiceConnector.Builder().client(restClient).build();
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
final HttpClientParams hmp = mock(HttpClientParams.class);
|
||||
when(client.getParams()).thenReturn(hmp);
|
||||
connector = new RESTServiceConnector(null) {
|
||||
@Override
|
||||
public HttpClient createHttpClient() {
|
||||
return client;
|
||||
connector.executeUpdateObject(newObject, "/somepath");
|
||||
|
||||
verify(httpClient).execute(any(HttpHost.class), HttpUriRequestMethodMatcher.aMethod("PUT"), any(HttpClientContext.class));
|
||||
verify(httpClient).execute(any(HttpHost.class), HttpUriRequestPayloadMatcher.aPayload(newObjectJson), any(HttpClientContext.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExecuteUpdateObjectWithParameters() throws Exception {
|
||||
final TestPojo newObject = new TestPojo();
|
||||
newObject.setField("newValue");
|
||||
final String newObjectJson = gson.toJson(newObject);
|
||||
final CloseableHttpResponse response = mock(CloseableHttpResponse.class);
|
||||
when(response.getStatusLine()).thenReturn(HTTP_200_STATUS_LINE);
|
||||
final CloseableHttpClient httpClient = mock(CloseableHttpClient.class);
|
||||
when(httpClient.execute(any(HttpHost.class), any(HttpRequest.class), any(HttpClientContext.class))).thenReturn(response);
|
||||
final RestClient restClient = new BasicRestClient(httpClient, HttpClientContext.create(), "localhost");
|
||||
|
||||
final RESTServiceConnector connector = new RESTServiceConnector.Builder().client(restClient).build();
|
||||
|
||||
connector.executeUpdateObject(newObject, "/somepath", DEFAULT_TEST_PARAMETERS);
|
||||
|
||||
verify(httpClient).execute(any(HttpHost.class), HttpUriRequestMethodMatcher.aMethod("PUT"), any(HttpClientContext.class));
|
||||
verify(httpClient).execute(any(HttpHost.class), HttpUriRequestPayloadMatcher.aPayload(newObjectJson), any(HttpClientContext.class));
|
||||
verify(httpClient).execute(any(HttpHost.class), HttpUriRequestQueryMatcher.aQueryThatContains("arg2=val2"), any(HttpClientContext.class));
|
||||
verify(httpClient).execute(any(HttpHost.class), HttpUriRequestQueryMatcher.aQueryThatContains("arg1=val1"), any(HttpClientContext.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExecuteCreateObject() throws Exception {
|
||||
final TestPojo newObject = new TestPojo();
|
||||
newObject.setField("newValue");
|
||||
final String newObjectJson = gson.toJson(newObject);
|
||||
final CloseableHttpResponse response = mock(CloseableHttpResponse.class);
|
||||
when(response.getEntity()).thenReturn(new StringEntity(newObjectJson));
|
||||
when(response.getStatusLine()).thenReturn(HTTP_200_STATUS_LINE);
|
||||
final CloseableHttpClient httpClient = mock(CloseableHttpClient.class);
|
||||
when(httpClient.execute(any(HttpHost.class), any(HttpRequest.class), any(HttpClientContext.class))).thenReturn(response);
|
||||
final RestClient restClient = new BasicRestClient(httpClient, HttpClientContext.create(), "localhost");
|
||||
final RESTServiceConnector connector = new RESTServiceConnector.Builder().client(restClient).build();
|
||||
|
||||
final TestPojo object = connector.executeCreateObject(newObject, "/somepath");
|
||||
|
||||
assertThat(object, notNullValue());
|
||||
assertThat(object, equalTo(newObject));
|
||||
verify(httpClient).execute(any(HttpHost.class), HttpUriRequestMethodMatcher.aMethod("POST"), any(HttpClientContext.class));
|
||||
verify(httpClient).execute(any(HttpHost.class), HttpUriRequestPayloadMatcher.aPayload(newObjectJson), any(HttpClientContext.class));
|
||||
verify(response).close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExecuteCreateObjectWithParameters() throws Exception {
|
||||
final TestPojo newObject = new TestPojo();
|
||||
newObject.setField("newValue");
|
||||
final String newObjectJson = gson.toJson(newObject);
|
||||
final CloseableHttpResponse response = mock(CloseableHttpResponse.class);
|
||||
when(response.getEntity()).thenReturn(new StringEntity(newObjectJson));
|
||||
when(response.getStatusLine()).thenReturn(HTTP_200_STATUS_LINE);
|
||||
final CloseableHttpClient httpClient = mock(CloseableHttpClient.class);
|
||||
when(httpClient.execute(any(HttpHost.class), any(HttpRequest.class), any(HttpClientContext.class))).thenReturn(response);
|
||||
final RestClient restClient = new BasicRestClient(httpClient, HttpClientContext.create(), "localhost");
|
||||
final RESTServiceConnector connector = new RESTServiceConnector.Builder().client(restClient).build();
|
||||
|
||||
final TestPojo object = connector.executeCreateObject(newObject, "/somepath", DEFAULT_TEST_PARAMETERS);
|
||||
|
||||
assertThat(object, notNullValue());
|
||||
assertThat(object, equalTo(newObject));
|
||||
verify(httpClient).execute(any(HttpHost.class), HttpUriRequestMethodMatcher.aMethod("POST"), any(HttpClientContext.class));
|
||||
verify(httpClient).execute(any(HttpHost.class), HttpUriRequestPayloadMatcher.aPayload(newObjectJson), any(HttpClientContext.class));
|
||||
verify(httpClient).execute(any(HttpHost.class), HttpUriRequestQueryMatcher.aQueryThatContains("arg2=val2"), any(HttpClientContext.class));
|
||||
verify(httpClient).execute(any(HttpHost.class), HttpUriRequestQueryMatcher.aQueryThatContains("arg1=val1"), any(HttpClientContext.class));
|
||||
verify(response).close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExecuteDeleteObject() throws Exception {
|
||||
final HttpEntity entity = mock(HttpEntity.class);
|
||||
final CloseableHttpResponse response = mock(CloseableHttpResponse.class);
|
||||
when(response.getEntity()).thenReturn(entity);
|
||||
when(response.getStatusLine()).thenReturn(HTTP_200_STATUS_LINE);
|
||||
final CloseableHttpClient httpClient = mock(CloseableHttpClient.class);
|
||||
when(httpClient.execute(any(HttpHost.class), any(HttpRequest.class), any(HttpClientContext.class))).thenReturn(response);
|
||||
final RestClient restClient = new BasicRestClient(httpClient, HttpClientContext.create(), "localhost");
|
||||
final RESTServiceConnector connector = new RESTServiceConnector.Builder().client(restClient).build();
|
||||
|
||||
connector.executeDeleteObject("/somepath");
|
||||
|
||||
verify(httpClient).execute(any(HttpHost.class), HttpUriRequestMethodMatcher.aMethod("DELETE"), any(HttpClientContext.class));
|
||||
verify(response).close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExecuteRetrieveObject() throws Exception {
|
||||
final TestPojo existingObject = new TestPojo();
|
||||
existingObject.setField("existingValue");
|
||||
final String newObjectJson = gson.toJson(existingObject);
|
||||
final CloseableHttpResponse response = mock(CloseableHttpResponse.class);
|
||||
when(response.getEntity()).thenReturn(new StringEntity(newObjectJson));
|
||||
when(response.getStatusLine()).thenReturn(HTTP_200_STATUS_LINE);
|
||||
final CloseableHttpClient httpClient = mock(CloseableHttpClient.class);
|
||||
when(httpClient.execute(any(HttpHost.class), any(HttpRequest.class), any(HttpClientContext.class))).thenReturn(response);
|
||||
final RestClient restClient = new BasicRestClient(httpClient, HttpClientContext.create(), "localhost");
|
||||
final RESTServiceConnector connector = new RESTServiceConnector.Builder().client(restClient).build();
|
||||
|
||||
final TestPojo object = connector.executeRetrieveObject(TestPojo.class, "/somepath");
|
||||
|
||||
assertThat(object, notNullValue());
|
||||
assertThat(object, equalTo(existingObject));
|
||||
verify(httpClient).execute(any(HttpHost.class), HttpUriRequestMethodMatcher.aMethod("GET"), any(HttpClientContext.class));
|
||||
verify(response).close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExecuteRetrieveObjectWithParameters() throws Exception {
|
||||
final TestPojo existingObject = new TestPojo();
|
||||
existingObject.setField("existingValue");
|
||||
final String newObjectJson = gson.toJson(existingObject);
|
||||
final CloseableHttpResponse response = mock(CloseableHttpResponse.class);
|
||||
when(response.getEntity()).thenReturn(new StringEntity(newObjectJson));
|
||||
when(response.getStatusLine()).thenReturn(HTTP_200_STATUS_LINE);
|
||||
final CloseableHttpClient httpClient = mock(CloseableHttpClient.class);
|
||||
when(httpClient.execute(any(HttpHost.class), any(HttpRequest.class), any(HttpClientContext.class))).thenReturn(response);
|
||||
final RestClient restClient = new BasicRestClient(httpClient, HttpClientContext.create(), "localhost");
|
||||
final RESTServiceConnector connector = new RESTServiceConnector.Builder().client(restClient).build();
|
||||
|
||||
final TestPojo object = connector.executeRetrieveObject(TestPojo.class, "/somepath", DEFAULT_TEST_PARAMETERS);
|
||||
|
||||
assertThat(object, notNullValue());
|
||||
assertThat(object, equalTo(existingObject));
|
||||
verify(httpClient).execute(any(HttpHost.class), HttpUriRequestMethodMatcher.aMethod("GET"), any(HttpClientContext.class));
|
||||
verify(httpClient).execute(any(HttpHost.class), HttpUriRequestQueryMatcher.aQueryThatContains("arg2=val2"), any(HttpClientContext.class));
|
||||
verify(httpClient).execute(any(HttpHost.class), HttpUriRequestQueryMatcher.aQueryThatContains("arg1=val1"), any(HttpClientContext.class));
|
||||
verify(response).close();
|
||||
}
|
||||
|
||||
@Test(expected = JsonParseException.class)
|
||||
public void testCustomDeserializerTypeMismatch() throws Exception {
|
||||
final CloseableHttpResponse response = mock(CloseableHttpResponse.class);
|
||||
when(response.getStatusLine()).thenReturn(HTTP_200_STATUS_LINE);
|
||||
when(response.getEntity()).thenReturn(new StringEntity("[{somethig_not_type : \"WrongType\"}]"));
|
||||
final CloseableHttpClient httpClient = mock(CloseableHttpClient.class);
|
||||
when(httpClient.execute(any(HttpHost.class), any(HttpRequest.class), any(HttpClientContext.class))).thenReturn(response);
|
||||
final RestClient restClient = new BasicRestClient(httpClient, HttpClientContext.create(), "localhost");
|
||||
final RESTServiceConnector connector = new RESTServiceConnector.Builder()
|
||||
.client(restClient)
|
||||
.classToDeserializerEntry(TestPojo.class, new TestPojoDeserializer())
|
||||
.build();
|
||||
|
||||
connector.executeRetrieveObject(TestPojo.class, "/somepath");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustomDeserializerForCustomLists() throws Exception {
|
||||
final CloseableHttpResponse response = mock(CloseableHttpResponse.class);
|
||||
when(response.getStatusLine()).thenReturn(HTTP_200_STATUS_LINE);
|
||||
when(response.getEntity()).thenReturn(new StringEntity("{results: [{field : \"SomeValue\"}], results_count: 1}"));
|
||||
final CloseableHttpClient httpClient = mock(CloseableHttpClient.class);
|
||||
when(httpClient.execute(any(HttpHost.class), any(HttpRequest.class), any(HttpClientContext.class))).thenReturn(response);
|
||||
final RestClient restClient = new BasicRestClient(httpClient, HttpClientContext.create(), "localhost");
|
||||
final Class<? extends CollectionType> clazzListOfTestPojo = new ObjectMapper().getTypeFactory().constructCollectionType(List.class, TestPojo.class).getClass();
|
||||
final RESTServiceConnector connector = new RESTServiceConnector.Builder()
|
||||
.client(restClient)
|
||||
.classToDeserializerEntry(clazzListOfTestPojo, new CustomListDeserializer<TestPojoDeserializer>())
|
||||
.build();
|
||||
|
||||
connector.executeRetrieveObject(TestPojo.class, "/somepath");
|
||||
}
|
||||
|
||||
class NiciraList<T> {
|
||||
private List<T> results;
|
||||
private int resultCount;
|
||||
|
||||
public List<T> getResults() {
|
||||
return results;
|
||||
}
|
||||
|
||||
public void setResults(final List<T> results) {
|
||||
this.results = results;
|
||||
}
|
||||
|
||||
public int getResultCount() {
|
||||
return resultCount;
|
||||
}
|
||||
|
||||
public void setResultCount(final int resultCount) {
|
||||
this.resultCount = resultCount;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class TestPojo {
|
||||
private String field;
|
||||
|
||||
public String getField() {
|
||||
return field;
|
||||
}
|
||||
|
||||
public void setField(final String field) {
|
||||
this.field = field;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return HashCodeBuilder.reflectionHashCode(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
return EqualsBuilder.reflectionEquals(this, obj);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private final class TestPojoDeserializer implements JsonDeserializer<TestPojo> {
|
||||
@Override
|
||||
public TestPojo deserialize(final JsonElement json, final Type typeOfT, final JsonDeserializationContext context) throws JsonParseException {
|
||||
final JsonObject jsonObject = json.getAsJsonObject();
|
||||
|
||||
if (!jsonObject.has("type")) {
|
||||
throw new JsonParseException("Deserializing as a TestPojo, but no type present in the json object");
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpMethod createMethod(final String newType, final String newUri) {
|
||||
type = newType;
|
||||
uri = newUri;
|
||||
return method;
|
||||
return context.deserialize(jsonObject, TestPojo.class);
|
||||
}
|
||||
}
|
||||
|
||||
private final class CustomListDeserializer<T> implements JsonDeserializer<T> {
|
||||
private final Gson standardGson = new GsonBuilder().create();
|
||||
|
||||
@Override
|
||||
public T deserialize(final JsonElement json, final Type typeOfT, final JsonDeserializationContext context) throws JsonParseException {
|
||||
final JsonObject jsonObject = json.getAsJsonObject();
|
||||
|
||||
System.err.println(json.toString());
|
||||
|
||||
if (jsonObject.has("results")) {
|
||||
final JsonArray results = jsonObject.getAsJsonArray("results");
|
||||
return context.deserialize(results, typeOfT);
|
||||
} else {
|
||||
return standardGson.fromJson(jsonObject, typeOfT);
|
||||
}
|
||||
};
|
||||
|
||||
connector.validation = new RESTValidationStrategy();
|
||||
connector.setAdminCredentials("admin", "adminpass");
|
||||
connector.setControllerAddress("localhost");
|
||||
}
|
||||
|
||||
@Test(expected = CloudstackRESTException.class)
|
||||
public void testExecuteLoginWithoutHostname() throws CloudstackRESTException {
|
||||
connector.setControllerAddress(null);
|
||||
connector.validation.login(RESTServiceConnector.protocol, client);
|
||||
}
|
||||
|
||||
@Test(expected = CloudstackRESTException.class)
|
||||
public void testExecuteLoginWithoutCredentials() throws CloudstackRESTException {
|
||||
method = mock(PutMethod.class);
|
||||
connector.setAdminCredentials(null, null);
|
||||
connector.validation.login(RESTServiceConnector.protocol, client);
|
||||
}
|
||||
|
||||
@Test(expected = CloudstackRESTException.class)
|
||||
public void testExecuteUpdateObjectWithoutHostname() throws CloudstackRESTException {
|
||||
method = mock(PutMethod.class);
|
||||
connector.setControllerAddress(null);
|
||||
connector.executeUpdateObject(new String(), "/", Collections.<String, String> emptyMap());
|
||||
}
|
||||
|
||||
@Test(expected = CloudstackRESTException.class)
|
||||
public void testExecuteUpdateObjectWithoutCredentials() throws CloudstackRESTException {
|
||||
method = mock(PutMethod.class);
|
||||
connector.setAdminCredentials(null, null);
|
||||
connector.executeUpdateObject(new String(), "/", Collections.<String, String> emptyMap());
|
||||
}
|
||||
|
||||
@Test(expected = CloudstackRESTException.class)
|
||||
public void testExecuteCreateObjectWithoutHostname() throws CloudstackRESTException {
|
||||
method = mock(PostMethod.class);
|
||||
connector.setControllerAddress(null);
|
||||
connector.executeCreateObject(new String(), String.class, "/", Collections.<String, String> emptyMap());
|
||||
}
|
||||
|
||||
@Test(expected = CloudstackRESTException.class)
|
||||
public void testExecuteCreateObjectWithoutCredentials() throws CloudstackRESTException {
|
||||
method = mock(PostMethod.class);
|
||||
connector.setAdminCredentials(null, null);
|
||||
connector.executeCreateObject(new String(), String.class, "/", Collections.<String, String> emptyMap());
|
||||
}
|
||||
|
||||
@Test(expected = CloudstackRESTException.class)
|
||||
public void testExecuteDeleteObjectWithoutHostname() throws CloudstackRESTException {
|
||||
method = mock(DeleteMethod.class);
|
||||
connector.setControllerAddress(null);
|
||||
connector.executeDeleteObject("/");
|
||||
}
|
||||
|
||||
@Test(expected = CloudstackRESTException.class)
|
||||
public void testExecuteDeleteObjectWithoutCredentials() throws CloudstackRESTException {
|
||||
method = mock(DeleteMethod.class);
|
||||
connector.setAdminCredentials(null, null);
|
||||
connector.executeDeleteObject("/");
|
||||
}
|
||||
|
||||
@Test(expected = CloudstackRESTException.class)
|
||||
public void testExecuteRetrieveObjectWithoutHostname() throws CloudstackRESTException {
|
||||
method = mock(GetMethod.class);
|
||||
connector.setControllerAddress(null);
|
||||
connector.executeRetrieveObject(String.class, "/", Collections.<String, String> emptyMap());
|
||||
}
|
||||
|
||||
@Test(expected = CloudstackRESTException.class)
|
||||
public void testExecuteRetrieveObjectWithoutCredentials() throws CloudstackRESTException {
|
||||
method = mock(GetMethod.class);
|
||||
connector.setAdminCredentials(null, null);
|
||||
connector.executeRetrieveObject(String.class, "/", Collections.<String, String> emptyMap());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExecuteMethod() throws CloudstackRESTException {
|
||||
final GetMethod gm = mock(GetMethod.class);
|
||||
|
||||
when(gm.getStatusCode()).thenReturn(HttpStatus.SC_OK);
|
||||
connector.executeMethod(gm);
|
||||
verify(gm, times(1)).getStatusCode();
|
||||
}
|
||||
|
||||
@Test(expected = CloudstackRESTException.class)
|
||||
public void testExecuteMethodWithLogin() throws CloudstackRESTException, HttpException, IOException {
|
||||
final GetMethod gm = mock(GetMethod.class);
|
||||
when(client.executeMethod((HttpMethod) any())).thenThrow(new HttpException());
|
||||
when(gm.getStatusCode()).thenReturn(HttpStatus.SC_UNAUTHORIZED).thenReturn(HttpStatus.SC_UNAUTHORIZED);
|
||||
connector.executeMethod(gm);
|
||||
verify(gm, times(1)).getStatusCode();
|
||||
}
|
||||
|
||||
/*
|
||||
* Bit of a roundabout way to ensure that login is called after an un authorized result It not possible to properly mock login()
|
||||
*/
|
||||
public void testExecuteMethodWithLoginSucced2ndAttempt() throws CloudstackRESTException {
|
||||
// Prepare
|
||||
final GetMethod gm = mock(GetMethod.class);
|
||||
when(gm.getStatusCode()).thenReturn(HttpStatus.SC_UNAUTHORIZED).thenReturn(HttpStatus.SC_UNAUTHORIZED);
|
||||
|
||||
final RESTValidationStrategy previousValidationStrategy = connector.validation;
|
||||
connector.validation = new RESTValidationStrategy() {
|
||||
@Override
|
||||
protected void login(final String protocol, final HttpClient client) throws CloudstackRESTException {
|
||||
// Do nothing
|
||||
}
|
||||
};
|
||||
connector.setAdminCredentials("admin", "adminpass");
|
||||
connector.setControllerAddress("localhost");
|
||||
|
||||
// Execute
|
||||
connector.executeMethod(gm);
|
||||
// Leave mock object as is was
|
||||
connector.validation = previousValidationStrategy;
|
||||
|
||||
// Assert/verify
|
||||
verify(gm, times(2)).getStatusCode();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExecuteCreateObject() throws CloudstackRESTException, IOException {
|
||||
JsonEntity ls = new JsonEntity();
|
||||
method = mock(PostMethod.class);
|
||||
when(method.getStatusCode()).thenReturn(HttpStatus.SC_CREATED);
|
||||
when(method.getResponseBodyAsString()).thenReturn(UUID_JSON_RESPONSE);
|
||||
ls = connector.executeCreateObject(ls, JsonEntity.class, "/", Collections.<String, String> emptyMap());
|
||||
assertTrue(UUID.equals(ls.getUuid()));
|
||||
verify(method, times(1)).releaseConnection();
|
||||
|
||||
}
|
||||
|
||||
@Test(expected = CloudstackRESTException.class)
|
||||
public void testExecuteCreateObjectFailure() throws CloudstackRESTException, IOException {
|
||||
JsonEntity ls = new JsonEntity();
|
||||
method = mock(PostMethod.class);
|
||||
when(method.getStatusCode()).thenReturn(HttpStatus.SC_INTERNAL_SERVER_ERROR);
|
||||
final Header header = mock(Header.class);
|
||||
when(header.getValue()).thenReturn("text/html");
|
||||
when(method.getResponseHeader("Content-Type")).thenReturn(header);
|
||||
when(method.getResponseBodyAsString()).thenReturn("Off to timbuktu, won't be back later.");
|
||||
when(method.isRequestSent()).thenReturn(true);
|
||||
try {
|
||||
ls = connector.executeCreateObject(ls, JsonEntity.class, "/", Collections.<String, String> emptyMap());
|
||||
} finally {
|
||||
verify(method, times(1)).releaseConnection();
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expected = CloudstackRESTException.class)
|
||||
public void testExecuteCreateObjectException() throws CloudstackRESTException, IOException {
|
||||
JsonEntity ls = new JsonEntity();
|
||||
when(client.executeMethod((HttpMethod) any())).thenThrow(new HttpException());
|
||||
method = mock(PostMethod.class);
|
||||
when(method.getStatusCode()).thenReturn(HttpStatus.SC_INTERNAL_SERVER_ERROR);
|
||||
final Header header = mock(Header.class);
|
||||
when(header.getValue()).thenReturn("text/html");
|
||||
when(method.getResponseHeader("Content-Type")).thenReturn(header);
|
||||
when(method.getResponseBodyAsString()).thenReturn("Off to timbuktu, won't be back later.");
|
||||
try {
|
||||
ls = connector.executeCreateObject(ls, JsonEntity.class, "/", Collections.<String, String> emptyMap());
|
||||
} finally {
|
||||
verify(method, times(1)).releaseConnection();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExecuteUpdateObject() throws CloudstackRESTException, IOException {
|
||||
final JsonEntity ls = new JsonEntity();
|
||||
method = mock(PutMethod.class);
|
||||
when(method.getStatusCode()).thenReturn(HttpStatus.SC_OK);
|
||||
connector.executeUpdateObject(ls, "/", Collections.<String, String> emptyMap());
|
||||
verify(method, times(1)).releaseConnection();
|
||||
verify(client, times(1)).executeMethod(method);
|
||||
}
|
||||
|
||||
@Test(expected = CloudstackRESTException.class)
|
||||
public void testExecuteUpdateObjectFailure() throws CloudstackRESTException, IOException {
|
||||
final JsonEntity ls = new JsonEntity();
|
||||
method = mock(PutMethod.class);
|
||||
when(method.getStatusCode()).thenReturn(HttpStatus.SC_INTERNAL_SERVER_ERROR);
|
||||
final Header header = mock(Header.class);
|
||||
when(header.getValue()).thenReturn("text/html");
|
||||
when(method.getResponseHeader("Content-Type")).thenReturn(header);
|
||||
when(method.getResponseBodyAsString()).thenReturn("Off to timbuktu, won't be back later.");
|
||||
when(method.isRequestSent()).thenReturn(true);
|
||||
try {
|
||||
connector.executeUpdateObject(ls, "/", Collections.<String, String> emptyMap());
|
||||
} finally {
|
||||
verify(method, times(1)).releaseConnection();
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expected = CloudstackRESTException.class)
|
||||
public void testExecuteUpdateObjectException() throws CloudstackRESTException, IOException {
|
||||
final JsonEntity ls = new JsonEntity();
|
||||
method = mock(PutMethod.class);
|
||||
when(method.getStatusCode()).thenReturn(HttpStatus.SC_OK);
|
||||
when(client.executeMethod((HttpMethod) any())).thenThrow(new IOException());
|
||||
try {
|
||||
connector.executeUpdateObject(ls, "/", Collections.<String, String> emptyMap());
|
||||
} finally {
|
||||
verify(method, times(1)).releaseConnection();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExecuteDeleteObject() throws CloudstackRESTException, IOException {
|
||||
method = mock(DeleteMethod.class);
|
||||
when(method.getStatusCode()).thenReturn(HttpStatus.SC_NO_CONTENT);
|
||||
connector.executeDeleteObject("/");
|
||||
verify(method, times(1)).releaseConnection();
|
||||
verify(client, times(1)).executeMethod(method);
|
||||
}
|
||||
|
||||
@Test(expected = CloudstackRESTException.class)
|
||||
public void testExecuteDeleteObjectFailure() throws CloudstackRESTException, IOException {
|
||||
method = mock(DeleteMethod.class);
|
||||
when(method.getStatusCode()).thenReturn(HttpStatus.SC_INTERNAL_SERVER_ERROR);
|
||||
final Header header = mock(Header.class);
|
||||
when(header.getValue()).thenReturn("text/html");
|
||||
when(method.getResponseHeader("Content-Type")).thenReturn(header);
|
||||
when(method.getResponseBodyAsString()).thenReturn("Off to timbuktu, won't be back later.");
|
||||
when(method.isRequestSent()).thenReturn(true);
|
||||
try {
|
||||
connector.executeDeleteObject("/");
|
||||
} finally {
|
||||
verify(method, times(1)).releaseConnection();
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expected = CloudstackRESTException.class)
|
||||
public void testExecuteDeleteObjectException() throws CloudstackRESTException, IOException {
|
||||
method = mock(DeleteMethod.class);
|
||||
when(method.getStatusCode()).thenReturn(HttpStatus.SC_NO_CONTENT);
|
||||
when(client.executeMethod((HttpMethod) any())).thenThrow(new HttpException());
|
||||
try {
|
||||
connector.executeDeleteObject("/");
|
||||
} finally {
|
||||
verify(method, times(1)).releaseConnection();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExecuteRetrieveObject() throws CloudstackRESTException, IOException {
|
||||
method = mock(GetMethod.class);
|
||||
when(method.getStatusCode()).thenReturn(HttpStatus.SC_OK);
|
||||
when(method.getResponseBodyAsString()).thenReturn(UUID_JSON_RESPONSE);
|
||||
connector.executeRetrieveObject(JsonEntity.class, "/", Collections.<String, String> emptyMap());
|
||||
verify(method, times(1)).releaseConnection();
|
||||
verify(client, times(1)).executeMethod(method);
|
||||
}
|
||||
|
||||
@Test(expected = CloudstackRESTException.class)
|
||||
public void testExecuteRetrieveObjectFailure() throws CloudstackRESTException, IOException {
|
||||
method = mock(GetMethod.class);
|
||||
when(method.getStatusCode()).thenReturn(HttpStatus.SC_INTERNAL_SERVER_ERROR);
|
||||
when(method.getResponseBodyAsString()).thenReturn(UUID_JSON_RESPONSE);
|
||||
final Header header = mock(Header.class);
|
||||
when(header.getValue()).thenReturn("text/html");
|
||||
when(method.getResponseHeader("Content-Type")).thenReturn(header);
|
||||
when(method.getResponseBodyAsString()).thenReturn("Off to timbuktu, won't be back later.");
|
||||
when(method.isRequestSent()).thenReturn(true);
|
||||
try {
|
||||
connector.executeRetrieveObject(JsonEntity.class, "/", Collections.<String, String> emptyMap());
|
||||
} finally {
|
||||
verify(method, times(1)).releaseConnection();
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expected = CloudstackRESTException.class)
|
||||
public void testExecuteRetrieveObjectException() throws CloudstackRESTException, IOException {
|
||||
method = mock(GetMethod.class);
|
||||
when(method.getStatusCode()).thenReturn(HttpStatus.SC_OK);
|
||||
when(method.getResponseBodyAsString()).thenReturn(UUID_JSON_RESPONSE);
|
||||
when(client.executeMethod((HttpMethod) any())).thenThrow(new HttpException());
|
||||
try {
|
||||
connector.executeRetrieveObject(JsonEntity.class, "/", Collections.<String, String> emptyMap());
|
||||
} finally {
|
||||
verify(method, times(1)).releaseConnection();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class JsonEntity {
|
||||
private String displayName;
|
||||
private String uuid;
|
||||
private String href;
|
||||
private String schema;
|
||||
|
||||
public String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
public void setDisplayName(final String displayName) {
|
||||
this.displayName = displayName;
|
||||
}
|
||||
|
||||
public String getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
public void setUuid(final String uuid) {
|
||||
this.uuid = uuid;
|
||||
}
|
||||
|
||||
public String getHref() {
|
||||
return href;
|
||||
}
|
||||
|
||||
public void setHref(final String href) {
|
||||
this.href = href;
|
||||
}
|
||||
|
||||
public String getSchema() {
|
||||
return schema;
|
||||
}
|
||||
|
||||
public void setSchema(final String schema) {
|
||||
this.schema = schema;
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user