You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jclouds.apache.org by ab...@apache.org on 2013/08/02 16:57:55 UTC

[02/10] git commit: Removed async from elasticstack

Removed async from elasticstack


Project: http://git-wip-us.apache.org/repos/asf/incubator-jclouds/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-jclouds/commit/a2af31c4
Tree: http://git-wip-us.apache.org/repos/asf/incubator-jclouds/tree/a2af31c4
Diff: http://git-wip-us.apache.org/repos/asf/incubator-jclouds/diff/a2af31c4

Branch: refs/heads/master
Commit: a2af31c419ed98fc704d98c9f9693fb3816d18eb
Parents: 1a6071a
Author: Andrew Bayer <an...@gmail.com>
Authored: Fri Aug 2 07:56:48 2013 -0700
Committer: Andrew Bayer <an...@gmail.com>
Committed: Fri Aug 2 07:57:41 2013 -0700

----------------------------------------------------------------------
 .../jclouds/elasticstack/ElasticStackApi.java   | 343 ++++++++++++++
 .../elasticstack/ElasticStackApiMetadata.java   |  23 +-
 .../elasticstack/ElasticStackAsyncClient.java   | 259 -----------
 .../elasticstack/ElasticStackClient.java        | 237 ----------
 .../ElasticStackComputeServiceAdapter.java      |   9 +-
 ...ElasticStackComputeServiceContextModule.java |   6 +-
 .../config/ElasticStackHttpApiModule.java       |  90 ++++
 .../config/ElasticStackRestClientModule.java    |  91 ----
 .../elasticstack/predicates/DriveClaimed.java   |   6 +-
 .../elasticstack/ElasticStackApiLiveTest.java   | 350 ++++++++++++++
 .../elasticstack/ElasticStackApiTest.java       | 451 +++++++++++++++++++
 .../ElasticStackAsyncClientTest.java            | 451 -------------------
 .../ElasticStackClientLiveTest.java             | 350 --------------
 .../ElasticHostsPeer1LosAngelesApiLiveTest.java |  32 ++
 ...asticHostsPeer1LosAngelesClientLiveTest.java |  32 --
 ...ElasticHostsBlueSquareLondonApiLiveTest.java |  31 ++
 ...sticHostsBlueSquareLondonClientLiveTest.java |  31 --
 .../ElasticHostsPeer1LondonApiLiveTest.java     |  31 ++
 .../ElasticHostsPeer1LondonClientLiveTest.java  |  31 --
 .../ElasticHostsPeer1SanAntonioApiLiveTest.java |  31 ++
 ...asticHostsPeer1SanAntonioClientLiveTest.java |  31 --
 .../ElasticHostsPeer1TorontoApiLiveTest.java    |  31 ++
 .../ElasticHostsPeer1TorontoClientLiveTest.java |  31 --
 .../Go2CloudJohannesburg1ApiLiveTest.java       |  31 ++
 .../Go2CloudJohannesburg1ClientLiveTest.java    |  31 --
 .../OpenHostingEast1ApiLiveTest.java            |  31 ++
 .../OpenHostingEast1ClientLiveTest.java         |  31 --
 .../ServerloveManchesterApiLiveTest.java        |  38 ++
 .../ServerloveManchesterClientLiveTest.java     |  38 --
 .../SkaliCloudMalaysiaApiLiveTest.java          |  31 ++
 .../SkaliCloudMalaysiaClientLiveTest.java       |  31 --
 31 files changed, 1537 insertions(+), 1703 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/a2af31c4/apis/elasticstack/src/main/java/org/jclouds/elasticstack/ElasticStackApi.java
----------------------------------------------------------------------
diff --git a/apis/elasticstack/src/main/java/org/jclouds/elasticstack/ElasticStackApi.java b/apis/elasticstack/src/main/java/org/jclouds/elasticstack/ElasticStackApi.java
new file mode 100644
index 0000000..bc6fe39
--- /dev/null
+++ b/apis/elasticstack/src/main/java/org/jclouds/elasticstack/ElasticStackApi.java
@@ -0,0 +1,343 @@
+/*
+ * 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 org.jclouds.elasticstack;
+
+import java.io.Closeable;
+import java.util.Set;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+import org.jclouds.Fallbacks.NullOnNotFoundOr404;
+import org.jclouds.Fallbacks.VoidOnNotFoundOr404;
+import org.jclouds.elasticstack.binders.BindDriveDataToPlainTextString;
+import org.jclouds.elasticstack.binders.BindDriveToPlainTextString;
+import org.jclouds.elasticstack.binders.BindServerToPlainTextString;
+import org.jclouds.elasticstack.domain.Drive;
+import org.jclouds.elasticstack.domain.DriveData;
+import org.jclouds.elasticstack.domain.DriveInfo;
+import org.jclouds.elasticstack.domain.ImageConversionType;
+import org.jclouds.elasticstack.domain.Server;
+import org.jclouds.elasticstack.domain.ServerInfo;
+import org.jclouds.elasticstack.functions.KeyValuesDelimitedByBlankLinesToDriveInfo;
+import org.jclouds.elasticstack.functions.KeyValuesDelimitedByBlankLinesToServerInfo;
+import org.jclouds.elasticstack.functions.ListOfKeyValuesDelimitedByBlankLinesToDriveInfoSet;
+import org.jclouds.elasticstack.functions.ListOfKeyValuesDelimitedByBlankLinesToServerInfoSet;
+import org.jclouds.elasticstack.functions.ReturnPayload;
+import org.jclouds.elasticstack.functions.SplitNewlines;
+import org.jclouds.http.filters.BasicAuthentication;
+import org.jclouds.io.Payload;
+import org.jclouds.rest.annotations.BinderParam;
+import org.jclouds.rest.annotations.Fallback;
+import org.jclouds.rest.annotations.RequestFilters;
+import org.jclouds.rest.annotations.ResponseParser;
+
+/**
+ * Provides synchronous access to elasticstack via their REST API.
+ * <p/>
+ *
+ * @see <a href="TODO: insert URL of provider documentation" />
+ * @author Adrian Cole
+ */
+@RequestFilters(BasicAuthentication.class)
+@Consumes(MediaType.TEXT_PLAIN)
+public interface ElasticStackApi extends Closeable {
+
+   /**
+    * list of server uuids in your account
+    *
+    * @return or empty set if no servers are found
+    */
+   @GET
+   @Path("/servers/list")
+   @ResponseParser(SplitNewlines.class)
+   Set<String> listServers();
+
+   /**
+    * Get all servers info
+    *
+    * @return or empty set if no servers are found
+    */
+   @GET
+   @Path("/servers/info")
+   @ResponseParser(ListOfKeyValuesDelimitedByBlankLinesToServerInfoSet.class)
+   Set<ServerInfo> listServerInfo();
+
+   /**
+    * @param uuid
+    *           what to get
+    * @return null, if not found
+    */
+   @GET
+   @Fallback(NullOnNotFoundOr404.class)
+   @ResponseParser(KeyValuesDelimitedByBlankLinesToServerInfo.class)
+   @Path("/servers/{uuid}/info")
+   ServerInfo getServerInfo(@PathParam("uuid") String uuid);
+
+   /**
+    * create a new server
+    *
+    * @param createServer
+    * @return newly created server
+    */
+   @POST
+   @Fallback(NullOnNotFoundOr404.class)
+   @ResponseParser(KeyValuesDelimitedByBlankLinesToServerInfo.class)
+   @Path("/servers/create/stopped")
+   ServerInfo createServer(
+           @BinderParam(BindServerToPlainTextString.class) Server createServer);
+
+   /**
+    * set server configuration
+    *
+    * @param uuid
+    *           what server to change
+    * @param setServer
+    *           what values to change
+    * @return new data
+    */
+   @POST
+   @Fallback(NullOnNotFoundOr404.class)
+   @ResponseParser(KeyValuesDelimitedByBlankLinesToServerInfo.class)
+   @Path("/servers/{uuid}/set")
+   ServerInfo setServerConfiguration(@PathParam("uuid") String uuid,
+                                                       @BinderParam(BindServerToPlainTextString.class) Server setServer);
+
+   /**
+    * Destroy a server
+    *
+    * @param uuid
+    *           what to destroy
+    */
+   @POST
+   @Path("/servers/{uuid}/destroy")
+   @Fallback(VoidOnNotFoundOr404.class)
+   void destroyServer(@PathParam("uuid") String uuid);
+
+   /**
+    * Start a server
+    *
+    * @param uuid
+    *           what to start
+    */
+   @POST
+   @Path("/servers/{uuid}/start")
+   void startServer(@PathParam("uuid") String uuid);
+
+   /**
+    * Stop a server
+    * <p/>
+    * Kills the server immediately, equivalent to a power failure. Server reverts to a stopped
+    * status if it is persistent and is automatically destroyed otherwise.
+    *
+    * @param uuid
+    *           what to stop
+    */
+   @POST
+   @Path("/servers/{uuid}/stop")
+   void stopServer(@PathParam("uuid") String uuid);
+
+   /**
+    * Shutdown a server
+    * <p/>
+    * Sends the server an ACPI power-down event. Server reverts to a stopped status if it is
+    * persistent and is automatically destroyed otherwise.
+    * <h4>note</h4> behaviour on shutdown depends on how your server OS is set up to respond to an
+    * ACPI power button signal.
+    *
+    * @param uuid
+    *           what to shutdown
+    */
+   @POST
+   @Path("/servers/{uuid}/shutdown")
+   void shutdownServer(@PathParam("uuid") String uuid);
+
+
+   /**
+    * Reset a server
+    *
+    * @param uuid
+    *           what to reset
+    */
+   @POST
+   @Path("/servers/{uuid}/reset")
+   void resetServer(@PathParam("uuid") String uuid);
+
+   /**
+    * list of drive uuids in your account
+    *
+    * @return or empty set if no drives are found
+    */
+   @GET
+   @Path("/drives/list")
+   @ResponseParser(SplitNewlines.class)
+   Set<String> listDrives();
+
+   /**
+    * Get all drives info
+    *
+    * @return or empty set if no drives are found
+    */
+   @GET
+   @Path("/drives/info")
+   @ResponseParser(ListOfKeyValuesDelimitedByBlankLinesToDriveInfoSet.class)
+   Set<DriveInfo> listDriveInfo();
+
+   /**
+    * @param uuid
+    *           what to get
+    * @return null, if not found
+    */
+   @GET
+   @Fallback(NullOnNotFoundOr404.class)
+   @ResponseParser(KeyValuesDelimitedByBlankLinesToDriveInfo.class)
+   @Path("/drives/{uuid}/info")
+   DriveInfo getDriveInfo(@PathParam("uuid") String uuid);
+
+   /**
+    * create a new drive
+    *
+    * @param createDrive
+    *           required parameters: name, size
+    * @return newly created drive
+    */
+   @POST
+   @Fallback(NullOnNotFoundOr404.class)
+   @ResponseParser(KeyValuesDelimitedByBlankLinesToDriveInfo.class)
+   @Path("/drives/create")
+   DriveInfo createDrive(@BinderParam(BindDriveToPlainTextString.class) Drive createDrive);
+
+   /**
+    * set extra drive data
+    *
+    * @param uuid
+    *           what drive to change
+    * @param setDrive
+    *           what values to change
+    * @return new data
+    */
+   @POST
+   @Fallback(NullOnNotFoundOr404.class)
+   @ResponseParser(KeyValuesDelimitedByBlankLinesToDriveInfo.class)
+   @Path("/drives/{uuid}/set")
+   DriveInfo setDriveData(@PathParam("uuid") String uuid,
+                                            @BinderParam(BindDriveDataToPlainTextString.class) DriveData setDrive);
+
+   /**
+    * Destroy a drive
+    *
+    * @param uuid
+    *           what to delete
+    */
+   @POST
+   @Path("/drives/{uuid}/destroy")
+   @Fallback(VoidOnNotFoundOr404.class)
+   void destroyDrive(@PathParam("uuid") String uuid);
+
+   /**
+    * create and start a new server
+    *
+    * @param createServer
+    * @return newly created server
+    */
+   @POST
+   @Fallback(NullOnNotFoundOr404.class)
+   @ResponseParser(KeyValuesDelimitedByBlankLinesToServerInfo.class)
+   @Path("/servers/create")
+   ServerInfo createAndStartServer(
+           @BinderParam(BindServerToPlainTextString.class) Server createServer);
+
+   /**
+    * Image a drive from another drive. The actual imaging process is asynchronous, with progress
+    * reported via drive info.
+    *
+    * @param source
+    *           drive to copy from
+    * @param destination
+    *           drive to copy to
+    */
+   @POST
+   @Path("/drives/{destination}/image/{source}")
+   @Fallback(VoidOnNotFoundOr404.class)
+   void imageDrive(@PathParam("source") String source, @PathParam("destination") String destination);
+
+   /**
+    * @see #imageDrive(String, String)
+    * @param conversionType
+    *           Supports 'gzip' or 'gunzip' conversions.
+    */
+   @POST
+   @Path("/drives/{destination}/image/{source}/{conversion}")
+   @Fallback(VoidOnNotFoundOr404.class)
+   void imageDrive(@PathParam("source") String source, @PathParam("destination") String destination,
+                                     @PathParam("conversion") ImageConversionType conversionType);
+
+   /**
+    * Read binary data from a drive
+    *
+    * @param uuid
+    *           drive to read
+    * @param offset
+    *           start at the specified offset in bytes
+    * @param size
+    *           the specified size in bytes; must be <=4096k
+    * @return binary content of the drive.
+    */
+   @POST
+   @Consumes(MediaType.APPLICATION_OCTET_STREAM)
+   @Path("/drives/{uuid}/read/{offset}/{size}")
+   @ResponseParser(ReturnPayload.class)
+   @Fallback(NullOnNotFoundOr404.class)
+   Payload readDrive(@PathParam("uuid") String uuid, @PathParam("offset") long offset,
+                                       @PathParam("size") long size);
+
+   /**
+    * Write binary data to a drive
+    *
+    * @param uuid
+    *           drive to write
+    * @param content
+    *           what to write.
+    *           <ul>
+    *           <li>Binary data (Content-Type: application/octet-stream)</li>
+    *           <li>Supports raw data or Content-Encoding: gzip</li>
+    *           <li>Does not support Transfer-Encoding: chunked</li>
+    *           </ul>
+    */
+   @POST
+   @Produces(MediaType.APPLICATION_OCTET_STREAM)
+   @Path("/drives/{uuid}/write")
+   @Fallback(VoidOnNotFoundOr404.class)
+   void writeDrive(@PathParam("uuid") String uuid, Payload content);
+
+   /**
+    * @see ElasticStackApi#writeDrive(String, Payload)
+    * @param offset
+    *           the byte offset in the target drive at which to start writing, not an offset in the
+    *           input stream.
+    */
+   @POST
+   @Produces(MediaType.APPLICATION_OCTET_STREAM)
+   @Path("/drives/{uuid}/write/{offset}")
+   @Fallback(VoidOnNotFoundOr404.class)
+   void writeDrive(@PathParam("uuid") String uuid, Payload content, @PathParam("offset") long offset);
+}

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/a2af31c4/apis/elasticstack/src/main/java/org/jclouds/elasticstack/ElasticStackApiMetadata.java
----------------------------------------------------------------------
diff --git a/apis/elasticstack/src/main/java/org/jclouds/elasticstack/ElasticStackApiMetadata.java b/apis/elasticstack/src/main/java/org/jclouds/elasticstack/ElasticStackApiMetadata.java
index 2e5f91a..ed42e7d 100644
--- a/apis/elasticstack/src/main/java/org/jclouds/elasticstack/ElasticStackApiMetadata.java
+++ b/apis/elasticstack/src/main/java/org/jclouds/elasticstack/ElasticStackApiMetadata.java
@@ -25,11 +25,10 @@ import java.util.Properties;
 import org.jclouds.apis.ApiMetadata;
 import org.jclouds.compute.ComputeServiceContext;
 import org.jclouds.elasticstack.compute.config.ElasticStackComputeServiceContextModule;
