diff --git a/gogrid/src/main/java/org/jclouds/gogrid/GoGridResponseException.java b/gogrid/src/main/java/org/jclouds/gogrid/GoGridResponseException.java new file mode 100644 index 0000000000..da327f9c4d --- /dev/null +++ b/gogrid/src/main/java/org/jclouds/gogrid/GoGridResponseException.java @@ -0,0 +1,35 @@ +package org.jclouds.gogrid; + +import org.jclouds.gogrid.domain.internal.ErrorResponse; +import org.jclouds.http.HttpCommand; +import org.jclouds.http.HttpResponse; +import org.jclouds.http.HttpResponseException; + +import static java.lang.String.format; + +/** + * @author Oleksiy Yarmula + */ +public class GoGridResponseException extends HttpResponseException { + + private static final long serialVersionUID = 1924589L; + + private ErrorResponse error; + + public GoGridResponseException(HttpCommand command, HttpResponse response, ErrorResponse error) { + super(format("command %s failed with code %s, error [%s]: %s", + command.toString(), response.getStatusCode(), error.getErrorCode(), + error.getMessage()), + command, response); + this.setError(error); + } + + + public ErrorResponse getError() { + return error; + } + + public void setError(ErrorResponse error) { + this.error = error; + } +} diff --git a/gogrid/src/main/java/org/jclouds/gogrid/config/GoGridRestClientModule.java b/gogrid/src/main/java/org/jclouds/gogrid/config/GoGridRestClientModule.java index 5f44211ea9..c040c0eccf 100644 --- a/gogrid/src/main/java/org/jclouds/gogrid/config/GoGridRestClientModule.java +++ b/gogrid/src/main/java/org/jclouds/gogrid/config/GoGridRestClientModule.java @@ -54,9 +54,14 @@ import org.jclouds.date.DateService; import org.jclouds.date.TimeStamp; import org.jclouds.gogrid.GoGridAsyncClient; import org.jclouds.gogrid.GoGridClient; +import org.jclouds.gogrid.handlers.GoGridErrorHandler; import org.jclouds.gogrid.services.GridServerAsyncClient; import org.jclouds.gogrid.services.GridServerClient; +import org.jclouds.http.HttpErrorHandler; import org.jclouds.http.RequiresHttp; +import org.jclouds.http.annotation.ClientError; +import org.jclouds.http.annotation.Redirection; +import org.jclouds.http.annotation.ServerError; import org.jclouds.rest.ConfiguresRestClient; import org.jclouds.rest.RestClientFactory; @@ -125,7 +130,12 @@ public class GoGridRestClientModule extends AbstractModule { } protected void bindErrorHandlers() { - // TODO + bind(HttpErrorHandler.class).annotatedWith(Redirection.class).to( + GoGridErrorHandler.class); + bind(HttpErrorHandler.class).annotatedWith(ClientError.class).to( + GoGridErrorHandler.class); + bind(HttpErrorHandler.class).annotatedWith(ServerError.class).to( + GoGridErrorHandler.class); } protected void bindRetryHandlers() { diff --git a/gogrid/src/main/java/org/jclouds/gogrid/domain/internal/ErrorResponse.java b/gogrid/src/main/java/org/jclouds/gogrid/domain/internal/ErrorResponse.java new file mode 100644 index 0000000000..175fc228ca --- /dev/null +++ b/gogrid/src/main/java/org/jclouds/gogrid/domain/internal/ErrorResponse.java @@ -0,0 +1,29 @@ +package org.jclouds.gogrid.domain.internal; + +/** + * @author Oleksiy Yarmula + */ +public class ErrorResponse { + + private String message; + private String errorCode; + + /** + * A no-args constructor is required for deserialization + */ + public ErrorResponse() { + } + + public ErrorResponse(String message, String errorCode) { + this.message = message; + this.errorCode = errorCode; + } + + public String getMessage() { + return message; + } + + public String getErrorCode() { + return errorCode; + } +} diff --git a/gogrid/src/main/java/org/jclouds/gogrid/domain/internal/GenericResponseContainer.java b/gogrid/src/main/java/org/jclouds/gogrid/domain/internal/GenericResponseContainer.java index b1067e187d..0688b6d183 100644 --- a/gogrid/src/main/java/org/jclouds/gogrid/domain/internal/GenericResponseContainer.java +++ b/gogrid/src/main/java/org/jclouds/gogrid/domain/internal/GenericResponseContainer.java @@ -1,11 +1,15 @@ package org.jclouds.gogrid.domain.internal; -import com.google.common.base.Throwables; - -import java.lang.reflect.Field; import java.util.SortedSet; /** + * General format of GoGrid's response. + * + * This is the wrapper for most responses, and the actual + * result (or error) will be set to {@link #list}. + * Note that even the single returned item will be set to + * {@link #list} per GoGrid's design. + * * @author Oleksiy Yarmula */ public class GenericResponseContainer { diff --git a/gogrid/src/main/java/org/jclouds/gogrid/functions/ParseErrorFromJsonResponse.java b/gogrid/src/main/java/org/jclouds/gogrid/functions/ParseErrorFromJsonResponse.java new file mode 100644 index 0000000000..e51e14cd80 --- /dev/null +++ b/gogrid/src/main/java/org/jclouds/gogrid/functions/ParseErrorFromJsonResponse.java @@ -0,0 +1,42 @@ +package org.jclouds.gogrid.functions; + +import com.google.common.collect.Iterables; +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import com.google.inject.Singleton; +import org.jclouds.gogrid.domain.internal.ErrorResponse; +import org.jclouds.gogrid.domain.internal.GenericResponseContainer; +import org.jclouds.http.functions.ParseJson; + +import javax.inject.Inject; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.UnsupportedEncodingException; +import java.lang.reflect.Type; +import static com.google.common.base.Preconditions.checkState; + +/** + * @author Oleksiy Yarmula + */ +@Singleton +public class ParseErrorFromJsonResponse extends ParseJson { + + @Inject + public ParseErrorFromJsonResponse(Gson gson) { + super(gson); + } + + public ErrorResponse apply(InputStream stream) { + Type setType = new TypeToken>() { + }.getType(); + GenericResponseContainer response; + try { + response = gson.fromJson(new InputStreamReader(stream, "UTF-8"), setType); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException("jclouds requires UTF-8 encoding", e); + } + checkState(response.getList() != null && response.getList().size() == 1, + /*or throw*/ "Expected exactly 1 error object in response"); + return Iterables.getOnlyElement(response.getList()); + } +} diff --git a/gogrid/src/main/java/org/jclouds/gogrid/handlers/GoGridErrorHandler.java b/gogrid/src/main/java/org/jclouds/gogrid/handlers/GoGridErrorHandler.java new file mode 100644 index 0000000000..28ce16adef --- /dev/null +++ b/gogrid/src/main/java/org/jclouds/gogrid/handlers/GoGridErrorHandler.java @@ -0,0 +1,53 @@ +package org.jclouds.gogrid.handlers; + +import com.google.common.io.Closeables; +import com.google.inject.Inject; +import org.jclouds.gogrid.GoGridResponseException; +import org.jclouds.gogrid.domain.internal.ErrorResponse; +import org.jclouds.gogrid.functions.ParseErrorFromJsonResponse; +import org.jclouds.http.HttpCommand; +import org.jclouds.http.HttpErrorHandler; +import org.jclouds.http.HttpResponse; +import org.jclouds.http.HttpResponseException; +import org.jclouds.rest.AuthorizationException; + +import java.io.InputStream; + +/** + * @author Oleksiy Yarmula + */ +public class GoGridErrorHandler implements HttpErrorHandler { + + private final ParseErrorFromJsonResponse errorParser; + + @Inject + public GoGridErrorHandler(ParseErrorFromJsonResponse errorParser) { + this.errorParser = errorParser; + } + + @SuppressWarnings({"ThrowableInstanceNeverThrown"}) + @Override + public void handleError(HttpCommand command, HttpResponse response) { + Exception exception; + ErrorResponse error = parseErrorFromContentOrNull(response.getContent()); + switch (response.getStatusCode()) { + case 403: + exception = new AuthorizationException(command.getRequest(), + error.getMessage()); + break; + default: + exception = error != null ? + new GoGridResponseException(command, response, error) : + new HttpResponseException(command, response); + } + command.setException(exception); + Closeables.closeQuietly(response.getContent()); + } + + ErrorResponse parseErrorFromContentOrNull(InputStream content) { + if (content != null) { + return errorParser.apply(content); + } + return null; + } +} diff --git a/gogrid/src/main/java/org/jclouds/gogrid/services/GridServerClient.java b/gogrid/src/main/java/org/jclouds/gogrid/services/GridServerClient.java index d475cbe716..4f7fda0768 100644 --- a/gogrid/src/main/java/org/jclouds/gogrid/services/GridServerClient.java +++ b/gogrid/src/main/java/org/jclouds/gogrid/services/GridServerClient.java @@ -32,7 +32,7 @@ import org.jclouds.gogrid.domain.Server; /** * Provides synchronous access to GoGrid. *

- * + * * @see GridServerAsyncClient * @see * @@ -42,6 +42,19 @@ import org.jclouds.gogrid.domain.Server; @Timeout(duration = 30, timeUnit = TimeUnit.SECONDS) public interface GridServerClient { - Set getServerList(); + Set getServerList(); + + //Set getServersByName(String... names); + + /** + * Retrieves the server(s) by unique id(s). + * + * Given an id or a set of ids, finds one or + * multiple servers. + * @param id + * @return + */ + // Set getServersById(Long... ids); + } diff --git a/gogrid/src/test/java/org/jclouds/gogrid/config/GoGridContextModuleTest.java b/gogrid/src/test/java/org/jclouds/gogrid/config/GoGridContextModuleTest.java index d3cf3a497d..78e769aecb 100644 --- a/gogrid/src/test/java/org/jclouds/gogrid/config/GoGridContextModuleTest.java +++ b/gogrid/src/test/java/org/jclouds/gogrid/config/GoGridContextModuleTest.java @@ -22,12 +22,11 @@ import static org.testng.Assert.assertEquals; import static com.google.common.util.concurrent.Executors.sameThreadExecutor; import org.jclouds.concurrent.config.ExecutorServiceModule; +import org.jclouds.gogrid.handlers.GoGridErrorHandler; import org.jclouds.http.HttpRetryHandler; import org.jclouds.http.config.JavaUrlHttpCommandExecutorServiceModule; import org.jclouds.http.functions.config.ParserModule; -import org.jclouds.http.functions.config.ParserModule.CDateAdapter; import org.jclouds.http.functions.config.ParserModule.DateAdapter; -import org.jclouds.http.handlers.CloseContentAndSetExceptionErrorHandler; import org.jclouds.http.handlers.DelegatingErrorHandler; import org.jclouds.http.handlers.DelegatingRetryHandler; import org.jclouds.http.handlers.RedirectionRetryHandler; @@ -86,7 +85,7 @@ public class GoGridContextModuleTest { void testServerErrorHandler() { DelegatingErrorHandler handler = createInjector().getInstance(DelegatingErrorHandler.class); assertEquals(handler.getServerErrorHandler().getClass(), - CloseContentAndSetExceptionErrorHandler.class); + GoGridErrorHandler.class); } @Test @@ -99,7 +98,7 @@ public class GoGridContextModuleTest { void testClientErrorHandler() { DelegatingErrorHandler handler = createInjector().getInstance(DelegatingErrorHandler.class); assertEquals(handler.getClientErrorHandler().getClass(), - CloseContentAndSetExceptionErrorHandler.class); + GoGridErrorHandler.class); } @Test