Added annotation for throwing exception with Error object on 4xx status codes, and implemented in TaskClient

This commit is contained in:
Andrew Donald Kennedy 2012-02-08 13:40:41 +00:00
parent c771b2ac87
commit 412dd45ce1
9 changed files with 236 additions and 19 deletions

View File

@ -0,0 +1,48 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds 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 org.jclouds.vcloud.director.v1_5;
import org.jclouds.vcloud.director.v1_5.domain.Error;
import org.jclouds.vcloud.director.v1_5.domain.Task;
/**
* @author grkvlt@apache.org
*/
public class VCloudDirectorException extends RuntimeException {
/** The serialVersionUID. */
private static final long serialVersionUID = -3200853408568729058L;
private final Error error;
public VCloudDirectorException(Error error) {
super("Error: " + error.getMessage());
this.error = error;
}
public VCloudDirectorException(Task task) {
super("Task error: " + task.getError().getMessage());
this.error = task.getError();
}
public Error getError() {
return error;
}
}

View File

@ -29,8 +29,12 @@ package org.jclouds.vcloud.director.v1_5;
public interface VCloudDirectorMediaType { public interface VCloudDirectorMediaType {
public final static String NS = "http://www.vmware.com/vcloud/v1.5"; public final static String NS = "http://www.vmware.com/vcloud/v1.5";
public final static String ANY = "*/*";
public final static String SESSION_XML = "application/vnd.vmware.vcloud.session+xml"; public final static String SESSION_XML = "application/vnd.vmware.vcloud.session+xml";
public final static String ERROR_XML = "application/vnd.vmware.vcloud.error+xml";
public final static String ORGLIST_XML = "application/vnd.vmware.vcloud.orgList+xml"; public final static String ORGLIST_XML = "application/vnd.vmware.vcloud.orgList+xml";
public final static String METADATA_XML = "application/vnd.vmware.vcloud.metadata+xml"; public final static String METADATA_XML = "application/vnd.vmware.vcloud.metadata+xml";

View File

@ -30,10 +30,10 @@ import org.jclouds.rest.annotations.EndpointParam;
import org.jclouds.rest.annotations.ExceptionParser; import org.jclouds.rest.annotations.ExceptionParser;
import org.jclouds.rest.annotations.JAXBResponseParser; import org.jclouds.rest.annotations.JAXBResponseParser;
import org.jclouds.rest.annotations.RequestFilters; import org.jclouds.rest.annotations.RequestFilters;
import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404;
import org.jclouds.vcloud.director.v1_5.domain.Task; import org.jclouds.vcloud.director.v1_5.domain.Task;
import org.jclouds.vcloud.director.v1_5.domain.TasksList; import org.jclouds.vcloud.director.v1_5.domain.TasksList;
import org.jclouds.vcloud.director.v1_5.filters.AddVCloudAuthorizationToRequest; import org.jclouds.vcloud.director.v1_5.filters.AddVCloudAuthorizationToRequest;
import org.jclouds.vcloud.director.v1_5.functions.ThrowVCloudErrorOn4xxOrNull;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
@ -51,7 +51,7 @@ public interface TaskAsyncClient {
@Path("/tasksList/{id}") @Path("/tasksList/{id}")
@Consumes @Consumes
@JAXBResponseParser @JAXBResponseParser
@ExceptionParser(ReturnNullOnNotFoundOr404.class) @ExceptionParser(ThrowVCloudErrorOn4xxOrNull.class)
ListenableFuture<TasksList> getTaskList(@PathParam("id") String orgId); ListenableFuture<TasksList> getTaskList(@PathParam("id") String orgId);
/** /**
@ -60,7 +60,7 @@ public interface TaskAsyncClient {
@GET @GET
@Consumes @Consumes
@JAXBResponseParser @JAXBResponseParser
@ExceptionParser(ReturnNullOnNotFoundOr404.class) @ExceptionParser(ThrowVCloudErrorOn4xxOrNull.class)
ListenableFuture<Task> getTask(@EndpointParam URI taskHref); ListenableFuture<Task> getTask(@EndpointParam URI taskHref);
/** /**
@ -70,6 +70,6 @@ public interface TaskAsyncClient {
@Path("/action/cancel") @Path("/action/cancel")
@Consumes @Consumes
@JAXBResponseParser @JAXBResponseParser
@ExceptionParser(ReturnNullOnNotFoundOr404.class) @ExceptionParser(ThrowVCloudErrorOn4xxOrNull.class)
void cancelTask(@EndpointParam URI taskHref); void cancelTask(@EndpointParam URI taskHref);
} }

View File

@ -0,0 +1,61 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds 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 org.jclouds.vcloud.director.v1_5.functions;
import java.io.IOException;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.xml.bind.JAXB;
import org.jclouds.http.HttpResponseException;
import org.jclouds.io.InputSuppliers;
import org.jclouds.vcloud.director.v1_5.VCloudDirectorException;
import org.jclouds.vcloud.director.v1_5.domain.Error;
import com.google.common.base.Function;
import com.google.common.base.Throwables;
import com.google.common.collect.Iterables;
/**
* @author grkvlt@apache.org
*/
@Singleton
public class ThrowVCloudErrorOn4xxOrNull implements Function<Exception, Object> {
@Inject
private ThrowVCloudErrorOn4xxOrNull() { }
@Override
public Object apply(Exception from) {
Iterable<HttpResponseException> throwables = Iterables.filter(Throwables.getCausalChain(from), HttpResponseException.class);
if (Iterables.size(throwables) == 1) {
HttpResponseException exception = Iterables.getOnlyElement(throwables);
if (exception.getResponse().getStatusCode() >= 400 && exception.getResponse().getStatusCode() < 500) {
try {
Error error = JAXB.unmarshal(InputSuppliers.of(exception.getContent()).getInput(), Error.class);
throw new VCloudDirectorException(error);
} catch (IOException e) {
Throwables.propagate(e);
}
}
}
return null; // TODO is this correct?
}
}

View File

@ -25,16 +25,16 @@ import java.net.URI;
import org.jclouds.http.HttpRequest; import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse; import org.jclouds.http.HttpResponse;
import org.jclouds.vcloud.director.v1_5.VCloudDirectorClient; import org.jclouds.vcloud.director.v1_5.VCloudDirectorClient;
import org.jclouds.vcloud.director.v1_5.VCloudDirectorException;
import org.jclouds.vcloud.director.v1_5.VCloudDirectorMediaType; import org.jclouds.vcloud.director.v1_5.VCloudDirectorMediaType;
import org.jclouds.vcloud.director.v1_5.domain.Error;
import org.jclouds.vcloud.director.v1_5.domain.Reference; import org.jclouds.vcloud.director.v1_5.domain.Reference;
import org.jclouds.vcloud.director.v1_5.domain.Task; import org.jclouds.vcloud.director.v1_5.domain.Task;
import org.jclouds.vcloud.director.v1_5.domain.TasksList; import org.jclouds.vcloud.director.v1_5.domain.TasksList;
import org.jclouds.vcloud.director.v1_5.internal.BaseVCloudDirectorRestClientExpectTest; import org.jclouds.vcloud.director.v1_5.internal.BaseVCloudDirectorRestClientExpectTest;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.Iterables;
/** /**
* Test the {@link TaskClient} by observing its side effects. * Test the {@link TaskClient} by observing its side effects.
@ -48,7 +48,7 @@ public class TaskClientExpectTest extends BaseVCloudDirectorRestClientExpectTest
public void testTaskListForValidOrg() { public void testTaskListForValidOrg() {
HttpRequest orgListRequest = HttpRequest.builder() HttpRequest orgListRequest = HttpRequest.builder()
.method("GET") .method("GET")
.endpoint(URI.create("http://localhost/api/tasksList/6f312e42-cd2b-488d-a2bb-97519cd57ed0")) .endpoint(URI.create(endpoint + "/tasksList/6f312e42-cd2b-488d-a2bb-97519cd57ed0"))
.headers(ImmutableMultimap.<String, String> builder() .headers(ImmutableMultimap.<String, String> builder()
.put("Accept", "*/*") .put("Accept", "*/*")
.put("x-vcloud-authorization", token) .put("x-vcloud-authorization", token)
@ -57,14 +57,14 @@ public class TaskClientExpectTest extends BaseVCloudDirectorRestClientExpectTest
HttpResponse orgListResponse = HttpResponse.builder() HttpResponse orgListResponse = HttpResponse.builder()
.statusCode(200) .statusCode(200)
.payload(payloadFromResourceWithContentType("/taskslist.xml", VCloudDirectorMediaType.TASKSLIST_XML + ";version=1.5")) .payload(payloadFromResourceWithContentType("/task/taskslist.xml", VCloudDirectorMediaType.TASKSLIST_XML + ";version=1.5"))
.build(); .build();
VCloudDirectorClient client = requestsSendResponses(loginRequest, sessionResponse, orgListRequest, orgListResponse); VCloudDirectorClient client = requestsSendResponses(loginRequest, sessionResponse, orgListRequest, orgListResponse);
String orgId =Iterables.getLast(Splitter.on("/").split(URI.create("https://vcloudbeta.bluelock.com/api/org/6f312e42-cd2b-488d-a2bb-97519cd57ed0").getPath())); String orgId = getUuidFromReference.apply(Reference.builder().href(URI.create("https://vcloudbeta.bluelock.com/api/org/6f312e42-cd2b-488d-a2bb-97519cd57ed0")).build());
assertEquals(client.getTaskClient().getTaskList(orgId), TasksList.builder() TasksList expected = TasksList.builder()
.name("Tasks Lists") .name("Tasks Lists")
.type("application/vnd.vmware.vcloud.tasksList+xml") .type("application/vnd.vmware.vcloud.tasksList+xml")
.href(URI.create("https://vcloudbeta.bluelock.com/api/tasksList/6f312e42-cd2b-488d-a2bb-97519cd57ed0")) .href(URI.create("https://vcloudbeta.bluelock.com/api/tasksList/6f312e42-cd2b-488d-a2bb-97519cd57ed0"))
@ -117,7 +117,79 @@ public class TaskClientExpectTest extends BaseVCloudDirectorRestClientExpectTest
.href(URI.create("https://vcloudbeta.bluelock.com/api/org/6f312e42-cd2b-488d-a2bb-97519cd57ed0")) .href(URI.create("https://vcloudbeta.bluelock.com/api/org/6f312e42-cd2b-488d-a2bb-97519cd57ed0"))
.build()) .build())
.build()) .build())
.build() .build();
);
assertEquals(client.getTaskClient().getTaskList(orgId), expected);
}
@Test
public void testTaskListForInvalidOrgId() {
HttpRequest orgListRequest = HttpRequest.builder()
.method("GET")
.endpoint(URI.create(endpoint + "/tasksList/NOTAUUID"))
.headers(ImmutableMultimap.<String, String> builder()
.put("Accept", "*/*")
.put("x-vcloud-authorization", token)
.build())
.build();
HttpResponse orgListResponse = HttpResponse.builder()
.statusCode(400)
.payload(payloadFromResourceWithContentType("/task/error400.xml", VCloudDirectorMediaType.ERROR_XML + ";version=1.5"))
.build();
VCloudDirectorClient client = requestsSendResponses(loginRequest, sessionResponse, orgListRequest, orgListResponse);
String orgId = "NOTAUUID";
Error expected = Error.builder()
.message("validation error on field 'id': String value has invalid format or length")
.majorErrorCode(400)
.minorErrorCode("BAD_REQUEST")
.build();
try {
client.getTaskClient().getTaskList(orgId);
fail("Should give HTTP 400 error");
} catch (VCloudDirectorException vde) {
assertEquals(vde.getError(), expected);
} catch (Exception e) {
fail("Should have thrown a VCloudDirectorException");
}
}
@Test
public void testTaskListForNotFoundOrgId() {
HttpRequest orgListRequest = HttpRequest.builder()
.method("GET")
.endpoint(URI.create(endpoint + "/tasksList/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"))
.headers(ImmutableMultimap.<String, String> builder()
.put("Accept", "*/*")
.put("x-vcloud-authorization", token)
.build())
.build();
HttpResponse orgListResponse = HttpResponse.builder()
.statusCode(403)
.payload(payloadFromResourceWithContentType("/task/error403.xml", VCloudDirectorMediaType.ERROR_XML + ";version=1.5"))
.build();
VCloudDirectorClient client = requestsSendResponses(loginRequest, sessionResponse, orgListRequest, orgListResponse);
String orgId = "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee";
Error expected = Error.builder()
.message("No access to entity \"com.vmware.vcloud.entity.org:aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee\".")
.majorErrorCode(403)
.minorErrorCode("ACCESS_TO_RESOURCE_IS_FORBIDDEN")
.build();
try {
client.getTaskClient().getTaskList(orgId);
fail("Should give HTTP 400 error");
} catch (VCloudDirectorException vde) {
assertEquals(vde.getError(), expected);
} catch (Exception e) {
fail("Should have thrown a VCloudDirectorException");
}
} }
} }