-import org.jclouds.elasticstack.config.ElasticStackRestClientModule;
-import org.jclouds.rest.internal.BaseRestApiMetadata;
+import org.jclouds.elasticstack.config.ElasticStackHttpApiModule;
+import org.jclouds.rest.internal.BaseHttpApiMetadata;
 
 import com.google.common.collect.ImmutableSet;
-import com.google.common.reflect.TypeToken;
 import com.google.inject.Module;
 
 /**
@@ -37,17 +36,8 @@ import com.google.inject.Module;
  * 
  * @author Adrian Cole
  */
-public class ElasticStackApiMetadata extends BaseRestApiMetadata {
+public class ElasticStackApiMetadata extends BaseHttpApiMetadata<ElasticStackApi> {
 
-   /**
-    * @deprecated please use {@code org.jclouds.ContextBuilder#buildApi(ElasticStackClient.class)} as
-    *             {@link ElasticStackAsyncClient} interface will be removed in jclouds 1.7.
-    */
-   @Deprecated
-   public static final TypeToken<org.jclouds.rest.RestContext<ElasticStackClient, ElasticStackAsyncClient>> CONTEXT_TOKEN = new TypeToken<org.jclouds.rest.RestContext<ElasticStackClient, ElasticStackAsyncClient>>() {
-      private static final long serialVersionUID = 1L;
-   };
-   
    @Override
    public Builder toBuilder() {
       return new Builder().fromApiMetadata(this);
@@ -62,7 +52,7 @@ public class ElasticStackApiMetadata extends BaseRestApiMetadata {
    }
 
    public static Properties defaultProperties() {
-      Properties properties = BaseRestApiMetadata.defaultProperties();
+      Properties properties = BaseHttpApiMetadata.defaultProperties();
       properties.setProperty(PROPERTY_VNC_PASSWORD, "IL9vs34d");
       // passwords are set post-boot, so auth failures are possible
       // from a race condition applying the password set script
@@ -71,11 +61,10 @@ public class ElasticStackApiMetadata extends BaseRestApiMetadata {
       return properties;
    }
 
-   public static class Builder extends BaseRestApiMetadata.Builder<Builder> {
+   public static class Builder extends BaseHttpApiMetadata.Builder<ElasticStackApi, Builder> {
 
       @SuppressWarnings("deprecation")
       protected Builder() {
-         super(ElasticStackClient.class, ElasticStackAsyncClient.class);
          id("elasticstack")
          .name("ElasticStack API")
          .identityName("UUID")
@@ -85,7 +74,7 @@ public class ElasticStackApiMetadata extends BaseRestApiMetadata {
          .defaultEndpoint("https://api-lon-p.elastichosts.com")
          .defaultProperties(ElasticStackApiMetadata.defaultProperties())
          .view(typeToken(ComputeServiceContext.class))
-         .defaultModules(ImmutableSet.<Class<? extends Module>>of(ElasticStackRestClientModule.class, ElasticStackComputeServiceContextModule.class));
+         .defaultModules(ImmutableSet.<Class<? extends Module>>of(ElasticStackHttpApiModule.class, ElasticStackComputeServiceContextModule.class));
       }
 
       @Override

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/a2af31c4/apis/elasticstack/src/main/java/org/jclouds/elasticstack/ElasticStackAsyncClient.java
----------------------------------------------------------------------
diff --git a/apis/elasticstack/src/main/java/org/jclouds/elasticstack/ElasticStackAsyncClient.java b/apis/elasticstack/src/main/java/org/jclouds/elasticstack/ElasticStackAsyncClient.java
deleted file mode 100644
index a348a83..0000000
--- a/apis/elasticstack/src/main/java/org/jclouds/elasticstack/ElasticStackAsyncClient.java
+++ /dev/null
@@ -1,259 +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 org.jclouds.elasticstack;
-
-import java.io.Closeable;
-import java.util.Set;
-
-import javax.ws.rs.Consumes;
-import javax.ws.rs.GET;
-import javax.ws.rs.POST;
-import javax.ws.rs.Path;
-import javax.ws.rs.PathParam;
-import javax.ws.rs.Produces;
-import javax.ws.rs.core.MediaType;
-
-import org.jclouds.Fallbacks.NullOnNotFoundOr404;
-import org.jclouds.Fallbacks.VoidOnNotFoundOr404;
-import org.jclouds.elasticstack.binders.BindDriveDataToPlainTextString;
-import org.jclouds.elasticstack.binders.BindDriveToPlainTextString;
-import org.jclouds.elasticstack.binders.BindServerToPlainTextString;
-import org.jclouds.elasticstack.domain.Drive;
-import org.jclouds.elasticstack.domain.DriveData;
-import org.jclouds.elasticstack.domain.DriveInfo;
-import org.jclouds.elasticstack.domain.ImageConversionType;
-import org.jclouds.elasticstack.domain.Server;
-import org.jclouds.elasticstack.domain.ServerInfo;
-import org.jclouds.elasticstack.functions.KeyValuesDelimitedByBlankLinesToDriveInfo;
-import org.jclouds.elasticstack.functions.KeyValuesDelimitedByBlankLinesToServerInfo;
-import org.jclouds.elasticstack.functions.ListOfKeyValuesDelimitedByBlankLinesToDriveInfoSet;
-import org.jclouds.elasticstack.functions.ListOfKeyValuesDelimitedByBlankLinesToServerInfoSet;
-import org.jclouds.elasticstack.functions.ReturnPayload;
-import org.jclouds.elasticstack.functions.SplitNewlines;
-import org.jclouds.http.filters.BasicAuthentication;
-import org.jclouds.io.Payload;
-import org.jclouds.rest.annotations.BinderParam;
-import org.jclouds.rest.annotations.Fallback;
-import org.jclouds.rest.annotations.RequestFilters;
-import org.jclouds.rest.annotations.ResponseParser;
-
-import com.google.common.util.concurrent.ListenableFuture;
-
-/**
- * Provides asynchronous access to elasticstack via their REST API.
- * <p/>
- * 
- * @see ElasticStackClient
- * @see <a href="TODO: insert URL of provider documentation" />
- * @author Adrian Cole
- * @deprecated please use {@code org.jclouds.ContextBuilder#buildApi(ElasticStackClient.class)} as
- *             {@link ElasticStackAsyncClient} interface will be removed in jclouds 1.7.
- */
-@Deprecated
-@RequestFilters(BasicAuthentication.class)
-@Consumes(MediaType.TEXT_PLAIN)
-public interface ElasticStackAsyncClient extends Closeable {
-
-   /**
-    * @see ElasticStackClient#listServers()
-    */
-   @GET
-   @Path("/servers/list")
-   @ResponseParser(SplitNewlines.class)
-   ListenableFuture<Set<String>> listServers();
-
-   /**
-    * @see ElasticStackClient#listServerInfo()
-    */
-   @GET
-   @Path("/servers/info")
-   @ResponseParser(ListOfKeyValuesDelimitedByBlankLinesToServerInfoSet.class)
-   ListenableFuture<Set<ServerInfo>> listServerInfo();
-
-   /**
-    * @see ElasticStackClient#getServerInfo
-    */
-   @GET
-   @Fallback(NullOnNotFoundOr404.class)
-   @ResponseParser(KeyValuesDelimitedByBlankLinesToServerInfo.class)
-   @Path("/servers/{uuid}/info")
-   ListenableFuture<ServerInfo> getServerInfo(@PathParam("uuid") String uuid);
-
-   /**
-    * @see ElasticStackClient#createServer
-    */
-   @POST
-   @Fallback(NullOnNotFoundOr404.class)
-   @ResponseParser(KeyValuesDelimitedByBlankLinesToServerInfo.class)
-   @Path("/servers/create/stopped")
-   ListenableFuture<ServerInfo> createServer(
-         @BinderParam(BindServerToPlainTextString.class) Server createServer);
-
-   /**
-    * @see ElasticStackClient#setServerConfiguration
-    */
-   @POST
-   @Fallback(NullOnNotFoundOr404.class)
-   @ResponseParser(KeyValuesDelimitedByBlankLinesToServerInfo.class)
-   @Path("/servers/{uuid}/set")
-   ListenableFuture<ServerInfo> setServerConfiguration(@PathParam("uuid") String uuid,
-         @BinderParam(BindServerToPlainTextString.class) Server setServer);
-
-   /**
-    * @see ElasticStackClient#destroyServer
-    */
-   @POST
-   @Path("/servers/{uuid}/destroy")
-   @Fallback(VoidOnNotFoundOr404.class)
-   ListenableFuture<Void> destroyServer(@PathParam("uuid") String uuid);
-
-   /**
-    * @see ElasticStackClient#startServer
-    */
-   @POST
-   @Path("/servers/{uuid}/start")
-   ListenableFuture<Void> startServer(@PathParam("uuid") String uuid);
-
-   /**
-    * @see ElasticStackClient#stopServer
-    */
-   @POST
-   @Path("/servers/{uuid}/stop")
-   ListenableFuture<Void> stopServer(@PathParam("uuid") String uuid);
-
-   /**
-    * @see ElasticStackClient#shutdownServer
-    */
-   @POST
-   @Path("/servers/{uuid}/shutdown")
-   ListenableFuture<Void> shutdownServer(@PathParam("uuid") String uuid);
-
-   /**
-    * @see ElasticStackClient#resetServer
-    */
-   @POST
-   @Path("/servers/{uuid}/reset")
-   ListenableFuture<Void> resetServer(@PathParam("uuid") String uuid);
-
-   /**
-    * @see ElasticStackClient#listDrives()
-    */
-   @GET
-   @Path("/drives/list")
-   @ResponseParser(SplitNewlines.class)
-   ListenableFuture<Set<String>> listDrives();
-
-   /**
-    * @see ElasticStackClient#listDriveInfo()
-    */
-   @GET
-   @Path("/drives/info")
-   @ResponseParser(ListOfKeyValuesDelimitedByBlankLinesToDriveInfoSet.class)
-   ListenableFuture<Set<DriveInfo>> listDriveInfo();
-
-   /**
-    * @see ElasticStackClient#getDriveInfo
-    */
-   @GET
-   @Fallback(NullOnNotFoundOr404.class)
-   @ResponseParser(KeyValuesDelimitedByBlankLinesToDriveInfo.class)
-   @Path("/drives/{uuid}/info")
-   ListenableFuture<DriveInfo> getDriveInfo(@PathParam("uuid") String uuid);
-
-   /**
-    * @see ElasticStackClient#createDrive
-    */
-   @POST
-   @Fallback(NullOnNotFoundOr404.class)
-   @ResponseParser(KeyValuesDelimitedByBlankLinesToDriveInfo.class)
-   @Path("/drives/create")
-   ListenableFuture<DriveInfo> createDrive(@BinderParam(BindDriveToPlainTextString.class) Drive createDrive);
-
-   /**
-    * @see ElasticStackClient#setDriveData
-    */
-   @POST
-   @Fallback(NullOnNotFoundOr404.class)
-   @ResponseParser(KeyValuesDelimitedByBlankLinesToDriveInfo.class)
-   @Path("/drives/{uuid}/set")
-   ListenableFuture<DriveInfo> setDriveData(@PathParam("uuid") String uuid,
-         @BinderParam(BindDriveDataToPlainTextString.class) DriveData setDrive);
-
-   /**
-    * @see ElasticStackClient#destroyDrive
-    */
-   @POST
-   @Path("/drives/{uuid}/destroy")
-   @Fallback(VoidOnNotFoundOr404.class)
-   ListenableFuture<Void> destroyDrive(@PathParam("uuid") String uuid);
-
-   /**
-    * @see ElasticStackClient#createAndStartServer
-    */
-   @POST
-   @Fallback(NullOnNotFoundOr404.class)
-   @ResponseParser(KeyValuesDelimitedByBlankLinesToServerInfo.class)
-   @Path("/servers/create")
-   ListenableFuture<ServerInfo> createAndStartServer(
-         @BinderParam(BindServerToPlainTextString.class) Server createServer);
-
-   /**
-    * @see ElasticStackClient#imageDrive(String,String)
-    */
-   @POST
-   @Path("/drives/{destination}/image/{source}")
-   @Fallback(VoidOnNotFoundOr404.class)
-   ListenableFuture<Void> imageDrive(@PathParam("source") String source, @PathParam("destination") String destination);
-
-   /**
-    * @see ElasticStackClient#imageDrive(String,String,ImageConversionType)
-    */
-   @POST
-   @Path("/drives/{destination}/image/{source}/{conversion}")
-   @Fallback(VoidOnNotFoundOr404.class)
-   ListenableFuture<Void> imageDrive(@PathParam("source") String source, @PathParam("destination") String destination,
-         @PathParam("conversion") ImageConversionType conversionType);
-
-   /**
-    * @see ElasticStackClient#readDrive
-    */
-   @POST
-   @Consumes(MediaType.APPLICATION_OCTET_STREAM)
-   @Path("/drives/{uuid}/read/{offset}/{size}")
-   @ResponseParser(ReturnPayload.class)
-   @Fallback(NullOnNotFoundOr404.class)
-   ListenableFuture<Payload> readDrive(@PathParam("uuid") String uuid, @PathParam("offset") long offset,
-         @PathParam("size") long size);
-
-   /**
-    * @see ElasticStackClient#writeDrive(String, Payload)
-    */
-   @POST
-   @Produces(MediaType.APPLICATION_OCTET_STREAM)
-   @Path("/drives/{uuid}/write")
-   @Fallback(VoidOnNotFoundOr404.class)
-   ListenableFuture<Void> writeDrive(@PathParam("uuid") String uuid, Payload content);
-
-   /**
-    * @see ElasticStackClient#writeDrive(String, Payload, long)
-    */
-   @POST
-   @Produces(MediaType.APPLICATION_OCTET_STREAM)
-   @Path("/drives/{uuid}/write/{offset}")
-   @Fallback(VoidOnNotFoundOr404.class)
-   ListenableFuture<Void> writeDrive(@PathParam("uuid") String uuid, Payload content, @PathParam("offset") long offset);
-}

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/a2af31c4/apis/elasticstack/src/main/java/org/jclouds/elasticstack/ElasticStackClient.java
----------------------------------------------------------------------
diff --git a/apis/elasticstack/src/main/java/org/jclouds/elasticstack/ElasticStackClient.java b/apis/elasticstack/src/main/java/org/jclouds/elasticstack/ElasticStackClient.java
deleted file mode 100644
index 5f4666d..0000000
--- a/apis/elasticstack/src/main/java/org/jclouds/elasticstack/ElasticStackClient.java
+++ /dev/null
@@ -1,237 +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 org.jclouds.elasticstack;
-
-import java.io.Closeable;
-import java.util.Set;
-import org.jclouds.elasticstack.domain.Drive;
-import org.jclouds.elasticstack.domain.DriveData;
-import org.jclouds.elasticstack.domain.DriveInfo;
-import org.jclouds.elasticstack.domain.ImageConversionType;
-import org.jclouds.elasticstack.domain.Server;
-import org.jclouds.elasticstack.domain.ServerInfo;
-import org.jclouds.io.Payload;
-
-/**
- * Provides synchronous access to elasticstack.
- * <p/>
- * 
- * @see ElasticStackAsyncClient
- * @see <a href="TODO: insert URL of elasticstack documentation" />
- * @author Adrian Cole
- */
-public interface ElasticStackClient extends Closeable {
-   /**
-    * list of server uuids in your account
-    * 
-    * @return or empty set if no servers are found
-    */
-   Set<String> listServers();
-
-   /**
-    * Get all servers info
-    * 
-    * @return or empty set if no servers are found
-    */
-   Set<? extends ServerInfo> listServerInfo();
-
-   /**
-    * @param uuid
-    *           what to get
-    * @return null, if not found
-    */
-   ServerInfo getServerInfo(String uuid);
-
-   /**
-    * create a new server
-    * 
-    * @param server
-    * @return newly created server
-    */
-   ServerInfo createServer(Server server);
-
-   /**
-    * set server configuration
-    * 
-    * @param uuid
-    *           what server to change
-    * @param serverData
-    *           what values to change
-    * @return new data
-    */
-   ServerInfo setServerConfiguration(String uuid, Server server);
-
-   /**
-    * Destroy a server
-    * 
-    * @param uuid
-    *           what to destroy
-    */
-   void destroyServer(String uuid);
-
-   /**
-    * Start a server
-    * 
-    * @param uuid
-    *           what to start
-    */
-   void startServer(String uuid);
-
-   /**
-    * Stop a server
-    * <p/>
-    * Kills the server immediately, equivalent to a power failure. Server reverts to a stopped
-    * status if it is persistent and is automatically destroyed otherwise.
-    * 
-    * @param uuid
-    *           what to stop
-    */
-   void stopServer(String uuid);
-
-   /**
-    * Shutdown a server
-    * <p/>
-    * Sends the server an ACPI power-down event. Server reverts to a stopped status if it is
-    * persistent and is automatically destroyed otherwise.
-    * <h4>note</h4> behaviour on shutdown depends on how your server OS is set up to respond to an
-    * ACPI power button signal.
-    * 
-    * @param uuid
-    *           what to shutdown
-    */
-   void shutdownServer(String uuid);
-
-   /**
-    * Reset a server
-    * 
-    * @param uuid
-    *           what to reset
-    */
-   void resetServer(String uuid);
-
-   /**
-    * list of drive uuids in your account
-    * 
-    * @return or empty set if no drives are found
-    */
-   Set<String> listDrives();
-
-   /**
-    * Get all drives info
-    * 
-    * @return or empty set if no drives are found
-    */
-   Set<? extends DriveInfo> listDriveInfo();
-
-   /**
-    * @param uuid
-    *           what to get
-    * @return null, if not found
-    */
-   DriveInfo getDriveInfo(String uuid);
-
-   /**
-    * create a new drive
-    * 
-    * @param createDrive
-    *           required parameters: name, size
-    * @return newly created drive
-    */
-   DriveInfo createDrive(Drive createDrive);
-
-   /**
-    * set extra drive data
-    * 
-    * @param uuid
-    *           what drive to change
-    * @param driveData
-    *           what values to change
-    * @return new data
-    */
-   DriveInfo setDriveData(String uuid, DriveData driveData);
-
-   /**
-    * Destroy a drive
-    * 
-    * @param uuid
-    *           what to delete
-    */
-   void destroyDrive(String uuid);
-
-   /**
-    * create and start a new server
-    * 
-    * @param server
-    * @return newly created server
-    */
-   ServerInfo createAndStartServer(Server server);
-
-   /**
-    * Image a drive from another drive. The actual imaging process is asynchronous, with progress
-    * reported via drive info.
-    * 
-    * @param source
-    *           drive to copy from
-    * @param destination
-    *           drive to copy to
-    */
-   void imageDrive(String source, String destination);
-
-   /**
-    * @see #imageDrive(String, String)
-    * @param conversionType
-    *           Supports 'gzip' or 'gunzip' conversions.
-    */
-   void imageDrive(String source, String destination, ImageConversionType conversionType);
-
-   /**
-    * Read binary data from a drive
-    * 
-    * @param uuid
-    *           drive to read
-    * @param offset
-    *           start at the specified offset in bytes
-    * @param size
-    *           the specified size in bytes; must be <=4096k
-    * @return binary content of the drive.
-    */
-   Payload readDrive(String uuid, long offset, long size);
-
-   /**
-    * Write binary data to a drive
-    * 
-    * @param uuid
-    *           drive to write
-    * @param content
-    *           what to write.
-    *           <ul>
-    *           <li>Binary data (Content-Type: application/octet-stream)</li>
-    *           <li>Supports raw data or Content-Encoding: gzip</li>
-    *           <li>Does not support Transfer-Encoding: chunked</li>
-    *           </ul>
-    */
-   void writeDrive(String uuid, Payload content);
-
-   /**
-    * @see ElasticStackClient#writeDrive(String, Payload)
-    * @param offset
-    *           the byte offset in the target drive at which to start writing, not an offset in the
-    *           input stream.
-    */
-   void writeDrive(String uuid, Payload content, long offset);
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/a2af31c4/apis/elasticstack/src/main/java/org/jclouds/elasticstack/compute/ElasticStackComputeServiceAdapter.java
----------------------------------------------------------------------
diff --git a/apis/elasticstack/src/main/java/org/jclouds/elasticstack/compute/ElasticStackComputeServiceAdapter.java b/apis/elasticstack/src/main/java/org/jclouds/elasticstack/compute/ElasticStackComputeServiceAdapter.java
index 8b7cfba..94a46b9 100644
--- a/apis/elasticstack/src/main/java/org/jclouds/elasticstack/compute/ElasticStackComputeServiceAdapter.java
+++ b/apis/elasticstack/src/main/java/org/jclouds/elasticstack/compute/ElasticStackComputeServiceAdapter.java
@@ -44,7 +44,7 @@ import org.jclouds.compute.domain.internal.VolumeImpl;
 import org.jclouds.compute.reference.ComputeServiceConstants;
 import org.jclouds.domain.Location;
 import org.jclouds.domain.LoginCredentials;
-import org.jclouds.elasticstack.ElasticStackClient;
+import org.jclouds.elasticstack.ElasticStackApi;
 import org.jclouds.elasticstack.domain.Device;
 import org.jclouds.elasticstack.domain.Drive;
 import org.jclouds.elasticstack.domain.DriveInfo;
@@ -64,21 +64,20 @@ import com.google.common.collect.FluentIterable;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.ImmutableSet.Builder;
-import com.google.common.collect.Iterables;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
 import com.google.common.util.concurrent.ListeningExecutorService;
 import com.google.common.util.concurrent.UncheckedExecutionException;
 
 /**
- * defines the connection between the {@link ElasticStackClient} implementation
+ * defines the connection between the {@link org.jclouds.elasticstack.ElasticStackApi} implementation
  * and the jclouds {@link ComputeService}
  * 
  */
 @Singleton
 public class ElasticStackComputeServiceAdapter implements
       ComputeServiceAdapter<ServerInfo, Hardware, DriveInfo, Location> {
-   private final ElasticStackClient client;
+   private final ElasticStackApi client;
    private final Predicate<DriveInfo> driveNotClaimed;
    private final Map<String, WellKnownImage> preinstalledImages;
    private final LoadingCache<String, DriveInfo> cache;
@@ -90,7 +89,7 @@ public class ElasticStackComputeServiceAdapter implements
    protected Logger logger = Logger.NULL;
 
    @Inject
-   public ElasticStackComputeServiceAdapter(ElasticStackClient client, Predicate<DriveInfo> driveNotClaimed,
+   public ElasticStackComputeServiceAdapter(ElasticStackApi client, Predicate<DriveInfo> driveNotClaimed,
          Map<String, WellKnownImage> preinstalledImages, LoadingCache<String, DriveInfo> cache,
          @Named(ElasticStackConstants.PROPERTY_VNC_PASSWORD) String defaultVncPassword,
          @Named(Constants.PROPERTY_USER_THREADS) ListeningExecutorService userExecutor) {

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/a2af31c4/apis/elasticstack/src/main/java/org/jclouds/elasticstack/compute/config/ElasticStackComputeServiceContextModule.java
----------------------------------------------------------------------
diff --git a/apis/elasticstack/src/main/java/org/jclouds/elasticstack/compute/config/ElasticStackComputeServiceContextModule.java b/apis/elasticstack/src/main/java/org/jclouds/elasticstack/compute/config/ElasticStackComputeServiceContextModule.java
index 5b68fde..4aba9af 100644
--- a/apis/elasticstack/src/main/java/org/jclouds/elasticstack/compute/config/ElasticStackComputeServiceContextModule.java
+++ b/apis/elasticstack/src/main/java/org/jclouds/elasticstack/compute/config/ElasticStackComputeServiceContextModule.java
@@ -34,7 +34,7 @@ import org.jclouds.compute.domain.NodeMetadata;
 import org.jclouds.compute.domain.Volume;
 import org.jclouds.compute.reference.ComputeServiceConstants.Timeouts;
 import org.jclouds.domain.Location;
-import org.jclouds.elasticstack.ElasticStackClient;
+import org.jclouds.elasticstack.ElasticStackApi;
 import org.jclouds.elasticstack.compute.ElasticStackComputeServiceAdapter;
 import org.jclouds.elasticstack.compute.functions.ServerInfoToNodeMetadata;
 import org.jclouds.elasticstack.compute.functions.ServerInfoToNodeMetadata.DeviceToVolume;
@@ -98,10 +98,10 @@ public class ElasticStackComputeServiceContextModule extends
 
    @Singleton
    public static class GetDrive extends CacheLoader<String, DriveInfo> {
-      private final ElasticStackClient client;
+      private final ElasticStackApi client;
 
       @Inject
-      public GetDrive(ElasticStackClient client) {
+      public GetDrive(ElasticStackApi client) {
          this.client = client;
       }
 

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/a2af31c4/apis/elasticstack/src/main/java/org/jclouds/elasticstack/config/ElasticStackHttpApiModule.java
----------------------------------------------------------------------
diff --git a/apis/elasticstack/src/main/java/org/jclouds/elasticstack/config/ElasticStackHttpApiModule.java b/apis/elasticstack/src/main/java/org/jclouds/elasticstack/config/ElasticStackHttpApiModule.java
new file mode 100644
index 0000000..bfbce04
--- /dev/null
+++ b/apis/elasticstack/src/main/java/org/jclouds/elasticstack/config/ElasticStackHttpApiModule.java
@@ -0,0 +1,90 @@
+/*
+ * 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 org.jclouds.elasticstack.config;
+
+import java.util.List;
+import java.util.Map;
+
+import org.jclouds.elasticstack.ElasticStackApi;
+import org.jclouds.elasticstack.domain.Device;
+import org.jclouds.elasticstack.domain.Drive;
+import org.jclouds.elasticstack.domain.DriveData;
+import org.jclouds.elasticstack.domain.DriveMetrics;
+import org.jclouds.elasticstack.domain.NIC;
+import org.jclouds.elasticstack.domain.Server;
+import org.jclouds.elasticstack.domain.ServerMetrics;
+import org.jclouds.elasticstack.functions.CreateDriveRequestToMap;
+import org.jclouds.elasticstack.functions.DriveDataToMap;
+import org.jclouds.elasticstack.functions.MapToDevices;
+import org.jclouds.elasticstack.functions.MapToDevices.DeviceToId;
+import org.jclouds.elasticstack.functions.MapToDriveMetrics;
+import org.jclouds.elasticstack.functions.MapToNICs;
+import org.jclouds.elasticstack.functions.MapToServerMetrics;
+import org.jclouds.elasticstack.functions.ServerToMap;
+import org.jclouds.elasticstack.handlers.ElasticStackErrorHandler;
+import org.jclouds.http.HttpErrorHandler;
+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.config.HttpApiModule;
+
+import com.google.common.base.Function;
+import com.google.inject.TypeLiteral;
+
+/**
+ * Configures the elasticstack connection.
+ * 
+ * @author Adrian Cole
+ */
+@ConfiguresRestClient
+public class ElasticStackHttpApiModule extends HttpApiModule<ElasticStackApi> {
+
+   @Override
+   protected void configure() {
+      super.configure();
+      bind(new TypeLiteral<Function<Drive, Map<String, String>>>() {
+      }).to(CreateDriveRequestToMap.class);
+      bind(new TypeLiteral<Function<DriveData, Map<String, String>>>() {
+      }).to(DriveDataToMap.class);
+      bind(new TypeLiteral<Function<Map<String, String>, List<NIC>>>() {
+      }).to(MapToNICs.class);
+      bind(new TypeLiteral<Function<Map<String, String>, Map<String, ? extends Device>>>() {
+      }).to(MapToDevices.class);
+      bind(new TypeLiteral<Function<Map<String, String>, Map<String, ? extends DriveMetrics>>>() {
+      }).to(MapToDriveMetrics.class);
+      bind(new TypeLiteral<Function<Map<String, String>, ServerMetrics>>() {
+      }).to(MapToServerMetrics.class);
+      bind(new TypeLiteral<Function<Device, String>>() {
+      }).to(DeviceToId.class);
+      bind(new TypeLiteral<Function<Server, Map<String, String>>>() {
+      }).to(ServerToMap.class);
+   }
+
+   @Override
+   protected void bindErrorHandlers() {
+      bind(HttpErrorHandler.class).annotatedWith(Redirection.class).to(ElasticStackErrorHandler.class);
+      bind(HttpErrorHandler.class).annotatedWith(ClientError.class).to(ElasticStackErrorHandler.class);
+      bind(HttpErrorHandler.class).annotatedWith(ServerError.class).to(ElasticStackErrorHandler.class);
+   }
+
+   @Override
+   protected void bindRetryHandlers() {
+      // TODO
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/a2af31c4/apis/elasticstack/src/main/java/org/jclouds/elasticstack/config/ElasticStackRestClientModule.java
----------------------------------------------------------------------
diff --git a/apis/elasticstack/src/main/java/org/jclouds/elasticstack/config/ElasticStackRestClientModule.java b/apis/elasticstack/src/main/java/org/jclouds/elasticstack/config/ElasticStackRestClientModule.java
deleted file mode 100644
index 961bc14..0000000
--- a/apis/elasticstack/src/main/java/org/jclouds/elasticstack/config/ElasticStackRestClientModule.java
+++ /dev/null
@@ -1,91 +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 org.jclouds.elasticstack.config;
-
-import java.util.List;
-import java.util.Map;
-
-import org.jclouds.elasticstack.ElasticStackAsyncClient;
-import org.jclouds.elasticstack.ElasticStackClient;
-import org.jclouds.elasticstack.domain.Device;
-import org.jclouds.elasticstack.domain.Drive;
-import org.jclouds.elasticstack.domain.DriveData;
-import org.jclouds.elasticstack.domain.DriveMetrics;
-import org.jclouds.elasticstack.domain.NIC;
-import org.jclouds.elasticstack.domain.Server;
-import org.jclouds.elasticstack.domain.ServerMetrics;
-import org.jclouds.elasticstack.functions.CreateDriveRequestToMap;
-import org.jclouds.elasticstack.functions.DriveDataToMap;
-import org.jclouds.elasticstack.functions.MapToDevices;
-import org.jclouds.elasticstack.functions.MapToDriveMetrics;
-import org.jclouds.elasticstack.functions.MapToNICs;
-import org.jclouds.elasticstack.functions.MapToServerMetrics;
-import org.jclouds.elasticstack.functions.ServerToMap;
-import org.jclouds.elasticstack.functions.MapToDevices.DeviceToId;
-import org.jclouds.elasticstack.handlers.ElasticStackErrorHandler;
-import org.jclouds.http.HttpErrorHandler;
-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.config.RestClientModule;
-
-import com.google.common.base.Function;
-import com.google.inject.TypeLiteral;
-
-/**
- * Configures the elasticstack connection.
- * 
- * @author Adrian Cole
- */
-@ConfiguresRestClient
-public class ElasticStackRestClientModule extends RestClientModule<ElasticStackClient, ElasticStackAsyncClient> {
-
-   @Override
-   protected void configure() {
-      super.configure();
-      bind(new TypeLiteral<Function<Drive, Map<String, String>>>() {
-      }).to(CreateDriveRequestToMap.class);
-      bind(new TypeLiteral<Function<DriveData, Map<String, String>>>() {
-      }).to(DriveDataToMap.class);
-      bind(new TypeLiteral<Function<Map<String, String>, List<NIC>>>() {
-      }).to(MapToNICs.class);
-      bind(new TypeLiteral<Function<Map<String, String>, Map<String, ? extends Device>>>() {
-      }).to(MapToDevices.class);
-      bind(new TypeLiteral<Function<Map<String, String>, Map<String, ? extends DriveMetrics>>>() {
-      }).to(MapToDriveMetrics.class);
-      bind(new TypeLiteral<Function<Map<String, String>, ServerMetrics>>() {
-      }).to(MapToServerMetrics.class);
-      bind(new TypeLiteral<Function<Device, String>>() {
-      }).to(DeviceToId.class);
-      bind(new TypeLiteral<Function<Server, Map<String, String>>>() {
-      }).to(ServerToMap.class);
-   }
-
-   @Override
-   protected void bindErrorHandlers() {
-      bind(HttpErrorHandler.class).annotatedWith(Redirection.class).to(ElasticStackErrorHandler.class);
-      bind(HttpErrorHandler.class).annotatedWith(ClientError.class).to(ElasticStackErrorHandler.class);
-      bind(HttpErrorHandler.class).annotatedWith(ServerError.class).to(ElasticStackErrorHandler.class);
-   }
-
-   @Override
-   protected void bindRetryHandlers() {
-      // TODO
-   }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/a2af31c4/apis/elasticstack/src/main/java/org/jclouds/elasticstack/predicates/DriveClaimed.java
----------------------------------------------------------------------
diff --git a/apis/elasticstack/src/main/java/org/jclouds/elasticstack/predicates/DriveClaimed.java b/apis/elasticstack/src/main/java/org/jclouds/elasticstack/predicates/DriveClaimed.java
index ce7c08c..575a336 100644
--- a/apis/elasticstack/src/main/java/org/jclouds/elasticstack/predicates/DriveClaimed.java
+++ b/apis/elasticstack/src/main/java/org/jclouds/elasticstack/predicates/DriveClaimed.java
@@ -22,7 +22,7 @@ import javax.annotation.Resource;
 import javax.inject.Inject;
 import javax.inject.Singleton;
 
-import org.jclouds.elasticstack.ElasticStackClient;
+import org.jclouds.elasticstack.ElasticStackApi;
 import org.jclouds.elasticstack.domain.DriveInfo;
 import org.jclouds.logging.Logger;
 
@@ -35,13 +35,13 @@ import com.google.common.base.Predicate;
 @Singleton
 public class DriveClaimed implements Predicate<DriveInfo> {
 
-   private final ElasticStackClient client;
+   private final ElasticStackApi client;
 
    @Resource
    protected Logger logger = Logger.NULL;
 
    @Inject
-   public DriveClaimed(ElasticStackClient client) {
+   public DriveClaimed(ElasticStackApi client) {
       this.client = client;
    }
 

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/a2af31c4/apis/elasticstack/src/test/java/org/jclouds/elasticstack/ElasticStackApiLiveTest.java
----------------------------------------------------------------------
diff --git a/apis/elasticstack/src/test/java/org/jclouds/elasticstack/ElasticStackApiLiveTest.java b/apis/elasticstack/src/test/java/org/jclouds/elasticstack/ElasticStackApiLiveTest.java
new file mode 100644
index 0000000..2ce94fe
--- /dev/null
+++ b/apis/elasticstack/src/test/java/org/jclouds/elasticstack/ElasticStackApiLiveTest.java
@@ -0,0 +1,350 @@
+/*
+ * 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 org.jclouds.elasticstack;
+import static java.util.concurrent.TimeUnit.SECONDS;
+import static org.jclouds.util.Predicates2.retry;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+
+import java.io.IOException;
+import java.util.Set;
+import java.util.logging.Logger;
+
+import org.jclouds.compute.domain.ExecResponse;
+import org.jclouds.compute.internal.BaseComputeServiceContextLiveTest;
+import org.jclouds.domain.LoginCredentials;
+import org.jclouds.elasticstack.domain.ClaimType;
+import org.jclouds.elasticstack.domain.CreateDriveRequest;
+import org.jclouds.elasticstack.domain.DriveData;
+import org.jclouds.elasticstack.domain.DriveInfo;
+import org.jclouds.elasticstack.domain.DriveStatus;
+import org.jclouds.elasticstack.domain.IDEDevice;
+import org.jclouds.elasticstack.domain.ImageConversionType;
+import org.jclouds.elasticstack.domain.Model;
+import org.jclouds.elasticstack.domain.Server;
+import org.jclouds.elasticstack.domain.ServerInfo;
+import org.jclouds.elasticstack.domain.ServerStatus;
+import org.jclouds.elasticstack.predicates.DriveClaimed;
+import org.jclouds.elasticstack.util.Servers;
+import org.jclouds.io.Payloads;
+import org.jclouds.predicates.SocketOpen;
+import org.jclouds.ssh.SshClient;
+import org.jclouds.sshj.config.SshjSshClientModule;
+import org.jclouds.util.Strings2;
+import org.testng.annotations.AfterGroups;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.net.HostAndPort;
+import com.google.gson.Gson;
+import com.google.inject.Guice;
+
+/**
+ * Tests behavior of {@code ElasticStackApi}
+ * 
+ * @author Adrian Cole
+ */
+@Test(groups = "live", singleThreaded = true, testName = "ElasticStackApiLiveTest")
+public class ElasticStackApiLiveTest extends BaseComputeServiceContextLiveTest {
+
+   public ElasticStackApiLiveTest() {
+      provider = "elasticstack";
+   }
+
+   protected long driveSize = 1 * 1024 * 1024 * 1024l;
+   protected int maxDriveImageTime = 360;
+   protected String vncPassword = "Il0veVNC";
+   protected ElasticStackApi client;
+   protected Predicate<HostAndPort> socketTester;
+   protected Predicate<DriveInfo> driveNotClaimed;
+   protected String imageId;
+
+   @Override
+   @BeforeClass(groups = { "integration", "live" })
+   public void setupContext() {
+      super.setupContext();
+      imageId = view.getComputeService().templateBuilder().build().getImage().getId();
+         
+      client = view.utils().injector().getInstance(ElasticStackApi.class);
+      driveNotClaimed = retry(Predicates.not(new DriveClaimed(client)), maxDriveImageTime, 1, SECONDS);
+      SocketOpen socketOpen = context.utils().injector().getInstance(SocketOpen.class);
+      socketTester = retry(socketOpen, maxDriveImageTime, 1, SECONDS);
+   }
+   
+   @Test
+   public void testListServers() throws Exception {
+      Set<String> servers = client.listServers();
+      assertNotNull(servers);
+   }
+
+   @Test
+   public void testListServerInfo() throws Exception {
+      Set<? extends ServerInfo> servers = client.listServerInfo();
+      assertNotNull(servers);
+   }
+
+   @Test
+   public void testGetServer() throws Exception {
+      for (String serverUUID : client.listServers()) {
+         assert !"".equals(serverUUID);
+         assertNotNull(client.getServerInfo(serverUUID));
+      }
+   }
+
+   @Test
+   public void testListDrives() throws Exception {
+      Set<String> drives = client.listDrives();
+      assertNotNull(drives);
+   }
+
+   @Test
+   public void testListDriveInfo() throws Exception {
+      Set<? extends DriveInfo> drives = client.listDriveInfo();
+      assertNotNull(drives);
+   }
+
+   @Test
+   public void testGetDrive() throws Exception {
+      for (String driveUUID : client.listDrives()) {
+         assert !"".equals(driveUUID) : driveUUID;
+         assert client.getDriveInfo(driveUUID) != null : driveUUID;
+      }
+   }
+
+   protected String prefix = System.getProperty("user.name") + ".test";
+   protected DriveInfo drive;
+
+   @Test
+   public void testCreateDrive() throws Exception {
+      drive = client.createDrive(new CreateDriveRequest.Builder().name(prefix).size(driveSize).build());
+      checkCreatedDrive();
+
+      DriveInfo newInfo = client.getDriveInfo(drive.getUuid());
+      checkDriveMatchesGet(newInfo);
+
+   }
+
+   protected void checkDriveMatchesGet(DriveInfo newInfo) {
+      assertEquals(newInfo.getUuid(), drive.getUuid());
+   }
+
+   protected void checkCreatedDrive() {
+      assertNotNull(drive.getUuid());
+      assertNotNull(drive.getUser());
+      assertEquals(drive.getName(), prefix);
+      assertEquals(drive.getSize(), driveSize);
+      assertEquals(drive.getStatus(), DriveStatus.ACTIVE);
+      // for some reason, these occasionally return as 4096,1
+      // assertEquals(info.getReadBytes(), 0l);
+      // assertEquals(info.getWriteBytes(), 0l);
+      // assertEquals(info.getReadRequests(), 0l);
+      // assertEquals(info.getWriteRequests(), 0l);
+      assertEquals(drive.getEncryptionCipher(), "aes-xts-plain");
+   }
+
+   @Test(dependsOnMethods = "testCreateDrive")
+   public void testSetDriveData() throws Exception {
+
+      DriveInfo drive2 = client.setDriveData(drive.getUuid(), new DriveData.Builder().claimType(ClaimType.SHARED).name(
+               "rediculous").readers(ImmutableSet.of("ffffffff-ffff-ffff-ffff-ffffffffffff")).tags(
+               ImmutableSet.of("networking", "security", "gateway")).userMetadata(ImmutableMap.of("foo", "bar"))
+               .build());
+
+      assertNotNull(drive2.getUuid(), drive.getUuid());
+      assertEquals(drive2.getName(), "rediculous");
+      assertEquals(drive2.getClaimType(), ClaimType.SHARED);
+      assertEquals(drive2.getReaders(), ImmutableSet.of("ffffffff-ffff-ffff-ffff-ffffffffffff"));
+      assertEquals(drive2.getTags(), ImmutableSet.of("networking", "security", "gateway"));
+      assertEquals(drive2.getUserMetadata(), ImmutableMap.of("foo", "bar"));
+      drive = drive2;
+   }
+
+   protected ServerInfo server;
+
+   @Test(dependsOnMethods = "testSetDriveData")
+   public void testCreateAndStartServer() throws Exception {
+      Logger.getAnonymousLogger().info("preparing drive");
+      prepareDrive();
+
+      Server serverRequest = Servers.small(prefix, drive.getUuid(), vncPassword).build();
+
+      Logger.getAnonymousLogger().info("starting server");
+      server = client.createServer(serverRequest);
+      client.startServer(server.getUuid());
+      server = client.getServerInfo(server.getUuid());
+      checkStartedServer();
+
+      Server newInfo = client.getServerInfo(server.getUuid());
+      checkServerMatchesGet(newInfo);
+
+   }
+
+   protected void checkServerMatchesGet(Server newInfo) {
+      assertEquals(newInfo.getUuid(), server.getUuid());
+   }
+
+   protected void checkStartedServer() {
+      System.out.println(new Gson().toJson(server));
+      assertNotNull(server.getUuid());
+      assertNotNull(server.getUser());
+      assertEquals(server.getName(), prefix);
+      assertEquals(server.isPersistent(), true);
+      assertEquals(server.getDevices(), ImmutableMap.of("ide:0:0", new IDEDevice.Builder(0, 0).uuid(drive.getUuid())
+               .build()));
+      assertEquals(server.getBootDeviceIds(), ImmutableSet.of("ide:0:0"));
+      assertEquals(server.getNics().get(0).getDhcp(), server.getVnc().getIp());
+      assertEquals(server.getNics().get(0).getModel(), Model.E1000);
+      assertEquals(server.getStatus(), ServerStatus.ACTIVE);
+   }
+
+   @Test(dependsOnMethods = "testCreateAndStartServer")
+   public void testConnectivity() throws Exception {
+      HostAndPort vncsocket = HostAndPort.fromParts(server.getVnc().getIp(), 5900);
+      Logger.getAnonymousLogger().info("awaiting vnc: " + vncsocket);
+      assert socketTester.apply(vncsocket) : server;
+      HostAndPort sshsocket = HostAndPort.fromParts(server.getNics().get(0).getDhcp(), 22);
+      Logger.getAnonymousLogger().info("awaiting ssh: " + sshsocket);
+      assert socketTester.apply(sshsocket) : server;
+      doConnectViaSsh(server, getSshCredentials(server));
+   }
+
+   @Test(dependsOnMethods = "testConnectivity")
+   public void testLifeCycle() throws Exception {
+      client.stopServer(server.getUuid());
+      assertEquals(client.getServerInfo(server.getUuid()).getStatus(), ServerStatus.STOPPED);
+
+      client.startServer(server.getUuid());
+      assertEquals(client.getServerInfo(server.getUuid()).getStatus(), ServerStatus.ACTIVE);
+
+      client.resetServer(server.getUuid());
+      assertEquals(client.getServerInfo(server.getUuid()).getStatus(), ServerStatus.ACTIVE);
+
+      client.shutdownServer(server.getUuid());
+      // behavior on shutdown depends on how your server OS is set up to respond to an ACPI power
+      // button signal
+      assert (client.getServerInfo(server.getUuid()).getStatus() == ServerStatus.ACTIVE || client.getServerInfo(
+               server.getUuid()).getStatus() == ServerStatus.STOPPED);
+   }
+
+   @Test(dependsOnMethods = "testLifeCycle")
+   public void testSetServerConfiguration() throws Exception {
+      client.stopServer(server.getUuid());
+      assertEquals(client.getServerInfo(server.getUuid()).getStatus(), ServerStatus.STOPPED);
+
+      ServerInfo server2 = client.setServerConfiguration(server.getUuid(), Server.Builder.fromServer(server).name(
+               "rediculous").tags(ImmutableSet.of("networking", "security", "gateway")).userMetadata(
+               ImmutableMap.of("foo", "bar")).build());
+
+      assertNotNull(server2.getUuid(), server.getUuid());
+      assertEquals(server2.getName(), "rediculous");
+      checkTagsAndMetadata(server2);
+      server = server2;
+   }
+
+   protected void checkTagsAndMetadata(ServerInfo server2) {
+      assertEquals(server2.getTags(), ImmutableSet.of("networking", "security", "gateway"));
+      assertEquals(server2.getUserMetadata(), ImmutableMap.of("foo", "bar"));
+   }
+
+   @Test(dependsOnMethods = "testSetServerConfiguration")
+   public void testDestroyServer() throws Exception {
+      client.destroyServer(server.getUuid());
+      assertEquals(client.getServerInfo(server.getUuid()), null);
+   }
+
+   @Test(dependsOnMethods = "testDestroyServer")
+   public void testDestroyDrive() throws Exception {
+      client.destroyDrive(drive.getUuid());
+      assertEquals(client.getDriveInfo(drive.getUuid()), null);
+   }
+
+   protected void doConnectViaSsh(Server server, LoginCredentials creds) throws IOException {
+      SshClient ssh = Guice.createInjector(new SshjSshClientModule()).getInstance(SshClient.Factory.class).create(
+               HostAndPort.fromParts(server.getVnc().getIp(), 22), creds);
+      try {
+         ssh.connect();
+         ExecResponse hello = ssh.exec("echo hello");
+         assertEquals(hello.getOutput().trim(), "hello");
+         System.err.println(ssh.exec("df -k").getOutput());
+         System.err.println(ssh.exec("mount").getOutput());
+         System.err.println(ssh.exec("uname -a").getOutput());
+      } finally {
+         if (ssh != null)
+            ssh.disconnect();
+      }
+   }
+
+   @AfterGroups(groups = "live")
+   @Override
+   protected void tearDownContext() {
+      try {
+         client.destroyServer(server.getUuid());
+      } catch (Exception e) {
+         // no need to check null or anything as we swallow all
+      }
+      try {
+         client.destroyDrive(drive.getUuid());
+      } catch (Exception e) {
+
+      }
+      super.tearDownContext();
+   }
+
+   private DriveInfo drive2;
+   private DriveInfo drive3;
+
+   public void testWeCanReadAndWriteToDrive() throws IOException {
+      drive2 = client.createDrive(new CreateDriveRequest.Builder().name(prefix + "2").size(1 * 1024 * 1024l).build());
+      client.writeDrive(drive2.getUuid(), Payloads.newStringPayload("foo"));
+      assertEquals(Strings2.toString(client.readDrive(drive2.getUuid(), 0, 3)), "foo");
+   }
+
+   @Test(dependsOnMethods = "testWeCanReadAndWriteToDrive")
+   public void testWeCopyADriveContentsViaGzip() throws IOException {
+      try {
+         drive3 = client
+                  .createDrive(new CreateDriveRequest.Builder().name(prefix + "3").size(1 * 1024 * 1024l).build());
+         System.err.println("before image; drive 2" + client.getDriveInfo(drive2.getUuid()));
+         System.err.println("before image; drive 3" + client.getDriveInfo(drive3.getUuid()));
+         client.imageDrive(drive2.getUuid(), drive3.getUuid());
+         assert driveNotClaimed.apply(drive3) : client.getDriveInfo(drive3.getUuid());
+         assert driveNotClaimed.apply(drive2) : client.getDriveInfo(drive2.getUuid());
+         System.err.println("after image; drive 2" + client.getDriveInfo(drive2.getUuid()));
+         System.err.println("after image; drive 3" + client.getDriveInfo(drive3.getUuid()));
+         assertEquals(Strings2.toString(client.readDrive(drive3.getUuid(), 0, 3)), "foo");
+      } finally {
+         client.destroyDrive(drive2.getUuid());
+         client.destroyDrive(drive3.getUuid());
+      }
+   }
+
+   protected LoginCredentials getSshCredentials(Server server) {
+      return LoginCredentials.builder().user("toor").password(server.getVnc().getPassword()).build();
+   }
+
+   protected void prepareDrive() {
+      System.err.println("before prepare" + client.getDriveInfo(drive.getUuid()));
+      client.imageDrive(imageId, drive.getUuid(), ImageConversionType.GUNZIP);
+      assert driveNotClaimed.apply(drive) : client.getDriveInfo(drive.getUuid());
+      System.err.println("after prepare" + client.getDriveInfo(drive.getUuid()));
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/a2af31c4/apis/elasticstack/src/test/java/org/jclouds/elasticstack/ElasticStackApiTest.java
----------------------------------------------------------------------
diff --git a/apis/elasticstack/src/test/java/org/jclouds/elasticstack/ElasticStackApiTest.java b/apis/elasticstack/src/test/java/org/jclouds/elasticstack/ElasticStackApiTest.java
new file mode 100644
index 0000000..aa62b83
--- /dev/null
+++ b/apis/elasticstack/src/test/java/org/jclouds/elasticstack/ElasticStackApiTest.java
@@ -0,0 +1,451 @@
+/*
+ * 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 org.jclouds.elasticstack;
+
+import static org.jclouds.reflect.Reflection2.method;
+import static org.testng.Assert.assertEquals;
+
+import java.io.IOException;
+
+import javax.ws.rs.core.MediaType;
+
+import org.jclouds.Fallbacks.NullOnNotFoundOr404;
+import org.jclouds.Fallbacks.VoidOnNotFoundOr404;
+import org.jclouds.apis.ApiMetadata;
+import org.jclouds.elasticstack.binders.BindServerToPlainTextStringTest;
+import org.jclouds.elasticstack.domain.CreateDriveRequest;
+import org.jclouds.elasticstack.domain.Drive;
+import org.jclouds.elasticstack.domain.DriveData;
+import org.jclouds.elasticstack.domain.ImageConversionType;
+import org.jclouds.elasticstack.domain.Server;
+import org.jclouds.elasticstack.functions.KeyValuesDelimitedByBlankLinesToDriveInfo;
+import org.jclouds.elasticstack.functions.KeyValuesDelimitedByBlankLinesToServerInfo;
+import org.jclouds.elasticstack.functions.ListOfKeyValuesDelimitedByBlankLinesToDriveInfoSet;
+import org.jclouds.elasticstack.functions.ListOfKeyValuesDelimitedByBlankLinesToServerInfoSet;
+import org.jclouds.elasticstack.functions.ReturnPayload;
+import org.jclouds.elasticstack.functions.SplitNewlines;
+import org.jclouds.fallbacks.MapHttp4xxCodesToExceptions;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.http.filters.BasicAuthentication;
+import org.jclouds.http.functions.ReleasePayloadAndReturn;
+import org.jclouds.io.Payload;
+import org.jclouds.io.Payloads;
+import org.jclouds.rest.internal.BaseAsyncClientTest;
+import org.jclouds.rest.internal.GeneratedHttpRequest;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.common.reflect.Invokable;
+/**
+ * Tests behavior of {@code ElasticStackApi}
+ * 
+ * @author Adrian Cole
+ */
+// NOTE:without testName, this will not call @Before* and fail w/NPE during surefire
+@Test(groups = "unit", testName = "ElasticStackApiTest")
+public class ElasticStackApiTest extends BaseAsyncClientTest<ElasticStackApi> {
+   public void testListServers() throws SecurityException, NoSuchMethodException, IOException {
+      Invokable<?, ?> method = method(ElasticStackApi.class, "listServers");
+      GeneratedHttpRequest httpRequest = processor.createRequest(method, ImmutableList.of());
+
+      assertRequestLineEquals(httpRequest, "GET https://api-lon-p.elastichosts.com/servers/list HTTP/1.1");
+      assertNonPayloadHeadersEqual(httpRequest, "Accept: text/plain\n");
+      assertPayloadEquals(httpRequest, null, null, false);
+
+      // now make sure request filters apply by replaying
+      httpRequest = (GeneratedHttpRequest) Iterables.getOnlyElement(httpRequest.getFilters()).filter(httpRequest);
+      httpRequest = (GeneratedHttpRequest) Iterables.getOnlyElement(httpRequest.getFilters()).filter(httpRequest);
+
+      assertRequestLineEquals(httpRequest, "GET https://api-lon-p.elastichosts.com/servers/list HTTP/1.1");
+      // for example, using basic authentication, we should get "only one"
+      // header
+      assertNonPayloadHeadersEqual(httpRequest, "Accept: text/plain\nAuthorization: Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==\n");
+      assertPayloadEquals(httpRequest, null, null, false);
+
+      // TODO: insert expected response class, which probably extends ParseJson
+      assertResponseParserClassEquals(method, httpRequest, SplitNewlines.class);
+      assertSaxResponseParserClassEquals(method, null);
+      assertFallbackClassEquals(method, null);
+
+      checkFilters(httpRequest);
+
+   }
+
+   public void testListServerInfo() throws SecurityException, NoSuchMethodException, IOException {
+      Invokable<?, ?> method = method(ElasticStackApi.class, "listServerInfo");
+      GeneratedHttpRequest httpRequest = processor.createRequest(method, ImmutableList.of());
+
+      assertRequestLineEquals(httpRequest, "GET https://api-lon-p.elastichosts.com/servers/info HTTP/1.1");
+      assertNonPayloadHeadersEqual(httpRequest, "Accept: text/plain\n");
+      assertPayloadEquals(httpRequest, null, null, false);
+
+      assertResponseParserClassEquals(method, httpRequest, ListOfKeyValuesDelimitedByBlankLinesToServerInfoSet.class);
+      assertSaxResponseParserClassEquals(method, null);
+      assertFallbackClassEquals(method, null);
+
+      checkFilters(httpRequest);
+   }
+
+   public void testGetServerInfo() throws SecurityException, NoSuchMethodException, IOException {
+      Invokable<?, ?> method = method(ElasticStackApi.class, "getServerInfo", String.class);
+      GeneratedHttpRequest httpRequest = processor.createRequest(method, ImmutableList.<Object> of("uuid"));
+
+      assertRequestLineEquals(httpRequest, "GET https://api-lon-p.elastichosts.com/servers/uuid/info HTTP/1.1");
+      assertNonPayloadHeadersEqual(httpRequest, "Accept: text/plain\n");
+      assertPayloadEquals(httpRequest, null, null, false);
+
+      assertResponseParserClassEquals(method, httpRequest, KeyValuesDelimitedByBlankLinesToServerInfo.class);
+      assertSaxResponseParserClassEquals(method, null);
+      assertFallbackClassEquals(method, NullOnNotFoundOr404.class);
+
+      checkFilters(httpRequest);
+
+   }
+
+   public void testCreateAndStartServer() throws SecurityException, NoSuchMethodException, IOException {
+      Invokable<?, ?> method = method(ElasticStackApi.class, "createAndStartServer", Server.class);
+      GeneratedHttpRequest httpRequest = processor.createRequest(method, ImmutableList.<Object> of(
+            BindServerToPlainTextStringTest.SERVER));
+
+      assertRequestLineEquals(httpRequest, "POST https://api-lon-p.elastichosts.com/servers/create HTTP/1.1");
+      assertNonPayloadHeadersEqual(httpRequest, "Accept: text/plain\n");
+      assertPayloadEquals(httpRequest, BindServerToPlainTextStringTest.CREATED_SERVER, "text/plain", false);
+
+      assertResponseParserClassEquals(method, httpRequest, KeyValuesDelimitedByBlankLinesToServerInfo.class);
+      assertSaxResponseParserClassEquals(method, null);
+      assertFallbackClassEquals(method, NullOnNotFoundOr404.class);
+
+      checkFilters(httpRequest);
+
+   }
+
+   public void testCreateServer() throws SecurityException, NoSuchMethodException, IOException {
+      Invokable<?, ?> method = method(ElasticStackApi.class, "createServer", Server.class);
+      GeneratedHttpRequest httpRequest = processor.createRequest(method, ImmutableList.<Object> of(
+            BindServerToPlainTextStringTest.SERVER));
+
+      assertRequestLineEquals(httpRequest, "POST https://api-lon-p.elastichosts.com/servers/create/stopped HTTP/1.1");
+      assertNonPayloadHeadersEqual(httpRequest, "Accept: text/plain\n");
+      assertPayloadEquals(httpRequest, BindServerToPlainTextStringTest.CREATED_SERVER, "text/plain", false);
+
+      assertResponseParserClassEquals(method, httpRequest, KeyValuesDelimitedByBlankLinesToServerInfo.class);
+      assertSaxResponseParserClassEquals(method, null);
+      assertFallbackClassEquals(method, NullOnNotFoundOr404.class);
+
+      checkFilters(httpRequest);
+
+   }
+
+   public void testSetServerConfiguration() throws SecurityException, NoSuchMethodException, IOException {
+      Invokable<?, ?> method = method(ElasticStackApi.class, "setServerConfiguration", String.class, Server.class);
+      GeneratedHttpRequest httpRequest = processor.createRequest(method, ImmutableList.<Object> of("100",
+            BindServerToPlainTextStringTest.SERVER));
+
+      assertRequestLineEquals(httpRequest, "POST https://api-lon-p.elastichosts.com/servers/100/set HTTP/1.1");
+      assertNonPayloadHeadersEqual(httpRequest, "Accept: text/plain\n");
+      assertPayloadEquals(httpRequest, BindServerToPlainTextStringTest.CREATED_SERVER, "text/plain", false);
+
+      assertResponseParserClassEquals(method, httpRequest, KeyValuesDelimitedByBlankLinesToServerInfo.class);
+      assertSaxResponseParserClassEquals(method, null);
+      assertFallbackClassEquals(method, NullOnNotFoundOr404.class);
+
+      checkFilters(httpRequest);
+
+   }
+
+   public void testDestroyServer() throws SecurityException, NoSuchMethodException, IOException {
+      Invokable<?, ?> method = method(ElasticStackApi.class, "destroyServer", String.class);
+      GeneratedHttpRequest httpRequest = processor.createRequest(method, ImmutableList.<Object> of("uuid"));
+
+      assertRequestLineEquals(httpRequest, "POST https://api-lon-p.elastichosts.com/servers/uuid/destroy HTTP/1.1");
+      assertNonPayloadHeadersEqual(httpRequest, "Accept: text/plain\n");
+      assertPayloadEquals(httpRequest, null, null, false);
+
+      assertResponseParserClassEquals(method, httpRequest, ReleasePayloadAndReturn.class);
+      assertSaxResponseParserClassEquals(method, null);
+      assertFallbackClassEquals(method, VoidOnNotFoundOr404.class);
+
+      checkFilters(httpRequest);
+
+   }
+
+   public void testStartServer() throws SecurityException, NoSuchMethodException, IOException {
+      Invokable<?, ?> method = method(ElasticStackApi.class, "startServer", String.class);
+      GeneratedHttpRequest httpRequest = processor.createRequest(method, ImmutableList.<Object> of("uuid"));
+
+      assertRequestLineEquals(httpRequest, "POST https://api-lon-p.elastichosts.com/servers/uuid/start HTTP/1.1");
+      assertNonPayloadHeadersEqual(httpRequest, "Accept: text/plain\n");
+      assertPayloadEquals(httpRequest, null, null, false);
+
+      assertResponseParserClassEquals(method, httpRequest, ReleasePayloadAndReturn.class);
+      assertSaxResponseParserClassEquals(method, null);
+      assertFallbackClassEquals(method, MapHttp4xxCodesToExceptions.class);
+
+      checkFilters(httpRequest);
+
+   }
+
+   public void testStopServer() throws SecurityException, NoSuchMethodException, IOException {
+      Invokable<?, ?> method = method(ElasticStackApi.class, "stopServer", String.class);
+      GeneratedHttpRequest httpRequest = processor.createRequest(method, ImmutableList.<Object> of("uuid"));
+
+      assertRequestLineEquals(httpRequest, "POST https://api-lon-p.elastichosts.com/servers/uuid/stop HTTP/1.1");
+      assertNonPayloadHeadersEqual(httpRequest, "Accept: text/plain\n");
+      assertPayloadEquals(httpRequest, null, null, false);
+
+      assertResponseParserClassEquals(method, httpRequest, ReleasePayloadAndReturn.class);
+      assertSaxResponseParserClassEquals(method, null);
+      assertFallbackClassEquals(method, MapHttp4xxCodesToExceptions.class);
+
+      checkFilters(httpRequest);
+
+   }
+
+   public void testShutdownServer() throws SecurityException, NoSuchMethodException, IOException {
+      Invokable<?, ?> method = method(ElasticStackApi.class, "shutdownServer", String.class);
+      GeneratedHttpRequest httpRequest = processor.createRequest(method, ImmutableList.<Object> of("uuid"));
+
+      assertRequestLineEquals(httpRequest, "POST https://api-lon-p.elastichosts.com/servers/uuid/shutdown HTTP/1.1");
+      assertNonPayloadHeadersEqual(httpRequest, "Accept: text/plain\n");
+      assertPayloadEquals(httpRequest, null, null, false);
+
+      assertResponseParserClassEquals(method, httpRequest, ReleasePayloadAndReturn.class);
+      assertSaxResponseParserClassEquals(method, null);
+      assertFallbackClassEquals(method, MapHttp4xxCodesToExceptions.class);
+
+      checkFilters(httpRequest);
+
+   }
+
+   public void testResetServer() throws SecurityException, NoSuchMethodException, IOException {
+      Invokable<?, ?> method = method(ElasticStackApi.class, "resetServer", String.class);
+      GeneratedHttpRequest httpRequest = processor.createRequest(method, ImmutableList.<Object> of("uuid"));
+
+      assertRequestLineEquals(httpRequest, "POST https://api-lon-p.elastichosts.com/servers/uuid/reset HTTP/1.1");
+      assertNonPayloadHeadersEqual(httpRequest, "Accept: text/plain\n");
+      assertPayloadEquals(httpRequest, null, null, false);
+
+      assertResponseParserClassEquals(method, httpRequest, ReleasePayloadAndReturn.class);
+      assertSaxResponseParserClassEquals(method, null);
+      assertFallbackClassEquals(method, MapHttp4xxCodesToExceptions.class);
+
+      checkFilters(httpRequest);
+
+   }
+
+   public void testListDrives() throws SecurityException, NoSuchMethodException, IOException {
+      Invokable<?, ?> method = method(ElasticStackApi.class, "listDrives");
+      GeneratedHttpRequest httpRequest = processor.createRequest(method, ImmutableList.of());
+
+      assertRequestLineEquals(httpRequest, "GET https://api-lon-p.elastichosts.com/drives/list HTTP/1.1");
+      assertNonPayloadHeadersEqual(httpRequest, "Accept: text/plain\n");
+      assertPayloadEquals(httpRequest, null, null, false);
+
+      // now make sure request filters apply by replaying
+      httpRequest = (GeneratedHttpRequest) Iterables.getOnlyElement(httpRequest.getFilters()).filter(httpRequest);
+      httpRequest = (GeneratedHttpRequest) Iterables.getOnlyElement(httpRequest.getFilters()).filter(httpRequest);
+
+      assertRequestLineEquals(httpRequest, "GET https://api-lon-p.elastichosts.com/drives/list HTTP/1.1");
+      // for example, using basic authentication, we should get "only one"
+      // header
+      assertNonPayloadHeadersEqual(httpRequest, "Accept: text/plain\nAuthorization: Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==\n");
+      assertPayloadEquals(httpRequest, null, null, false);
+
+      // TODO: insert expected response class, which probably extends ParseJson
+      assertResponseParserClassEquals(method, httpRequest, SplitNewlines.class);
+      assertSaxResponseParserClassEquals(method, null);
+      assertFallbackClassEquals(method, null);
+
+      checkFilters(httpRequest);
+
+   }
+
+   public void testListDriveInfo() throws SecurityException, NoSuchMethodException, IOException {
+      Invokable<?, ?> method = method(ElasticStackApi.class, "listDriveInfo");
+      GeneratedHttpRequest httpRequest = processor.createRequest(method, ImmutableList.of());
+
+      assertRequestLineEquals(httpRequest, "GET https://api-lon-p.elastichosts.com/drives/info HTTP/1.1");
+      assertNonPayloadHeadersEqual(httpRequest, "Accept: text/plain\n");
+      assertPayloadEquals(httpRequest, null, null, false);
+
+      assertResponseParserClassEquals(method, httpRequest, ListOfKeyValuesDelimitedByBlankLinesToDriveInfoSet.class);
+      assertSaxResponseParserClassEquals(method, null);
+      assertFallbackClassEquals(method, null);
+
+      checkFilters(httpRequest);
+   }
+
+   public void testGetDriveInfo() throws SecurityException, NoSuchMethodException, IOException {
+      Invokable<?, ?> method = method(ElasticStackApi.class, "getDriveInfo", String.class);
+      GeneratedHttpRequest httpRequest = processor.createRequest(method, ImmutableList.<Object> of("uuid"));
+
+      assertRequestLineEquals(httpRequest, "GET https://api-lon-p.elastichosts.com/drives/uuid/info HTTP/1.1");
+      assertNonPayloadHeadersEqual(httpRequest, "Accept: text/plain\n");
+      assertPayloadEquals(httpRequest, null, null, false);
+
+      assertResponseParserClassEquals(method, httpRequest, KeyValuesDelimitedByBlankLinesToDriveInfo.class);
+      assertSaxResponseParserClassEquals(method, null);
+      assertFallbackClassEquals(method, NullOnNotFoundOr404.class);
+
+      checkFilters(httpRequest);
+
+   }
+
+   public void testCreateDrive() throws SecurityException, NoSuchMethodException, IOException {
+      Invokable<?, ?> method = method(ElasticStackApi.class, "createDrive", Drive.class);
+      GeneratedHttpRequest httpRequest = processor.createRequest(method, ImmutableList.<Object> of(
+            new CreateDriveRequest.Builder().name("foo").size(10000l).build()));
+
+      assertRequestLineEquals(httpRequest, "POST https://api-lon-p.elastichosts.com/drives/create HTTP/1.1");
+      assertNonPayloadHeadersEqual(httpRequest, "Accept: text/plain\n");
+      assertPayloadEquals(httpRequest, "name foo\nsize 10000", "text/plain", false);
+
+      assertResponseParserClassEquals(method, httpRequest, KeyValuesDelimitedByBlankLinesToDriveInfo.class);
+      assertSaxResponseParserClassEquals(method, null);
+      assertFallbackClassEquals(method, NullOnNotFoundOr404.class);
+
+      checkFilters(httpRequest);
+
+   }
+
+   public void testSetDriveData() throws SecurityException, NoSuchMethodException, IOException {
+      Invokable<?, ?> method = method(ElasticStackApi.class, "setDriveData", String.class, DriveData.class);
+      GeneratedHttpRequest httpRequest = processor.createRequest(method, ImmutableList.<Object> of("100",
+            new DriveData.Builder().name("foo").size(10000l).tags(ImmutableList.of("production", "candy")).build()));
+
+      assertRequestLineEquals(httpRequest, "POST https://api-lon-p.elastichosts.com/drives/100/set HTTP/1.1");
+      assertNonPayloadHeadersEqual(httpRequest, "Accept: text/plain\n");
+      assertPayloadEquals(httpRequest, "name foo\nsize 10000\ntags production candy", "text/plain", false);
+
+      assertResponseParserClassEquals(method, httpRequest, KeyValuesDelimitedByBlankLinesToDriveInfo.class);
+      assertSaxResponseParserClassEquals(method, null);
+      assertFallbackClassEquals(method, NullOnNotFoundOr404.class);
+
+      checkFilters(httpRequest);
+
+   }
+
+   public void testDestroyDrive() throws SecurityException, NoSuchMethodException, IOException {
+      Invokable<?, ?> method = method(ElasticStackApi.class, "destroyDrive", String.class);
+      GeneratedHttpRequest httpRequest = processor.createRequest(method, ImmutableList.<Object> of("uuid"));
+
+      assertRequestLineEquals(httpRequest, "POST https://api-lon-p.elastichosts.com/drives/uuid/destroy HTTP/1.1");
+      assertNonPayloadHeadersEqual(httpRequest, "Accept: text/plain\n");
+      assertPayloadEquals(httpRequest, null, null, false);
+
+      assertResponseParserClassEquals(method, httpRequest, ReleasePayloadAndReturn.class);
+      assertSaxResponseParserClassEquals(method, null);
+      assertFallbackClassEquals(method, VoidOnNotFoundOr404.class);
+
+      checkFilters(httpRequest);
+
+   }
+
+   public void testImageDrive() throws SecurityException, NoSuchMethodException, IOException {
+      Invokable<?, ?> method = method(ElasticStackApi.class, "imageDrive", String.class, String.class);
+      GeneratedHttpRequest httpRequest = processor.createRequest(method, ImmutableList.<Object> of("100", "200"));
+
+      assertRequestLineEquals(httpRequest, "POST https://api-lon-p.elastichosts.com/drives/200/image/100 HTTP/1.1");
+      assertNonPayloadHeadersEqual(httpRequest, "Accept: text/plain\n");
+      assertPayloadEquals(httpRequest, null, null, false);
+
+      assertResponseParserClassEquals(method, httpRequest, ReleasePayloadAndReturn.class);
+      assertSaxResponseParserClassEquals(method, null);
+      assertFallbackClassEquals(method, VoidOnNotFoundOr404.class);
+
+      checkFilters(httpRequest);
+
+   }
+
+   public void testImageDriveWithConversion() throws SecurityException, NoSuchMethodException, IOException {
+      Invokable<?, ?> method = method(ElasticStackApi.class, "imageDrive", String.class, String.class,
+            ImageConversionType.class);
+      GeneratedHttpRequest httpRequest = processor.createRequest(method, ImmutableList.<Object> of("100", "200",
+            ImageConversionType.GUNZIP));
+
+      assertRequestLineEquals(httpRequest, "POST https://api-lon-p.elastichosts.com/drives/200/image/100/gunzip HTTP/1.1");
+      assertNonPayloadHeadersEqual(httpRequest, "Accept: text/plain\n");
+      assertPayloadEquals(httpRequest, null, null, false);
+
+      assertResponseParserClassEquals(method, httpRequest, ReleasePayloadAndReturn.class);
+      assertSaxResponseParserClassEquals(method, null);
+      assertFallbackClassEquals(method, VoidOnNotFoundOr404.class);
+
+      checkFilters(httpRequest);
+
+   }
+
+   public void testReadDrive() throws SecurityException, NoSuchMethodException, IOException {
+      Invokable<?, ?> method = method(ElasticStackApi.class, "readDrive", String.class, long.class, long.class);
+      GeneratedHttpRequest httpRequest = processor.createRequest(method, ImmutableList.<Object> of("100", 1024, 2048));
+
+      assertRequestLineEquals(httpRequest, "POST https://api-lon-p.elastichosts.com/drives/100/read/1024/2048 HTTP/1.1");
+      assertNonPayloadHeadersEqual(httpRequest, "Accept: application/octet-stream\n");
+      assertPayloadEquals(httpRequest, null, null, false);
+
+      assertResponseParserClassEquals(method, httpRequest, ReturnPayload.class);
+      assertSaxResponseParserClassEquals(method, null);
+      assertFallbackClassEquals(method, NullOnNotFoundOr404.class);
+
+      checkFilters(httpRequest);
+   }
+
+   public void testWriteDrive() throws SecurityException, NoSuchMethodException, IOException {
+      Invokable<?, ?> method = method(ElasticStackApi.class, "writeDrive", String.class, Payload.class);
+      GeneratedHttpRequest httpRequest = processor.createRequest(method, ImmutableList.<Object> of("100",
+            Payloads.newStringPayload("foo")));
+
+      assertRequestLineEquals(httpRequest, "POST https://api-lon-p.elastichosts.com/drives/100/write HTTP/1.1");
+      assertNonPayloadHeadersEqual(httpRequest, "Accept: text/plain\n");
+      assertPayloadEquals(httpRequest, "foo", MediaType.APPLICATION_OCTET_STREAM, false);
+
+      assertResponseParserClassEquals(method, httpRequest, ReleasePayloadAndReturn.class);
+      assertSaxResponseParserClassEquals(method, null);
+      assertFallbackClassEquals(method, VoidOnNotFoundOr404.class);
+
+      checkFilters(httpRequest);
+   }
+
+   public void testWriteDriveOffset() throws SecurityException, NoSuchMethodException, IOException {
+      Invokable<?, ?> method = method(ElasticStackApi.class, "writeDrive", String.class, Payload.class, long.class);
+      GeneratedHttpRequest httpRequest = processor.createRequest(method, ImmutableList.<Object> of("100",
+            Payloads.newStringPayload("foo"), 2048));
+
+      assertRequestLineEquals(httpRequest, "POST https://api-lon-p.elastichosts.com/drives/100/write/2048 HTTP/1.1");
+      assertNonPayloadHeadersEqual(httpRequest, "Accept: text/plain\n");
+      assertPayloadEquals(httpRequest, "foo", MediaType.APPLICATION_OCTET_STREAM, false);
+
+      assertResponseParserClassEquals(method, httpRequest, ReleasePayloadAndReturn.class);
+      assertSaxResponseParserClassEquals(method, null);
+      assertFallbackClassEquals(method, VoidOnNotFoundOr404.class);
+
+      checkFilters(httpRequest);
+   }
+
+   @Override
+   protected void checkFilters(HttpRequest request) {
+      assertEquals(request.getFilters().size(), 1);
+      assertEquals(request.getFilters().get(0).getClass(), BasicAuthentication.class);
+   }
+
+   @Override
+   protected ApiMetadata createApiMetadata() {
+      return new ElasticStackApiMetadata();
+   }
+
+}