You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jclouds.apache.org by na...@apache.org on 2015/02/05 15:02:23 UTC

[3/4] jclouds-labs git commit: JCLOUDS-702: JClouds ProfitBricks provider - Storage API

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/67cae0ca/profitbricks/src/main/java/org/jclouds/profitbricks/features/StorageApi.java
----------------------------------------------------------------------
diff --git a/profitbricks/src/main/java/org/jclouds/profitbricks/features/StorageApi.java b/profitbricks/src/main/java/org/jclouds/profitbricks/features/StorageApi.java
new file mode 100644
index 0000000..f5d3577
--- /dev/null
+++ b/profitbricks/src/main/java/org/jclouds/profitbricks/features/StorageApi.java
@@ -0,0 +1,136 @@
+/*
+ * 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.profitbricks.features;
+
+import java.util.List;
+
+import javax.inject.Named;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.POST;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+import org.jclouds.Fallbacks;
+import org.jclouds.http.filters.BasicAuthentication;
+import org.jclouds.profitbricks.binder.storage.ConnectStorageToServerRequestBinder;
+import org.jclouds.profitbricks.binder.storage.CreateStorageRequestBinder;
+import org.jclouds.profitbricks.binder.storage.UpdateStorageRequestBinder;
+import org.jclouds.profitbricks.domain.Storage;
+import org.jclouds.profitbricks.http.filters.ProfitBricksSoapMessageEnvelope;
+import org.jclouds.profitbricks.http.parser.RequestIdOnlyResponseHandler;
+import org.jclouds.profitbricks.http.parser.storage.StorageIdOnlyResponseHandler;
+import org.jclouds.profitbricks.http.parser.storage.StorageInfoResponseHandler;
+import org.jclouds.profitbricks.http.parser.storage.StorageListResponseHandler;
+import org.jclouds.rest.annotations.Fallback;
+import org.jclouds.rest.annotations.MapBinder;
+import org.jclouds.rest.annotations.Payload;
+import org.jclouds.rest.annotations.PayloadParam;
+import org.jclouds.rest.annotations.RequestFilters;
+import org.jclouds.rest.annotations.XMLResponseParser;
+
+@RequestFilters({BasicAuthentication.class, ProfitBricksSoapMessageEnvelope.class})
+@Consumes(MediaType.TEXT_XML)
+@Produces(MediaType.TEXT_XML)
+public interface StorageApi {
+
+   /**
+    *
+    * @return Returns information about all virtual storage, such as configuration and provisioning state.
+    */
+   @POST
+   @Named("storage:getall")
+   @Payload("<ws:getAllStorages/>")
+   @XMLResponseParser(StorageListResponseHandler.class)
+   @Fallback(Fallbacks.EmptyListOnNotFoundOr404.class)
+   List<Storage> getAllStorages();
+
+   /**
+    *
+    * @param id Storage identifier
+    * @return Returns information about a virtual storage’s configuration and provisioning state.
+    */
+   @POST
+   @Named("storage:get")
+   @Payload("<ws:getStorage><storageId>{id}</storageId></ws:getStorage>")
+   @XMLResponseParser(StorageInfoResponseHandler.class)
+   @Fallback(Fallbacks.NullOnNotFoundOr404.class)
+   Storage getStorage(@PayloadParam("id") String id);
+
+   /**
+    * Creates a virtual storage within an existing virtual data center. Additional parameters can be specified, e.g. for assigning a HDD
+    * image to the storage.
+    *
+    * @param payload Payload
+    * @return storageId of the created storage
+    */
+   @POST
+   @Named("storage:create")
+   @MapBinder(CreateStorageRequestBinder.class)
+   @XMLResponseParser(StorageIdOnlyResponseHandler.class)
+   String createStorage(@PayloadParam("storage") Storage.Request.CreatePayload payload);
+
+   /**
+    * Updates parameters of an existing virtual storage device. It is possible to increase the storage size without reboot of an already
+    * provisioned storage. The additional capacity is not added to any partition. You have to partition the storage afterwards. Vice versa,
+    * it is not possible to decrease the storage size of an already provisioned storage.
+    *
+    * @param payload Payload
+    * @return Identifier of current request
+    */
+   @POST
+   @Named("storage:update")
+   @MapBinder(UpdateStorageRequestBinder.class)
+   @XMLResponseParser(RequestIdOnlyResponseHandler.class)
+   String updateStorage(@PayloadParam("storage") Storage.Request.UpdatePayload payload);
+
+   /**
+    * Deletes an existing virtual storage device.
+    *
+    * @param id Identifier of the target virtual storage
+    * @return Identifier of current request
+    */
+   @POST
+   @Named("storage:delete")
+   @Payload("<ws:deleteStorage><storageId>{id}</storageId></ws:deleteStorage>")
+   @Fallback(Fallbacks.FalseOnNotFoundOr404.class)
+   boolean deleteStorage(@PayloadParam("id") String id);
+
+   /**
+    * Connects a virtual storage device to an existing server.
+    *
+    * @param payload Payload
+    * @return Identifier of current request
+    */
+   @POST
+   @Named("storage:connect")
+   @MapBinder(ConnectStorageToServerRequestBinder.class)
+   @XMLResponseParser(RequestIdOnlyResponseHandler.class)
+   String connectStorageToServer(@PayloadParam("storage") Storage.Request.ConnectPayload payload);
+
+   /**
+    * Disconnects a virtual storage device from a connected server.
+    *
+    * @param storageId Identifier of the connected virtual storage
+    * @param serverId Identifier of the connected virtual server
+    * @return Identifier of current request
+    */
+   @POST
+   @Named("storage:disconnect")
+   @Payload("<ws:disconnectStorageFromServer><storageId>{storageId}</storageId><serverId>{serverId}</serverId></ws:disconnectStorageFromServer>")
+   @XMLResponseParser(RequestIdOnlyResponseHandler.class)
+   String disconnectStorageFromServer(@PayloadParam("storageId") String storageId, @PayloadParam("serverId") String serverId);
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/67cae0ca/profitbricks/src/main/java/org/jclouds/profitbricks/handlers/ProfitBricksHttpErrorHandler.java
----------------------------------------------------------------------
diff --git a/profitbricks/src/main/java/org/jclouds/profitbricks/handlers/ProfitBricksHttpErrorHandler.java b/profitbricks/src/main/java/org/jclouds/profitbricks/handlers/ProfitBricksHttpErrorHandler.java
index 6453327..a4ecb50 100644
--- a/profitbricks/src/main/java/org/jclouds/profitbricks/handlers/ProfitBricksHttpErrorHandler.java
+++ b/profitbricks/src/main/java/org/jclouds/profitbricks/handlers/ProfitBricksHttpErrorHandler.java
@@ -23,6 +23,7 @@ import javax.inject.Singleton;
 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 org.jclouds.rest.InsufficientResourcesException;
 import org.jclouds.rest.ResourceNotFoundException;