View File

@ -26,9 +26,13 @@ import org.jclouds.http.HttpResponse;
import org.jclouds.rest.BaseRestClientExpectTest; import org.jclouds.rest.BaseRestClientExpectTest;
import org.jclouds.vcloud.director.v1_5.VCloudDirectorClient; import org.jclouds.vcloud.director.v1_5.VCloudDirectorClient;
import org.jclouds.vcloud.director.v1_5.VCloudDirectorMediaType; import org.jclouds.vcloud.director.v1_5.VCloudDirectorMediaType;
import org.jclouds.vcloud.director.v1_5.domain.ReferenceType;
import org.testng.annotations.BeforeTest; import org.testng.annotations.BeforeTest;
import com.google.common.base.Function;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.Iterables;
import com.google.inject.Guice; import com.google.inject.Guice;
/** /**
@ -42,6 +46,7 @@ public class BaseVCloudDirectorRestClientExpectTest extends BaseRestClientExpect
public static final String org = "JClouds"; public static final String org = "JClouds";
public static final String password = "password"; public static final String password = "password";
public static final String token = "mIaR3/6Lna8DWImd7/JPR5rK8FcUHabt+G/UCJV5pJQ="; public static final String token = "mIaR3/6Lna8DWImd7/JPR5rK8FcUHabt+G/UCJV5pJQ=";
public static final String endpoint = "http://localhost/api";
protected DateService dateService; protected DateService dateService;
@ -51,6 +56,14 @@ public class BaseVCloudDirectorRestClientExpectTest extends BaseRestClientExpect
assert dateService != null; assert dateService != null;
} }
protected static final Function<ReferenceType<?>, String> getUuidFromReference = new Function<ReferenceType<?>, String>() {
@Override
public String apply(ReferenceType<?> input) {
String uuid = Iterables.getLast(Splitter.on("/").split(input.getHref().getPath()));
return uuid;
}
};
protected HttpRequest loginRequest = HttpRequest.builder() protected HttpRequest loginRequest = HttpRequest.builder()
.method("POST") .method("POST")
.endpoint(URI.create("http://localhost/api/sessions")) .endpoint(URI.create("http://localhost/api/sessions"))
@ -75,17 +88,32 @@ public class BaseVCloudDirectorRestClientExpectTest extends BaseRestClientExpect
credential = password; credential = password;
} }
protected HttpRequest getStandardRequest(String method, String command) {
return getStandardRequest(method, URI.create(endpoint + command));
}
protected HttpRequest getStandardRequest(String method, URI uri) { protected HttpRequest getStandardRequest(String method, URI uri) {
return HttpRequest.builder().method(method).endpoint(uri).headers( return getStandardRequest(method, uri, VCloudDirectorMediaType.ANY);
ImmutableMultimap.<String, String> builder() }
.put("Accept", "*/*")
.put("x-vcloud-authorization",token) protected HttpRequest getStandardRequest(String method, URI uri, String mediaType) {
.build()).build(); return HttpRequest.builder()
.method(method)
.endpoint(uri)
.headers(ImmutableMultimap.<String, String> builder()
.put("Accept", mediaType)
.put("x-vcloud-authorization", token)
.build())
.build();
} }
protected HttpResponse getStandardPayloadResponse(String relativeFilePath, String mediaType) { protected HttpResponse getStandardPayloadResponse(String relativeFilePath, String mediaType) {
return HttpResponse.builder().statusCode(200) return getStandardPayloadResponse(200, relativeFilePath, mediaType);
.payload(payloadFromResourceWithContentType(relativeFilePath, mediaType+";version=1.5")).build();
} }
protected HttpResponse getStandardPayloadResponse(int statusCode, String relativeFilePath, String mediaType) {
return HttpResponse.builder()
.statusCode(statusCode)
.payload(payloadFromResourceWithContentType(relativeFilePath, mediaType+";version=1.5")).build();
}
} }

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<Error xmlns="http://www.vmware.com/vcloud/v1.5" minorErrorCode="BAD_REQUEST" message="validation error on field 'id': String value has invalid format or length" majorErrorCode="400" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.vmware.com/vcloud/v1.5 http://vcloudbeta.bluelock.com/api/v1.5/schema/master.xsd"></Error>

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<Error xmlns="http://www.vmware.com/vcloud/v1.5" minorErrorCode="ACCESS_TO_RESOURCE_IS_FORBIDDEN" message="No access to entity &quot;com.vmware.vcloud.entity.org:aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee&quot;." majorErrorCode="403" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.vmware.com/vcloud/v1.5 http://vcloudbeta.bluelock.com/api/v1.5/schema/master.xsd"></Error>