@@ -40,31 +41,35 @@ public class ProfitBricksHttpErrorHandler implements HttpErrorHandler {
    public void handleError(final HttpCommand command, final HttpResponse response) {
       Exception exception = null;
       try {
-	 switch (response.getStatusCode()) {
-	    case 400:
-	    case 405:
-	       exception = new IllegalArgumentException(response.getMessage(), exception);
-	       break;
-	    case 401:
-	       exception = new AuthorizationException(response.getMessage(), exception);
-	       break;
-	    case 402:
-	    case 409:
-	       exception = new IllegalStateException(response.getMessage(), exception);
-	       break;
-	    case 404:
-	    case 410:
-	       if (!command.getCurrentRequest().getMethod().equals("DELETE"))
-		  exception = new ResourceNotFoundException(response.getMessage(), exception);
-	       break;
-	    case 413:
-	    case 503:
-	       exception = new InsufficientResourcesException(response.getMessage(), exception);
-	       break;
-	 }
+         switch (response.getStatusCode()) {
+            case 400:
+            case 405:
+               exception = new IllegalArgumentException(response.getMessage(), exception);
+               break;
+            case 401:
+               exception = new AuthorizationException("This request requires authentication.", exception);
+               break;
+            case 402:
+            case 409:
+               exception = new IllegalStateException(response.getMessage(), exception);
+               break;
+            case 404:
+            case 410:
+               if (!command.getCurrentRequest().getMethod().equals("DELETE"))
+                  exception = new ResourceNotFoundException(response.getMessage(), exception);
+               break;
+            case 413:
+            case 503:
+               // if nothing (default message was OK) was parsed from command executor, assume it was an 503 (Maintenance) html response.
+               if (response.getMessage().equals("OK"))
+                  exception = new HttpResponseException("The ProfitBricks team is currently carrying out maintenance.", command, response);
+               else
+                  exception = new InsufficientResourcesException(response.getMessage(), exception);
+               break;
+         }
       } finally {
-	 closeQuietly(response.getPayload());
-	 command.setException(exception);
+         closeQuietly(response.getPayload());
+         command.setException(exception);
       }
    }
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/67cae0ca/profitbricks/src/main/java/org/jclouds/profitbricks/http/ResponseStatusFromPayloadHttpCommandExecutorService.java
----------------------------------------------------------------------
diff --git a/profitbricks/src/main/java/org/jclouds/profitbricks/http/ResponseStatusFromPayloadHttpCommandExecutorService.java b/profitbricks/src/main/java/org/jclouds/profitbricks/http/ResponseStatusFromPayloadHttpCommandExecutorService.java
index 78b0c8b..b818d3d 100644
--- a/profitbricks/src/main/java/org/jclouds/profitbricks/http/ResponseStatusFromPayloadHttpCommandExecutorService.java
+++ b/profitbricks/src/main/java/org/jclouds/profitbricks/http/ResponseStatusFromPayloadHttpCommandExecutorService.java
@@ -24,6 +24,7 @@ import java.io.InputStream;
 import java.net.HttpURLConnection;
 import java.net.Proxy;
 import java.net.URI;
+import java.util.regex.Pattern;
 
 import javax.inject.Named;
 import javax.inject.Singleton;
@@ -51,20 +52,22 @@ import com.google.inject.Inject;
 /**
  * Custom implementation of the HTTP driver to read actual http status and message from SOAP Fault.
  * <br/>
- * ProfitBricks API errors are always returned with 500 HTTP code. This class parses and reads the SOAP response to map the actual http code
- * and message
+ * ProfitBricks API errors are always returned with 500 HTTP code. This class parses and reads the SOAP response to map
+ * the actual http code and message
  */
 @Singleton
 public class ResponseStatusFromPayloadHttpCommandExecutorService extends JavaUrlHttpCommandExecutorService {
 
    private final ParseSax<ServiceFault> faultHandler;
 
+   private static final Pattern endSoapTag = Pattern.compile("</.+:Envelope>$");
+
    @Inject
    ResponseStatusFromPayloadHttpCommandExecutorService(HttpUtils utils, ContentMetadataCodec contentMetadataCodec,
-           DelegatingRetryHandler retryHandler, IOExceptionRetryHandler ioRetryHandler,
-           DelegatingErrorHandler errorHandler, HttpWire wire, @Named("untrusted") HostnameVerifier verifier,
-           @Named("untrusted") Supplier<SSLContext> untrustedSSLContextProvider, Function<URI, Proxy> proxyForURI,
-           ParseSax<ServiceFault> faultHandler) {
+	   DelegatingRetryHandler retryHandler, IOExceptionRetryHandler ioRetryHandler,
+	   DelegatingErrorHandler errorHandler, HttpWire wire, @Named("untrusted") HostnameVerifier verifier,
+	   @Named("untrusted") Supplier<SSLContext> untrustedSSLContextProvider, Function<URI, Proxy> proxyForURI,
+	   ParseSax<ServiceFault> faultHandler) {
       super(utils, contentMetadataCodec, retryHandler, ioRetryHandler, errorHandler, wire, verifier, untrustedSSLContextProvider, proxyForURI);
       this.faultHandler = faultHandler;
    }
@@ -74,38 +77,40 @@ public class ResponseStatusFromPayloadHttpCommandExecutorService extends JavaUrl
       HttpResponse originalResponse = super.invoke(connection);
       HttpResponse.Builder<?> responseBuilder = originalResponse.toBuilder();
 
-      if (hasPayload(originalResponse) && hasServerError(originalResponse)) {
-         // As we need to read the response body to determine if there are errors, but we may need to process the body
-         // again later in the response parsers if everything is OK, we buffer the body into an InputStream we can reset
-         InputStream in = null;
-         InputStream originalInputStream = originalResponse.getPayload().openStream();
-         if (originalInputStream instanceof ByteArrayInputStream)
-            in = originalInputStream;
-         else
-            try {
-               in = new ByteArrayInputStream(ByteStreams.toByteArray(originalInputStream));
-            } finally {
-               closeQuietly(originalInputStream);
-            }
-
-         try {
-            ServiceFault fault = faultHandler.parse(in);
-            if (fault != null)
-               responseBuilder
-                       .statusCode(fault.httpCode())
-                       .message(fault.message());
-         } catch (Exception ex) {
-            // ignore
-         } finally {
-            // Reset the input stream and set the payload, so it can be read again
-            // by the response and error parsers
-            if (in != null) {
-               in.reset();
-               Payload payload = Payloads.newInputStreamPayload(in);
-               contentMetadataCodec.fromHeaders(payload.getContentMetadata(), originalResponse.getHeaders());
-               responseBuilder.payload(payload);
-            }
-         }
+      if (hasServerError(originalResponse) && hasPayload(originalResponse)) {
+	 // As we need to read the response body to determine if there are errors, but we may need to process the body
+	 // again later in the response parsers if everything is OK, we buffer the body into an InputStream we can reset
+	 InputStream in = null;
+	 InputStream originalInputStream = originalResponse.getPayload().openStream();
+
+	 if (originalInputStream instanceof ByteArrayInputStream)
+	    in = originalInputStream;
+	 else
+	    try {
+	       in = new ByteArrayInputStream(ByteStreams.toByteArray(originalInputStream));
+	    } finally {
+	       closeQuietly(originalInputStream);
+	    }
+	 try {
+	    if (isSoapPayload(in)) {
+	       ServiceFault fault = faultHandler.parse(in);
+	       if (fault != null)
+		  responseBuilder
+			  .statusCode(fault.httpCode())
+			  .message(fault.message());
+	    }
+	 } catch (Exception ex) {
+	    // ignore
+	 } finally {
+	    // Reset the input stream and set the payload, so it can be read again
+	    // by the response and error parsers
+	    if (in != null) {
+	       in.reset();
+	       Payload payload = Payloads.newInputStreamPayload(in);
+	       contentMetadataCodec.fromHeaders(payload.getContentMetadata(), originalResponse.getHeaders());
+	       responseBuilder.payload(payload);
+	    }
+	 }
       }
 
       return responseBuilder.build();
@@ -119,4 +124,17 @@ public class ResponseStatusFromPayloadHttpCommandExecutorService extends JavaUrl
       return response.getPayload() != null && response.getPayload().getRawContent() != null;
    }
 
+   private static boolean isSoapPayload(final InputStream is) throws IOException {
+      int size = is.available();
+      char[] chars = new char[size];
+      byte[] bytes = new byte[size];
+
+      is.read(bytes, 0, size);
+      for (int i = 0; i < size;)
+	 chars[i] = (char) (bytes[i++] & 0xff);
+
+      is.reset(); // throws premature end of file w/o this
+
+      return endSoapTag.matcher(new String(chars)).find();
+   }
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/67cae0ca/profitbricks/src/main/java/org/jclouds/profitbricks/http/filters/ProfitBricksSoapMessageEnvelope.java
----------------------------------------------------------------------
diff --git a/profitbricks/src/main/java/org/jclouds/profitbricks/http/filters/ProfitBricksSoapMessageEnvelope.java b/profitbricks/src/main/java/org/jclouds/profitbricks/http/filters/ProfitBricksSoapMessageEnvelope.java
index fce8e21..7f2131d 100644
--- a/profitbricks/src/main/java/org/jclouds/profitbricks/http/filters/ProfitBricksSoapMessageEnvelope.java
+++ b/profitbricks/src/main/java/org/jclouds/profitbricks/http/filters/ProfitBricksSoapMessageEnvelope.java
@@ -32,9 +32,9 @@ import org.jclouds.io.Payloads;
 public class ProfitBricksSoapMessageEnvelope implements HttpRequestFilter {
 
    private final String SOAP_PREFIX
-	   = "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:ws=\"http://ws.api.profitbricks.com/\">"
-	   + "<soapenv:Header/>"
-	   + "<soapenv:Body>";
+           = "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:ws=\"http://ws.api.profitbricks.com/\">"
+           + "<soapenv:Header/>"
+           + "<soapenv:Body>";
 
    private final String SOAP_SUFFIX = "</soapenv:Body></soapenv:Envelope>";
 

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/67cae0ca/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/BaseProfitBricksResponseHandler.java
----------------------------------------------------------------------
diff --git a/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/BaseProfitBricksResponseHandler.java b/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/BaseProfitBricksResponseHandler.java
index c61f2b5..6a9933f 100644
--- a/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/BaseProfitBricksResponseHandler.java
+++ b/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/BaseProfitBricksResponseHandler.java
@@ -16,23 +16,14 @@
  */
 package org.jclouds.profitbricks.http.parser;
 
-import static com.google.common.base.Preconditions.checkNotNull;
-
-import java.util.Date;
-
-import org.jclouds.date.DateCodec;
-import org.jclouds.date.DateCodecFactory;
 import org.jclouds.http.functions.ParseSax;
 import org.xml.sax.SAXException;
 
 public abstract class BaseProfitBricksResponseHandler<T> extends ParseSax.HandlerForGeneratedRequestWithResult<T> {
 
-   protected final DateCodec dateCodec;
-
    private final StringBuilder strBuilder;
 
-   public BaseProfitBricksResponseHandler(DateCodecFactory dateCodec) {
-      this.dateCodec = checkNotNull(checkNotNull(dateCodec, "dateCodecFactory null").iso8601(), "iso8601 date codec null");
+   public BaseProfitBricksResponseHandler() {
       this.strBuilder = new StringBuilder();
    }
 
@@ -41,10 +32,6 @@ public abstract class BaseProfitBricksResponseHandler<T> extends ParseSax.Handle
       strBuilder.append(ch, start, length);
    }
 
-   protected final Date textToIso8601Date() {
-      return dateCodec.toDate(textToStringValue());
-   }
-
    protected String textToStringValue() {
       return strBuilder.toString().trim();
    }
@@ -52,9 +39,9 @@ public abstract class BaseProfitBricksResponseHandler<T> extends ParseSax.Handle
    protected Float textToFloatValue() {
       return Float.valueOf(textToStringValue());
    }
-   
-   protected Double textToDoubleValue(){
-      return Double.valueOf( textToStringValue());
+
+   protected Double textToDoubleValue() {
+      return Double.valueOf(textToStringValue());
    }
 
    protected int textToIntValue() {

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/67cae0ca/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/RequestIdOnlyResponseHandler.java
----------------------------------------------------------------------
diff --git a/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/RequestIdOnlyResponseHandler.java b/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/RequestIdOnlyResponseHandler.java
index 6e6b288..2914e50 100644
--- a/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/RequestIdOnlyResponseHandler.java
+++ b/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/RequestIdOnlyResponseHandler.java
@@ -16,28 +16,24 @@
  */
 package org.jclouds.profitbricks.http.parser;
 
-import com.google.inject.Inject;
-import org.jclouds.date.DateCodecFactory;
 import org.xml.sax.SAXException;
 
 public class RequestIdOnlyResponseHandler extends BaseProfitBricksResponseHandler<String> {
 
    private String requestId;
 
-   @Inject
-   RequestIdOnlyResponseHandler( DateCodecFactory dateCodec ) {
-      super( dateCodec );
+   RequestIdOnlyResponseHandler() {
    }
 
    @Override
-   public void endElement( String uri, String localName, String qName ) throws SAXException {
-      setPropertyOnEndTag( qName );
+   public void endElement(String uri, String localName, String qName) throws SAXException {
+      setPropertyOnEndTag(qName);
       clearTextBuffer();
    }
 
    @Override
-   protected void setPropertyOnEndTag( String qName ) {
-      if ( "requestId".equals( qName ) )
+   protected void setPropertyOnEndTag(String qName) {
+      if ("requestId".equals(qName))
          requestId = textToStringValue();
    }
 

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/67cae0ca/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/ServiceFaultResponseHandler.java
----------------------------------------------------------------------
diff --git a/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/ServiceFaultResponseHandler.java b/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/ServiceFaultResponseHandler.java
index c6a504b..f21c32e 100644
--- a/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/ServiceFaultResponseHandler.java
+++ b/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/ServiceFaultResponseHandler.java
@@ -16,42 +16,37 @@
  */
 package org.jclouds.profitbricks.http.parser;
 
-import org.jclouds.date.DateCodecFactory;
 import org.jclouds.profitbricks.domain.ServiceFault;
 import org.xml.sax.SAXException;
 
-import com.google.inject.Inject;
-
 public class ServiceFaultResponseHandler extends BaseProfitBricksResponseHandler<ServiceFault> {
 
    private final ServiceFault.Builder builder;
    private boolean done = false;
 
-   @Inject
-   ServiceFaultResponseHandler(DateCodecFactory dateCodec) {
-      super(dateCodec);
+   ServiceFaultResponseHandler() {
       this.builder = ServiceFault.builder();
    }
 
    @Override
    protected void setPropertyOnEndTag(String qName) {
       if ("faultCode".equals(qName))
-	 builder.faultCode(ServiceFault.FaultCode.fromValue(textToStringValue()));
+         builder.faultCode(ServiceFault.FaultCode.fromValue(textToStringValue()));
       else if ("httpCode".equals(qName))
-	 builder.httpCode(textToIntValue());
+         builder.httpCode(textToIntValue());
       else if ("message".equals(qName))
-	 builder.message(textToStringValue());
+         builder.message(textToStringValue());
       else if ("requestId".equals(qName))
-	 builder.requestId(textToIntValue());
+         builder.requestId(textToIntValue());
    }
 
    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException {
       if (done)
-	 return;
+         return;
       setPropertyOnEndTag(qName);
       if ("detail".equals(qName))
-	 done = true;
+         done = true;
       clearTextBuffer();
    }
 

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/67cae0ca/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/datacenter/BaseDataCenterResponseHandler.java
----------------------------------------------------------------------
diff --git a/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/datacenter/BaseDataCenterResponseHandler.java b/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/datacenter/BaseDataCenterResponseHandler.java
index 409131f..931d5e9 100644
--- a/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/datacenter/BaseDataCenterResponseHandler.java
+++ b/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/datacenter/BaseDataCenterResponseHandler.java
@@ -16,9 +16,6 @@
  */
 package org.jclouds.profitbricks.http.parser.datacenter;
 
-import javax.inject.Inject;
-
-import org.jclouds.date.DateCodecFactory;
 import org.jclouds.profitbricks.domain.DataCenter;
 import org.jclouds.profitbricks.http.parser.BaseProfitBricksResponseHandler;
 
@@ -26,18 +23,16 @@ public abstract class BaseDataCenterResponseHandler<T> extends BaseProfitBricksR
 
    protected DataCenter.Builder builder;
 
-   @Inject
-   BaseDataCenterResponseHandler(DateCodecFactory dateCodecFactory) {
-      super(dateCodecFactory);
+   BaseDataCenterResponseHandler() {
       this.builder = DataCenter.builder();
    }
 
    @Override
    protected void setPropertyOnEndTag(String qName) {
       if ("dataCenterId".equals(qName))
-	 builder.id(textToStringValue());
+         builder.id(textToStringValue());
       else if ("dataCenterVersion".equals(qName))
-	 builder.version(textToIntValue());
+         builder.version(textToIntValue());
    }
 
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/67cae0ca/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/datacenter/DataCenterInfoResponseHandler.java
----------------------------------------------------------------------
diff --git a/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/datacenter/DataCenterInfoResponseHandler.java b/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/datacenter/DataCenterInfoResponseHandler.java
index 5b3aadd..d44c880 100644
--- a/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/datacenter/DataCenterInfoResponseHandler.java
+++ b/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/datacenter/DataCenterInfoResponseHandler.java
@@ -16,42 +16,94 @@
  */
 package org.jclouds.profitbricks.http.parser.datacenter;
 
-import org.jclouds.date.DateCodecFactory;
+import java.util.List;
+
 import org.jclouds.profitbricks.domain.DataCenter;
 import org.jclouds.profitbricks.domain.Location;
 import org.jclouds.profitbricks.domain.ProvisioningState;
+import org.jclouds.profitbricks.domain.Server;
+import org.jclouds.profitbricks.domain.Storage;
+import org.jclouds.profitbricks.http.parser.server.ServerInfoResponseHandler;
+import org.jclouds.profitbricks.http.parser.storage.StorageInfoResponseHandler;
+import org.xml.sax.Attributes;
 import org.xml.sax.SAXException;
 
+import com.google.common.collect.Lists;
 import com.google.inject.Inject;
 
 public class DataCenterInfoResponseHandler extends BaseDataCenterResponseHandler<DataCenter> {
 
+   private final ServerInfoResponseHandler serverInfoResponseHandler;
+   private final StorageInfoResponseHandler storageInfoResponseHandler;
+
+   private final List<Server> servers = Lists.newArrayList();
+   private final List<Storage> storages = Lists.newArrayList();
+
    private boolean done = false;
+   private boolean useServerParser = false;
+   private boolean useStorageParser = false;
 
    @Inject
-   DataCenterInfoResponseHandler(DateCodecFactory dateCodecFactory) {
-      super(dateCodecFactory);
+   DataCenterInfoResponseHandler(ServerInfoResponseHandler serverInfoResponseHandler, StorageInfoResponseHandler storageInforResponseHandler) {
+      this.serverInfoResponseHandler = serverInfoResponseHandler;
+      this.storageInfoResponseHandler = storageInforResponseHandler;
+   }
+
+   @Override
+   public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
+      if ("servers".equals(qName))
+         useServerParser = true;
+      else if ("storages".equals(qName))
+         useStorageParser = true;
    }
 
    @Override
    protected void setPropertyOnEndTag(String qName) {
       super.setPropertyOnEndTag(qName);
       if ("dataCenterName".equals(qName))
-	 builder.name(textToStringValue());
+         builder.name(textToStringValue());
       else if ("location".equals(qName))
-	 builder.location(Location.fromId(textToStringValue()));
+         builder.location(Location.fromId(textToStringValue()));
       else if ("provisioningState".equals(qName))
-	 builder.state(ProvisioningState.fromValue(textToStringValue()));
+         builder.state(ProvisioningState.fromValue(textToStringValue()));
+   }
+
+   @Override
+   public void characters(char[] ch, int start, int length) {
+      if (useServerParser)
+         serverInfoResponseHandler.characters(ch, start, length);
+      else if (useStorageParser)
+         storageInfoResponseHandler.characters(ch, start, length);
+      else
+         super.characters(ch, start, length);
    }
 
    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException {
       if (done)
-	 return;
-      setPropertyOnEndTag(qName);
-      if ("return".equals(qName))
-	 done = true;
-      clearTextBuffer();
+         return;
+
+      if ("servers".equals(qName)) {
+         useServerParser = false;
+         servers.add(serverInfoResponseHandler.getResult());
+      } else if ("storages".equals(qName)) {
+         useStorageParser = false;
+         storages.add(storageInfoResponseHandler.getResult());
+      }
+
+      if (useServerParser)
+         serverInfoResponseHandler.endElement(uri, localName, qName);
+      else if (useStorageParser)
+         storageInfoResponseHandler.endElement(uri, localName, qName);
+      else {
+         setPropertyOnEndTag(qName);
+         if ("return".equals(qName)) {
+            done = true;
+            builder.servers(servers);
+            builder.storages(storages);
+         }
+         clearTextBuffer();
+      }
    }
 
    @Override

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/67cae0ca/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/datacenter/DataCenterListResponseHandler.java
----------------------------------------------------------------------
diff --git a/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/datacenter/DataCenterListResponseHandler.java b/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/datacenter/DataCenterListResponseHandler.java
index 8b8ad1a..b66264e 100644
--- a/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/datacenter/DataCenterListResponseHandler.java
+++ b/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/datacenter/DataCenterListResponseHandler.java
@@ -18,22 +18,18 @@ package org.jclouds.profitbricks.http.parser.datacenter;
 
 import java.util.List;
 
-import org.jclouds.date.DateCodecFactory;
 import org.jclouds.profitbricks.domain.DataCenter;
 import org.jclouds.profitbricks.domain.Location;
 import org.jclouds.profitbricks.domain.ProvisioningState;
 import org.xml.sax.SAXException;
 
 import com.google.common.collect.Lists;
-import com.google.inject.Inject;
 
 public class DataCenterListResponseHandler extends BaseDataCenterResponseHandler<List<DataCenter>> {
 
    private final List<DataCenter> dataCenters;
 
-   @Inject
-   DataCenterListResponseHandler(DateCodecFactory dateCodec) {
-      super(dateCodec);
+   DataCenterListResponseHandler() {
       this.dataCenters = Lists.newArrayList();
    }
 
@@ -46,19 +42,19 @@ public class DataCenterListResponseHandler extends BaseDataCenterResponseHandler
    protected void setPropertyOnEndTag(String qName) {
       super.setPropertyOnEndTag(qName);
       if ("dataCenterName".equals(qName))
-	 builder.name(textToStringValue());
+         builder.name(textToStringValue());
       else if ("location".equals(qName))
-	 builder.location(Location.fromValue(textToStringValue()));
+         builder.location(Location.fromValue(textToStringValue()));
       else if ("provisioningState".equals(qName))
-	 builder.state(ProvisioningState.fromValue(textToStringValue()));
+         builder.state(ProvisioningState.fromValue(textToStringValue()));
    }
 
    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException {
       setPropertyOnEndTag(qName);
       if ("return".equals(qName)) {
-	 dataCenters.add(builder.build());
-	 builder = DataCenter.builder();
+         dataCenters.add(builder.build());
+         builder = DataCenter.builder();
       }
       clearTextBuffer();
    }

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/67cae0ca/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/image/BaseImageResponseHandler.java
----------------------------------------------------------------------
diff --git a/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/image/BaseImageResponseHandler.java b/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/image/BaseImageResponseHandler.java
index 1a6ff94..1e8828c 100644
--- a/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/image/BaseImageResponseHandler.java
+++ b/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/image/BaseImageResponseHandler.java
@@ -16,9 +16,6 @@
  */
 package org.jclouds.profitbricks.http.parser.image;
 
-import javax.inject.Inject;
-
-import org.jclouds.date.DateCodecFactory;
 import org.jclouds.profitbricks.domain.Image;
 import org.jclouds.profitbricks.domain.Image.Type;
 import org.jclouds.profitbricks.domain.Location;
@@ -29,9 +26,7 @@ public abstract class BaseImageResponseHandler<T> extends BaseProfitBricksRespon
 
    protected Image.Builder builder;
 
-   @Inject
-   BaseImageResponseHandler(DateCodecFactory dateCodecFactory) {
-      super(dateCodecFactory);
+   BaseImageResponseHandler() {
       this.builder = Image.builder();
    }
 

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/67cae0ca/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/image/ImageInfoResponseHandler.java
----------------------------------------------------------------------
diff --git a/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/image/ImageInfoResponseHandler.java b/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/image/ImageInfoResponseHandler.java
index 0736acb..62d3973 100644
--- a/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/image/ImageInfoResponseHandler.java
+++ b/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/image/ImageInfoResponseHandler.java
@@ -16,8 +16,6 @@
  */
 package org.jclouds.profitbricks.http.parser.image;
 
-import com.google.inject.Inject;
-import org.jclouds.date.DateCodecFactory;
 import org.jclouds.profitbricks.domain.Image;
 import org.xml.sax.SAXException;
 
@@ -25,9 +23,7 @@ public class ImageInfoResponseHandler extends BaseImageResponseHandler<Image> {
 
    private boolean done = false;
 
-   @Inject
-   ImageInfoResponseHandler(DateCodecFactory dateCodecFactory) {
-      super(dateCodecFactory);
+   ImageInfoResponseHandler() {
    }
 
    @Override

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/67cae0ca/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/image/ImageListResponseHandler.java
----------------------------------------------------------------------
diff --git a/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/image/ImageListResponseHandler.java b/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/image/ImageListResponseHandler.java
index 8fc8091..ee73908 100644
--- a/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/image/ImageListResponseHandler.java
+++ b/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/image/ImageListResponseHandler.java
@@ -17,9 +17,9 @@
 package org.jclouds.profitbricks.http.parser.image;
 
 import com.google.common.collect.Lists;
-import com.google.inject.Inject;
+
 import java.util.List;
-import org.jclouds.date.DateCodecFactory;
+
 import org.jclouds.profitbricks.domain.Image;
 import org.xml.sax.SAXException;
 
@@ -27,9 +27,7 @@ public class ImageListResponseHandler extends BaseImageResponseHandler<List<Imag
 
    private final List<Image> images;
 
-   @Inject
-   ImageListResponseHandler(DateCodecFactory dateCodecFactory) {
-      super(dateCodecFactory);
+   ImageListResponseHandler() {
       this.images = Lists.newArrayList();
    }
 

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/67cae0ca/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/server/BaseServerResponseHandler.java
----------------------------------------------------------------------
diff --git a/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/server/BaseServerResponseHandler.java b/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/server/BaseServerResponseHandler.java
index e594ce4..356d218 100644
--- a/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/server/BaseServerResponseHandler.java
+++ b/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/server/BaseServerResponseHandler.java
@@ -16,7 +16,12 @@
  */
 package org.jclouds.profitbricks.http.parser.server;
 
+import java.util.Date;
+
+import org.jclouds.date.DateCodec;
+
 import com.google.inject.Inject;
+
 import org.jclouds.date.DateCodecFactory;
 import org.jclouds.profitbricks.domain.AvailabilityZone;
 import org.jclouds.profitbricks.domain.OsType;
@@ -28,48 +33,54 @@ public abstract class BaseServerResponseHandler<T> extends BaseProfitBricksRespo
 
    protected Server.DescribingBuilder builder;
 
+   protected final DateCodec dateCodec;
+
    @Inject
-   BaseServerResponseHandler( DateCodecFactory dateCodec ) {
-      super( dateCodec );
+   BaseServerResponseHandler(DateCodecFactory dateCodec) {
+      this.dateCodec = dateCodec.iso8601();
       this.builder = Server.builder();
    }
 
+   protected final Date textToIso8601Date() {
+      return dateCodec.toDate(textToStringValue());
+   }
+
    @Override
-   protected void setPropertyOnEndTag( String qName ) {
-      if ( "serverId".equals( qName ) )
-         builder.id( textToStringValue() );
-      else if ( "serverName".equals( qName ) )
-         builder.name( textToStringValue() );
-      else if ( "cores".equals( qName ) )
-         builder.cores( textToIntValue() );
-      else if ( "ram".equals( qName ) )
-         builder.ram( textToIntValue() );
-      else if ( "provisioningState".equals( qName ) )
-         builder.state( ProvisioningState.fromValue( textToStringValue() ) );
-      else if ( "virtualMachineState".equals( qName ) )
-         builder.status( Server.Status.fromValue( textToStringValue() ) );
-      else if ( "osType".equals( qName ) )
-         builder.osType( OsType.fromValue( textToStringValue() ) );
-      else if ( "availabilityZone".equals( qName ) )
-         builder.availabilityZone( AvailabilityZone.fromValue( textToStringValue() ) );
-      else if ( "creationTime".equals( qName ) )
-         builder.creationTime( textToIso8601Date() );
-      else if ( "lastModificationTime".equals( qName ) )
-         builder.lastModificationTime( textToIso8601Date() );
-      else if ( "internetAccess".equals( qName ) )
-         builder.hasInternetAccess( textToBooleanValue() );
-      else if ( "cpuHotPlug".equals( qName ) )
-         builder.isCpuHotPlug( textToBooleanValue() );
-      else if ( "ramHotPlug".equals( qName ) )
-         builder.isRamHotPlug( textToBooleanValue() );
-      else if ( "nicHotPlug".equals( qName ) )
-         builder.isNicHotPlug( textToBooleanValue() );
-      else if ( "nicHotUnPlug".equals( qName ) )
-         builder.isNicHotUnPlug( textToBooleanValue() );
-      else if ( "discVirtioHotPlug".equals( qName ) )
-         builder.isDiscVirtioHotPlug( textToBooleanValue() );
-      else if ( "discVirtioHotUnPlug".equals( qName ) )
-         builder.isDiscVirtioHotUnPlug( textToBooleanValue() );
+   protected void setPropertyOnEndTag(String qName) {
+      if ("serverId".equals(qName))
+         builder.id(textToStringValue());
+      else if ("serverName".equals(qName))
+         builder.name(textToStringValue());
+      else if ("cores".equals(qName))
+         builder.cores(textToIntValue());
+      else if ("ram".equals(qName))
+         builder.ram(textToIntValue());
+      else if ("provisioningState".equals(qName))
+         builder.state(ProvisioningState.fromValue(textToStringValue()));
+      else if ("virtualMachineState".equals(qName))
+         builder.status(Server.Status.fromValue(textToStringValue()));
+      else if ("osType".equals(qName))
+         builder.osType(OsType.fromValue(textToStringValue()));
+      else if ("availabilityZone".equals(qName))
+         builder.availabilityZone(AvailabilityZone.fromValue(textToStringValue()));
+      else if ("creationTime".equals(qName))
+         builder.creationTime(textToIso8601Date());
+      else if ("lastModificationTime".equals(qName))
+         builder.lastModificationTime(textToIso8601Date());
+      else if ("internetAccess".equals(qName))
+         builder.hasInternetAccess(textToBooleanValue());
+      else if ("cpuHotPlug".equals(qName))
+         builder.isCpuHotPlug(textToBooleanValue());
+      else if ("ramHotPlug".equals(qName))
+         builder.isRamHotPlug(textToBooleanValue());
+      else if ("nicHotPlug".equals(qName))
+         builder.isNicHotPlug(textToBooleanValue());
+      else if ("nicHotUnPlug".equals(qName))
+         builder.isNicHotUnPlug(textToBooleanValue());
+      else if ("discVirtioHotPlug".equals(qName))
+         builder.isDiscVirtioHotPlug(textToBooleanValue());
+      else if ("discVirtioHotUnPlug".equals(qName))
+         builder.isDiscVirtioHotUnPlug(textToBooleanValue());
    }
 
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/67cae0ca/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/server/ServerIdOnlyResponseHandler.java
----------------------------------------------------------------------
diff --git a/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/server/ServerIdOnlyResponseHandler.java b/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/server/ServerIdOnlyResponseHandler.java
index ed88018..1c74354 100644
--- a/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/server/ServerIdOnlyResponseHandler.java
+++ b/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/server/ServerIdOnlyResponseHandler.java
@@ -16,8 +16,6 @@
  */
 package org.jclouds.profitbricks.http.parser.server;
 
-import com.google.inject.Inject;
-import org.jclouds.date.DateCodecFactory;
 import org.jclouds.profitbricks.http.parser.BaseProfitBricksResponseHandler;
 import org.xml.sax.SAXException;
 
@@ -30,20 +28,18 @@ public class ServerIdOnlyResponseHandler extends BaseProfitBricksResponseHandler
 
    private String serverId;
 
-   @Inject
-   ServerIdOnlyResponseHandler( DateCodecFactory dateCodec ) {
-      super( dateCodec );
+   ServerIdOnlyResponseHandler() {
    }
 
    @Override
-   public void endElement( String uri, String localName, String qName ) throws SAXException {
-      setPropertyOnEndTag( qName );
+   public void endElement(String uri, String localName, String qName) throws SAXException {
+      setPropertyOnEndTag(qName);
       clearTextBuffer();
    }
 
    @Override
-   protected void setPropertyOnEndTag( String qName ) {
-      if ( "serverId".equals( qName ) )
+   protected void setPropertyOnEndTag(String qName) {
+      if ("serverId".equals(qName))
          serverId = textToStringValue();
    }
 

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/67cae0ca/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/server/ServerInfoResponseHandler.java
----------------------------------------------------------------------
diff --git a/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/server/ServerInfoResponseHandler.java b/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/server/ServerInfoResponseHandler.java
index dfac4ff..3dc6e6e 100644
--- a/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/server/ServerInfoResponseHandler.java
+++ b/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/server/ServerInfoResponseHandler.java
@@ -26,16 +26,16 @@ public class ServerInfoResponseHandler extends BaseServerResponseHandler<Server>
    private boolean done = false;
 
    @Inject
-   ServerInfoResponseHandler( DateCodecFactory dateCodec ) {
-      super( dateCodec );
+   ServerInfoResponseHandler(DateCodecFactory dateCodec) {
+      super(dateCodec);
    }
 
    @Override
-   public void endElement( String uri, String localName, String qName ) throws SAXException {
-      if ( done )
+   public void endElement(String uri, String localName, String qName) throws SAXException {
+      if (done)
          return;
-      setPropertyOnEndTag( qName );
-      if ( "return".equals( qName ) )
+      setPropertyOnEndTag(qName);
+      if ("return".equals(qName))
          done = true;
       clearTextBuffer();
    }

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/67cae0ca/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/server/ServerListResponseHandler.java
----------------------------------------------------------------------
diff --git a/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/server/ServerListResponseHandler.java b/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/server/ServerListResponseHandler.java
index f50027c..f0fcd3f 100644
--- a/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/server/ServerListResponseHandler.java
+++ b/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/server/ServerListResponseHandler.java
@@ -28,16 +28,16 @@ public class ServerListResponseHandler extends BaseServerResponseHandler<List<Se
    private final List<Server> servers;
 
    @Inject
-   ServerListResponseHandler( DateCodecFactory dateCodec ) {
-      super( dateCodec );
+   ServerListResponseHandler(DateCodecFactory dateCodec) {
+      super(dateCodec);
       this.servers = Lists.newArrayList();
    }
 
    @Override
-   public void endElement( String uri, String localName, String qName ) throws SAXException {
-      setPropertyOnEndTag( qName );
-      if ( "return".equals( qName ) ) {
-         servers.add( builder.build() );
+   public void endElement(String uri, String localName, String qName) throws SAXException {
+      setPropertyOnEndTag(qName);
+      if ("return".equals(qName)) {
+         servers.add(builder.build());
          builder = Server.builder();
       }
       clearTextBuffer();

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/67cae0ca/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/state/GetProvisioningStateResponseHandler.java
----------------------------------------------------------------------
diff --git a/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/state/GetProvisioningStateResponseHandler.java b/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/state/GetProvisioningStateResponseHandler.java
index 705a918..d618cc1 100644
--- a/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/state/GetProvisioningStateResponseHandler.java
+++ b/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/state/GetProvisioningStateResponseHandler.java
@@ -16,20 +16,15 @@
  */
 package org.jclouds.profitbricks.http.parser.state;
 
-import org.jclouds.date.DateCodecFactory;
 import org.jclouds.profitbricks.domain.ProvisioningState;
 import org.jclouds.profitbricks.http.parser.BaseProfitBricksResponseHandler;
 import org.xml.sax.SAXException;
 
-import com.google.inject.Inject;
-
 public class GetProvisioningStateResponseHandler extends BaseProfitBricksResponseHandler<ProvisioningState> {
 
    private ProvisioningState state = ProvisioningState.UNRECOGNIZED;
 
-   @Inject
-   GetProvisioningStateResponseHandler(DateCodecFactory dateCodec) {
-      super(dateCodec);
+   GetProvisioningStateResponseHandler() {
    }
 
    @Override
@@ -41,7 +36,7 @@ public class GetProvisioningStateResponseHandler extends BaseProfitBricksRespons
    @Override
    protected void setPropertyOnEndTag(String qName) {
       if ("return".equals(qName))
-	 state = ProvisioningState.fromValue(textToStringValue());
+         state = ProvisioningState.fromValue(textToStringValue());
    }
 
    @Override

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/67cae0ca/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/storage/BaseStorageResponseHandler.java
----------------------------------------------------------------------
diff --git a/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/storage/BaseStorageResponseHandler.java b/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/storage/BaseStorageResponseHandler.java
new file mode 100644
index 0000000..cb61e79
--- /dev/null
+++ b/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/storage/BaseStorageResponseHandler.java
@@ -0,0 +1,83 @@
+/*
+ * 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.profitbricks.http.parser.storage;
+
+import java.util.Date;
+import java.util.List;
+
+import org.jclouds.date.DateCodec;
+
+import com.google.inject.Inject;
+
+import org.jclouds.date.DateCodecFactory;
+import org.jclouds.profitbricks.domain.ProvisioningState;
+import org.jclouds.profitbricks.domain.Storage;
+import org.jclouds.profitbricks.domain.Storage.BusType;
+import org.jclouds.profitbricks.http.parser.BaseProfitBricksResponseHandler;
+
+import com.google.common.collect.Lists;
+
+public abstract class BaseStorageResponseHandler<T> extends BaseProfitBricksResponseHandler<T> {
+
+   protected final DateCodec dateCodec;
+
+   protected Storage.Builder builder;
+   protected List<String> serverIds;
+
+   @Inject
+   BaseStorageResponseHandler(DateCodecFactory dateCodec) {
+      this.dateCodec = dateCodec.iso8601();
+      this.builder = Storage.builder();
+      this.serverIds = Lists.newArrayList();
+   }
+
+   protected final Date textToIso8601Date() {
+      return dateCodec.toDate(textToStringValue());
+   }
+
+   @Override
+   protected void setPropertyOnEndTag(String qName) {
+//            <requestId>?</requestId>
+//            <dataCenterId>?</dataCenterId>
+//            <dataCenterVersion>?</dataCenterVersion>
+      if ("storageId".equals(qName))
+         builder.id(textToStringValue());
+      else if ("size".equals(qName))
+         builder.size(textToFloatValue());
+      else if ("storageName".equals(qName))
+         builder.name(textToStringValue());
+      else if ("provisioningState".equals(qName))
+         builder.state(ProvisioningState.fromValue(textToStringValue()));
+      else if ("creationTime".equals(qName))
+         builder.creationTime(textToIso8601Date());
+      else if ("lastModificationTime".equals(qName))
+         builder.lastModificationTime(textToIso8601Date());
+//            <mountImage>
+//               <imageId>?</imageId>
+//               <imageName>?</imageName>
+//            </mountImage>
+      else if ("serverIds".equals(qName))
+         serverIds.add(textToStringValue());
+      else if ("bootDevice".equals(qName))
+         builder.bootDevice(textToBooleanValue());
+      else if ("busType".equals(qName))
+         builder.busType(BusType.fromValue(textToStringValue()));
+      else if ("deviceNumber".equals(qName))
+         builder.deviceNumber(textToIntValue());
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/67cae0ca/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/storage/StorageIdOnlyResponseHandler.java
----------------------------------------------------------------------
diff --git a/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/storage/StorageIdOnlyResponseHandler.java b/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/storage/StorageIdOnlyResponseHandler.java
new file mode 100644
index 0000000..ac3a039
--- /dev/null
+++ b/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/storage/StorageIdOnlyResponseHandler.java
@@ -0,0 +1,46 @@
+/*
+ * 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.profitbricks.http.parser.storage;
+
+import org.jclouds.profitbricks.http.parser.BaseProfitBricksResponseHandler;
+import org.xml.sax.SAXException;
+
+public class StorageIdOnlyResponseHandler extends BaseProfitBricksResponseHandler<String> {
+
+   private String storageId;
+
+   StorageIdOnlyResponseHandler() {
+   }
+
+   @Override
+   public void endElement(String uri, String localName, String qName) throws SAXException {
+      setPropertyOnEndTag(qName);
+      clearTextBuffer();
+   }
+
+   @Override
+   protected void setPropertyOnEndTag(String qName) {
+      if ("storageId".equals(qName))
+         storageId = textToStringValue();
+   }
+
+   @Override
+   public String getResult() {
+      return storageId;
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/67cae0ca/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/storage/StorageInfoResponseHandler.java
----------------------------------------------------------------------
diff --git a/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/storage/StorageInfoResponseHandler.java b/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/storage/StorageInfoResponseHandler.java
new file mode 100644
index 0000000..2bc1ed6
--- /dev/null
+++ b/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/storage/StorageInfoResponseHandler.java
@@ -0,0 +1,50 @@
+/*
+ * 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.profitbricks.http.parser.storage;
+
+import com.google.inject.Inject;
+import org.jclouds.date.DateCodecFactory;
+import org.jclouds.profitbricks.domain.Storage;
+import org.xml.sax.SAXException;
+
+public class StorageInfoResponseHandler extends BaseStorageResponseHandler<Storage> {
+
+   private boolean done = false;
+
+   @Inject
+   StorageInfoResponseHandler(DateCodecFactory dateCodec) {
+      super(dateCodec);
+   }
+
+   @Override
+   public void endElement(String uri, String localName, String qName) throws SAXException {
+      if (done)
+         return;
+      setPropertyOnEndTag(qName);
+      if ("return".equals(qName)) {
+         done = true;
+         builder.serverIds(serverIds);
+      }
+      clearTextBuffer();
+   }
+
+   @Override
+   public Storage getResult() {
+      return builder.build();
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/67cae0ca/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/storage/StorageListResponseHandler.java
----------------------------------------------------------------------
diff --git a/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/storage/StorageListResponseHandler.java b/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/storage/StorageListResponseHandler.java
new file mode 100644
index 0000000..fe5e3fa
--- /dev/null
+++ b/profitbricks/src/main/java/org/jclouds/profitbricks/http/parser/storage/StorageListResponseHandler.java
@@ -0,0 +1,54 @@
+/*
+ * 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.profitbricks.http.parser.storage;
+
+import autovalue.shaded.com.google.common.common.collect.Lists;
+import com.google.inject.Inject;
+import java.util.List;
+import org.jclouds.date.DateCodecFactory;
+import org.jclouds.profitbricks.domain.Storage;
+import org.xml.sax.SAXException;
+
+public class StorageListResponseHandler extends BaseStorageResponseHandler<List<Storage>> {
+
+   private final List<Storage> storages;
+
+   @Inject
+   StorageListResponseHandler(DateCodecFactory dateCodec) {
+      super(dateCodec);
+      this.storages = Lists.newArrayList();
+   }
+
+   @Override
+   public void endElement(String uri, String localName, String qName) throws SAXException {
+      setPropertyOnEndTag(qName);
+      if ("return".equals(qName)) {
+         storages.add(builder
+                 .serverIds(serverIds)
+                 .build());
+         builder = Storage.builder();
+         serverIds = Lists.newArrayList();
+      }
+      clearTextBuffer();
+   }
+
+   @Override
+   public List<Storage> getResult() {
+      return storages;
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/67cae0ca/profitbricks/src/test/java/org/jclouds/profitbricks/BaseProfitBricksLiveTest.java
----------------------------------------------------------------------
diff --git a/profitbricks/src/test/java/org/jclouds/profitbricks/BaseProfitBricksLiveTest.java b/profitbricks/src/test/java/org/jclouds/profitbricks/BaseProfitBricksLiveTest.java
index 3ebc9c0..e310b6e 100644
--- a/profitbricks/src/test/java/org/jclouds/profitbricks/BaseProfitBricksLiveTest.java
+++ b/profitbricks/src/test/java/org/jclouds/profitbricks/BaseProfitBricksLiveTest.java
@@ -38,8 +38,8 @@ public abstract class BaseProfitBricksLiveTest extends BaseApiLiveTest<ProfitBri
    protected void initialize() {
       super.initialize();
       this.dcWaitingPredicate = Predicates2.retry(
-	      new ProvisioningStatusPollingPredicate(api, ProvisioningStatusAware.DATACENTER, ProvisioningState.AVAILABLE),
-	      2l * 60l, 2l, TimeUnit.SECONDS);
+              new ProvisioningStatusPollingPredicate(api, ProvisioningStatusAware.DATACENTER, ProvisioningState.AVAILABLE),
+              2l * 60l, 2l, TimeUnit.SECONDS);
    }
 
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/67cae0ca/profitbricks/src/test/java/org/jclouds/profitbricks/binder/datacenter/CreateDataCenterRequestBinderTest.java
----------------------------------------------------------------------
diff --git a/profitbricks/src/test/java/org/jclouds/profitbricks/binder/datacenter/CreateDataCenterRequestBinderTest.java b/profitbricks/src/test/java/org/jclouds/profitbricks/binder/datacenter/CreateDataCenterRequestBinderTest.java
index b3a891b..6c4815f 100644
--- a/profitbricks/src/test/java/org/jclouds/profitbricks/binder/datacenter/CreateDataCenterRequestBinderTest.java
+++ b/profitbricks/src/test/java/org/jclouds/profitbricks/binder/datacenter/CreateDataCenterRequestBinderTest.java
@@ -38,10 +38,10 @@ public class CreateDataCenterRequestBinderTest {
    }
 
    private final String expectedPayload
-	   = ("      <ws:createDataCenter>\n"
-	   + "         <request>\n"
-	   + "            <dataCenterName>JClouds-DC</dataCenterName>\n"
-	   + "            <location>de/fkb</location>\n"
-	   + "         </request>\n"
-	   + "      </ws:createDataCenter>\n").replaceAll("\\s+", "");
+           = ("      <ws:createDataCenter>\n"
+           + "         <request>\n"
+           + "            <dataCenterName>JClouds-DC</dataCenterName>\n"
+           + "            <location>de/fkb</location>\n"
+           + "         </request>\n"
+           + "      </ws:createDataCenter>\n").replaceAll("\\s+", "");
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/67cae0ca/profitbricks/src/test/java/org/jclouds/profitbricks/binder/datacenter/UpdateDataCenterRequestBinderTest.java
----------------------------------------------------------------------
diff --git a/profitbricks/src/test/java/org/jclouds/profitbricks/binder/datacenter/UpdateDataCenterRequestBinderTest.java b/profitbricks/src/test/java/org/jclouds/profitbricks/binder/datacenter/UpdateDataCenterRequestBinderTest.java
index 9e72172..3f989ce 100644
--- a/profitbricks/src/test/java/org/jclouds/profitbricks/binder/datacenter/UpdateDataCenterRequestBinderTest.java
+++ b/profitbricks/src/test/java/org/jclouds/profitbricks/binder/datacenter/UpdateDataCenterRequestBinderTest.java
@@ -37,10 +37,10 @@ public class UpdateDataCenterRequestBinderTest {
    }
 
    private final String expectedPayload
-	   = ("      <ws:updateDataCenter>\n"
-	   + "         <request>\n"
-	   + "            <dataCenterId>aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee</dataCenterId>\n"
-	   + "            <dataCenterName>Apache-DC</dataCenterName>\n"
-	   + "         </request>\n"
-	   + "      </ws:updateDataCenter>").replaceAll("\\s+", "");
+           = ("      <ws:updateDataCenter>\n"
+           + "         <request>\n"
+           + "            <dataCenterId>aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee</dataCenterId>\n"
+           + "            <dataCenterName>Apache-DC</dataCenterName>\n"
+           + "         </request>\n"
+           + "      </ws:updateDataCenter>").replaceAll("\\s+", "");
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/67cae0ca/profitbricks/src/test/java/org/jclouds/profitbricks/binder/server/CreateServerRequestBinderTest.java
----------------------------------------------------------------------
diff --git a/profitbricks/src/test/java/org/jclouds/profitbricks/binder/server/CreateServerRequestBinderTest.java b/profitbricks/src/test/java/org/jclouds/profitbricks/binder/server/CreateServerRequestBinderTest.java
index bf18841..355f318 100644
--- a/profitbricks/src/test/java/org/jclouds/profitbricks/binder/server/CreateServerRequestBinderTest.java
+++ b/profitbricks/src/test/java/org/jclouds/profitbricks/binder/server/CreateServerRequestBinderTest.java
@@ -21,7 +21,7 @@ import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotNull;
 import org.testng.annotations.Test;
 
-@Test( groups = "unit", testName = "CreateServerRequestBinderTest" )
+@Test(groups = "unit", testName = "CreateServerRequestBinderTest")
 public class CreateServerRequestBinderTest {
 
    @Test
@@ -29,37 +29,37 @@ public class CreateServerRequestBinderTest {
       CreateServerRequestBinder binder = new CreateServerRequestBinder();
 
       Server.Request.CreatePayload payload = Server.Request.creatingBuilder()
-              .name( "jclouds-node" )
-              .cores( 4 )
-              .ram( 4 * 1024 )
-              .dataCenterId( "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee" )
+              .name("jclouds-node")
+              .cores(4)
+              .ram(4 * 1024)
+              .dataCenterId("aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee")
               .build();
 
-      String actual = binder.createPayload( payload );
-      assertNotNull( actual, "Binder returned null payload" );
-      assertEquals( actual, expectedPayload );
+      String actual = binder.createPayload(payload);
+      assertNotNull(actual, "Binder returned null payload");
+      assertEquals(actual, expectedPayload);
    }
 
    private final String expectedPayload
-           = ( "      <ws:createServer>\n"
+           = ("      <ws:createServer>\n"
            + "         <request>\n"
            + "            <dataCenterId>aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee</dataCenterId>\n"
            + "            <cores>4</cores>\n"
            + "            <ram>4096</ram>\n"
            + "            <serverName>jclouds-node</serverName>\n"
-//           + "            <bootFromStorageId>?</bootFromStorageId>\n"
-//           + "            <bootFromImageId>?</bootFromImageId>\n"
-//           + "            <internetAccess>false</internetAccess>\n"
-//           + "            <lanId>?</lanId>\n"
-//           + "            <osType>?</osType>\n"
-//           + "            <availabilityZone>AUTO</availabilityZone>\n"
-//           + "            <cpuHotPlug>false</cpuHotPlug>\n"
-//           + "            <ramHotPlug>false</ramHotPlug>\n"
-//           + "            <nicHotPlug>false</nicHotPlug>\n"
-//           + "            <nicHotUnPlug>false</nicHotUnPlug>\n"
-//           + "            <discVirtioHotPlug>false</discVirtioHotPlug>\n"
-//           + "            <discVirtioHotUnPlug>false</discVirtioHotUnPlug>\n"
+           //           + "            <bootFromStorageId>?</bootFromStorageId>\n"
+           //           + "            <bootFromImageId>?</bootFromImageId>\n"
+           //           + "            <internetAccess>false</internetAccess>\n"
+           //           + "            <lanId>?</lanId>\n"
+           //           + "            <osType>?</osType>\n"
+           //           + "            <availabilityZone>AUTO</availabilityZone>\n"
+           //           + "            <cpuHotPlug>false</cpuHotPlug>\n"
+           //           + "            <ramHotPlug>false</ramHotPlug>\n"
+           //           + "            <nicHotPlug>false</nicHotPlug>\n"
+           //           + "            <nicHotUnPlug>false</nicHotUnPlug>\n"
+           //           + "            <discVirtioHotPlug>false</discVirtioHotPlug>\n"
+           //           + "            <discVirtioHotUnPlug>false</discVirtioHotUnPlug>\n"
            + "         </request>\n"
-           + "      </ws:createServer>" )
-           .replaceAll( "\\s+", "" );
+           + "      </ws:createServer>")
+           .replaceAll("\\s+", "");
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/67cae0ca/profitbricks/src/test/java/org/jclouds/profitbricks/binder/server/UpdateServerRequestBinderTest.java
----------------------------------------------------------------------
diff --git a/profitbricks/src/test/java/org/jclouds/profitbricks/binder/server/UpdateServerRequestBinderTest.java b/profitbricks/src/test/java/org/jclouds/profitbricks/binder/server/UpdateServerRequestBinderTest.java
index cba666b..aa0e31c 100644
--- a/profitbricks/src/test/java/org/jclouds/profitbricks/binder/server/UpdateServerRequestBinderTest.java
+++ b/profitbricks/src/test/java/org/jclouds/profitbricks/binder/server/UpdateServerRequestBinderTest.java
@@ -21,7 +21,7 @@ import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotNull;
 import org.testng.annotations.Test;
 
-@Test( groups = "unit", testName = "UpdateServerRequestBinderTest" )
+@Test(groups = "unit", testName = "UpdateServerRequestBinderTest")
 public class UpdateServerRequestBinderTest {
 
    @Test
@@ -29,36 +29,36 @@ public class UpdateServerRequestBinderTest {
       UpdateServerRequestBinder binder = new UpdateServerRequestBinder();
 
       Server.Request.UpdatePayload payload = Server.Request.updatingBuilder()
-              .id( "qwertyui-qwer-qwer-qwer-qwertyyuiiop" )
-              .cores( 8 )
-              .ram( 8 * 1024 )
-              .name( "apache-node")
+              .id("qwertyui-qwer-qwer-qwer-qwertyyuiiop")
+              .cores(8)
+              .ram(8 * 1024)
+              .name("apache-node")
               .build();
-      
-      String actual = binder.createPayload( payload );
+
+      String actual = binder.createPayload(payload);
       assertNotNull(actual, "Binder returned null payload");
       assertEquals(actual, expectedPayload);
    }
 
    private final String expectedPayload
-           = ( "      <ws:updateServer>\n"
+           = ("      <ws:updateServer>\n"
            + "         <request>\n"
            + "            <serverId>qwertyui-qwer-qwer-qwer-qwertyyuiiop</serverId>\n"
            + "            <cores>8</cores>\n"
            + "            <ram>8192</ram>\n"
            + "            <serverName>apache-node</serverName>\n"
-//           + "            <bootFromStorageId>?</bootFromStorageId>\n"
-//           + "            <bootFromImageId>?</bootFromImageId>\n"
-//           + "            <osType>?</osType>\n"
-//           + "            <availabilityZone>?</availabilityZone>\n"
-//           + "            <cpuHotPlug>?</cpuHotPlug>\n"
-//           + "            <ramHotPlug>?</ramHotPlug>\n"
-//           + "            <nicHotPlug>?</nicHotPlug>\n"
-//           + "            <nicHotUnPlug>?</nicHotUnPlug>\n"
-//           + "            <discVirtioHotPlug>?</discVirtioHotPlug>\n"
-//           + "            <discVirtioHotUnPlug>?</discVirtioHotUnPlug>\n"
+           //           + "            <bootFromStorageId>?</bootFromStorageId>\n"
+           //           + "            <bootFromImageId>?</bootFromImageId>\n"
+           //           + "            <osType>?</osType>\n"
+           //           + "            <availabilityZone>?</availabilityZone>\n"
+           //           + "            <cpuHotPlug>?</cpuHotPlug>\n"
+           //           + "            <ramHotPlug>?</ramHotPlug>\n"
+           //           + "            <nicHotPlug>?</nicHotPlug>\n"
+           //           + "            <nicHotUnPlug>?</nicHotUnPlug>\n"
+           //           + "            <discVirtioHotPlug>?</discVirtioHotPlug>\n"
+           //           + "            <discVirtioHotUnPlug>?</discVirtioHotUnPlug>\n"
            + "         </request>\n"
-           + "      </ws:updateServer>" )
-           .replaceAll( "\\s+", "" );
+           + "      </ws:updateServer>")
+           .replaceAll("\\s+", "");
 
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/67cae0ca/profitbricks/src/test/java/org/jclouds/profitbricks/binder/storage/ConnectStorageToServerRequestBinderTest.java
----------------------------------------------------------------------
diff --git a/profitbricks/src/test/java/org/jclouds/profitbricks/binder/storage/ConnectStorageToServerRequestBinderTest.java b/profitbricks/src/test/java/org/jclouds/profitbricks/binder/storage/ConnectStorageToServerRequestBinderTest.java
new file mode 100644
index 0000000..0c0d3a4
--- /dev/null
+++ b/profitbricks/src/test/java/org/jclouds/profitbricks/binder/storage/ConnectStorageToServerRequestBinderTest.java
@@ -0,0 +1,55 @@
+/*
+ * 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.profitbricks.binder.storage;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+
+import org.jclouds.profitbricks.domain.Storage;
+import org.testng.annotations.Test;
+
+@Test(groups = "unit", testName = "ConnectStorageToServerRequestBinderTest")
+public class ConnectStorageToServerRequestBinderTest {
+
+   @Test
+   public void testCreatePayload() {
+      ConnectStorageToServerRequestBinder binder = new ConnectStorageToServerRequestBinder();
+
+      Storage.Request.ConnectPayload payload = Storage.Request.connectingBuilder()
+              .serverId("qwertyui-qwer-qwer-qwer-qwertyyuiiop")
+              .storageId("qswdefrg-qaws-qaws-defe-rgrgdsvcxbrh")
+              .busType(Storage.BusType.VIRTIO)
+              .deviceNumber(2)
+              .build();
+
+      String actual = binder.createPayload(payload);
+      assertNotNull(actual, "Binder returned null payload");
+      assertEquals(actual, expectedPayload);
+   }
+
+   private final String expectedPayload
+           = ("      <ws:connectStorageToServer>\n"
+           + "         <request>\n"
+           + "            <storageId>qswdefrg-qaws-qaws-defe-rgrgdsvcxbrh</storageId>\n"
+           + "            <serverId>qwertyui-qwer-qwer-qwer-qwertyyuiiop</serverId>\n"
+           + "            <busType>VIRTIO</busType>\n"
+           + "            <deviceNumber>2</deviceNumber>\n"
+           + "         </request>\n"
+           + "      </ws:connectStorageToServer>")
+           .replaceAll("\\s+", "");
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/67cae0ca/profitbricks/src/test/java/org/jclouds/profitbricks/binder/storage/CreateStorageRequestBinderTest.java
----------------------------------------------------------------------
diff --git a/profitbricks/src/test/java/org/jclouds/profitbricks/binder/storage/CreateStorageRequestBinderTest.java b/profitbricks/src/test/java/org/jclouds/profitbricks/binder/storage/CreateStorageRequestBinderTest.java
new file mode 100644
index 0000000..9120be7
--- /dev/null
+++ b/profitbricks/src/test/java/org/jclouds/profitbricks/binder/storage/CreateStorageRequestBinderTest.java
@@ -0,0 +1,56 @@
+/*
+ * 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.profitbricks.binder.storage;
+
+import org.jclouds.profitbricks.domain.Storage;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import org.testng.annotations.Test;
+
+@Test(groups = "unit", testName = "CreateStorageRequestBinderTest")
+public class CreateStorageRequestBinderTest {
+
+   @Test
+   public void testCreatePayload() {
+      CreateStorageRequestBinder binder = new CreateStorageRequestBinder();
+
+      Storage.Request.CreatePayload payload = Storage.Request.creatingBuilder()
+              .name("hdd-1")
+              .size(60f)
+              .dataCenterId("aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee")
+              .mountImageId("5ad99c9e-9166-11e4-9d74-52540066fee9")
+              .imagePassword("qqqqqqqqq")
+              .build();
+
+      String actual = binder.createPayload(payload);
+      assertNotNull(actual, "Binder returned null payload");
+      assertEquals(actual, expectedPayload);
+   }
+
+   private final String expectedPayload
+           = ("      <ws:createStorage>\n"
+           + "         <request>\n"
+           + "            <dataCenterId>aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee</dataCenterId>\n"
+           + "            <storageName>hdd-1</storageName>\n"
+           + "            <size>60</size>\n"
+           + "            <mountImageId>5ad99c9e-9166-11e4-9d74-52540066fee9</mountImageId>\n"
+           + "            <profitBricksImagePassword>qqqqqqqqq</profitBricksImagePassword>\n"
+           + "         </request>\n"
+           + "      </ws:createStorage>")
+           .replaceAll("\\s+", "");
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/67cae0ca/profitbricks/src/test/java/org/jclouds/profitbricks/binder/storage/UpdateStorageRequestBinderTest.java
----------------------------------------------------------------------
diff --git a/profitbricks/src/test/java/org/jclouds/profitbricks/binder/storage/UpdateStorageRequestBinderTest.java b/profitbricks/src/test/java/org/jclouds/profitbricks/binder/storage/UpdateStorageRequestBinderTest.java
new file mode 100644
index 0000000..0240297
--- /dev/null
+++ b/profitbricks/src/test/java/org/jclouds/profitbricks/binder/storage/UpdateStorageRequestBinderTest.java
@@ -0,0 +1,53 @@
+/*
+ * 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.profitbricks.binder.storage;
+
+import org.jclouds.profitbricks.domain.Storage;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import org.testng.annotations.Test;
+
+@Test(groups = "unit", testName = "UpdateStorageRequestBinderTest")
+public class UpdateStorageRequestBinderTest {
+
+   @Test
+   public void testUpdatePayload() {
+      UpdateStorageRequestBinder binder = new UpdateStorageRequestBinder();
+
+      Storage.Request.UpdatePayload payload = Storage.Request.updatingBuilder()
+              .id("pppppppp-llkl-kkjk-fhgh-vnmegrdgdsgr")
+              .size(100f)
+              .name("hdd-2")
+              .mountImageId("5f3cac96-915f-11e4-9d74-52540066fee9")
+              .build();
+
+      String actual = binder.createPayload(payload);
+      assertNotNull(actual, "Binder returned null payload");
+      assertEquals(actual, expectedPayload);
+   }
+
+   private final String expectedPayload
+           = ("      <ws:updateStorage>\n"
+           + "         <request>\n"
+           + "            <storageId>pppppppp-llkl-kkjk-fhgh-vnmegrdgdsgr</storageId>\n"
+           + "            <size>100</size>\n"
+           + "            <storageName>hdd-2</storageName>\n"
+           + "            <mountImageId>5f3cac96-915f-11e4-9d74-52540066fee9</mountImageId>\n"
+           + "         </request>\n"
+           + "      </ws:updateStorage>")
+           .replaceAll("\\s+", "");
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/67cae0ca/profitbricks/src/test/java/org/jclouds/profitbricks/compute/internal/ProvisioningStatusPollingPredicateTest.java
----------------------------------------------------------------------
diff --git a/profitbricks/src/test/java/org/jclouds/profitbricks/compute/internal/ProvisioningStatusPollingPredicateTest.java b/profitbricks/src/test/java/org/jclouds/profitbricks/compute/internal/ProvisioningStatusPollingPredicateTest.java
index 05001f2..f69fc02 100644
--- a/profitbricks/src/test/java/org/jclouds/profitbricks/compute/internal/ProvisioningStatusPollingPredicateTest.java
+++ b/profitbricks/src/test/java/org/jclouds/profitbricks/compute/internal/ProvisioningStatusPollingPredicateTest.java
@@ -39,7 +39,7 @@ import com.squareup.okhttp.mockwebserver.MockWebServer;
 public class ProvisioningStatusPollingPredicateTest extends BaseProfitBricksMockTest {
 
    @Test
-   public void testPredicate() throws Exception {
+   public void testDataCenterPredicate() throws Exception {
       MockWebServer server = mockWebServer();
 
       byte[] payloadInProcess = payloadFromResource("/datacenter/datacenter-state-inprocess.xml");
@@ -71,4 +71,70 @@ public class ProvisioningStatusPollingPredicateTest extends BaseProfitBricksMock
       }
    }
 
+   @Test
+   public void testServerPredicate() throws Exception {
+      MockWebServer server = mockWebServer();
+
+      byte[] payloadInProcess = payloadFromResource("/server/server-state-inprocess.xml");
+      byte[] payloadAvailable = payloadFromResource("/server/server.xml");
+
+      // wait 3 times
+      server.enqueue(new MockResponse().setBody(payloadInProcess));
+      server.enqueue(new MockResponse().setBody(payloadInProcess));
+      server.enqueue(new MockResponse().setBody(payloadInProcess));
+      server.enqueue(new MockResponse().setBody(payloadAvailable));
+
+      server.enqueue(new MockResponse().setBody(payloadAvailable));
+
+      ProfitBricksApi pbApi = api(server.getUrl(rootUrl));
+
+      Predicate<String> waitUntilAvailable = Predicates2.retry(
+              new ProvisioningStatusPollingPredicate(pbApi, ProvisioningStatusAware.SERVER, ProvisioningState.AVAILABLE),
+              30l, 1l, TimeUnit.SECONDS);
+
+      String id = "qwertyui-qwer-qwer-qwer-qwertyyuiiop";
+      try {
+         waitUntilAvailable.apply(id);
+         ProvisioningState finalState = pbApi.serverApi().getServer(id).state();
+         assertRequestHasCommonProperties(server.takeRequest());
+         assertEquals(finalState, ProvisioningState.AVAILABLE);
+      } finally {
+         pbApi.close();
+         server.shutdown();
+      }
+   }
+
+   @Test
+   public void testStoragePredicate() throws Exception {
+      MockWebServer server = mockWebServer();
+
+      byte[] payloadInProcess = payloadFromResource("/storage/storage-state-inprocess.xml");
+      byte[] payloadAvailable = payloadFromResource("/storage/storage.xml");
+
+      // wait 3 times
+      server.enqueue(new MockResponse().setBody(payloadInProcess));
+      server.enqueue(new MockResponse().setBody(payloadInProcess));
+      server.enqueue(new MockResponse().setBody(payloadInProcess));
+      server.enqueue(new MockResponse().setBody(payloadAvailable));
+
+      server.enqueue(new MockResponse().setBody(payloadAvailable));
+
+      ProfitBricksApi pbApi = api(server.getUrl(rootUrl));
+
+      Predicate<String> waitUntilAvailable = Predicates2.retry(
+              new ProvisioningStatusPollingPredicate(pbApi, ProvisioningStatusAware.STORAGE, ProvisioningState.AVAILABLE),
+              30l, 1l, TimeUnit.SECONDS);
+
+      String id = "qswdefrg-qaws-qaws-defe-rgrgdsvcxbrh";
+      try {
+         waitUntilAvailable.apply(id);
+         ProvisioningState finalState = pbApi.storageApi().getStorage(id).state();
+         assertRequestHasCommonProperties(server.takeRequest());
+         assertEquals(finalState, ProvisioningState.AVAILABLE);
+      } finally {
+         pbApi.close();
+         server.shutdown();
+      }
+   }
+
 }