merged latest
diff --git a/README.txt b/README.txt
index d8ec695..2bb3cce 100644
--- a/README.txt
+++ b/README.txt
@@ -7,8 +7,8 @@
 bootstrap machines in the cloud.  blobstore helps you manage key-value
 data.
  
-our current version is 1.4.0
-our next maintenance version is 1.4.1-SNAPSHOT
+our current version is 1.4.2
+our next maintenance version is 1.4.2-SNAPSHOT
 our dev version is 1.5.0-SNAPSHOT
  
 check out our examples site! https://github.com/jclouds/jclouds-examples
@@ -23,7 +23,8 @@
                           openhosting-east1, serverlove-z1-man, skalicloud-sdg-my,
                           greenhousedata-element-vcloud, softlayer, cloudsigma (generic),
                           cloudstack (generic), ninefold-compute, openstack-nov (keystone),
-                          hpcloud-compute, trystack-nova, openstack-nova-ec2
+                          hpcloud-compute, trystack-nova, openstack-nova-ec2,
+                          rackspace-cloudservers-us (next gen)
 
   * note * the pom dependency org.jclouds/jclouds-allcompute gives you access to
            to all of these providers
diff --git a/all/pom.xml b/all/pom.xml
index 4815115..c0ed79d 100644
--- a/all/pom.xml
+++ b/all/pom.xml
@@ -31,6 +31,11 @@
     <name>all</name>
     <dependencies>
         <dependency>
+            <groupId>org.jclouds.api</groupId>
+            <artifactId>rackspace-cloudidentity</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
             <groupId>org.jclouds.provider</groupId>
             <artifactId>aws-cloudwatch</artifactId>
             <version>${project.version}</version>
diff --git a/allcompute/pom.xml b/allcompute/pom.xml
index 907098c..c36fd74 100644
--- a/allcompute/pom.xml
+++ b/allcompute/pom.xml
@@ -210,5 +210,10 @@
             <artifactId>trystack-nova</artifactId>
             <version>${project.version}</version>
         </dependency>
+        <dependency>
+            <groupId>org.jclouds.provider</groupId>
+            <artifactId>rackspace-cloudservers-us</artifactId>
+            <version>${project.version}</version>
+        </dependency>
     </dependencies>
 </project>
diff --git a/apis/cloudloadbalancers/src/main/java/org/jclouds/cloudloadbalancers/config/CloudLoadBalancersRestClientModule.java b/apis/cloudloadbalancers/src/main/java/org/jclouds/cloudloadbalancers/config/CloudLoadBalancersRestClientModule.java
index f7bc1f3..5ccaf9a 100644
--- a/apis/cloudloadbalancers/src/main/java/org/jclouds/cloudloadbalancers/config/CloudLoadBalancersRestClientModule.java
+++ b/apis/cloudloadbalancers/src/main/java/org/jclouds/cloudloadbalancers/config/CloudLoadBalancersRestClientModule.java
@@ -48,10 +48,10 @@
 import org.jclouds.rest.ConfiguresRestClient;
 import org.jclouds.rest.annotations.ApiVersion;
 import org.jclouds.rest.config.RestClientModule;
+import org.jclouds.util.Suppliers2;
 
 import com.google.common.base.Function;
 import com.google.common.base.Supplier;
-import com.google.common.base.Suppliers;
 import com.google.common.collect.ImmutableMap;
 import com.google.inject.AbstractModule;
 import com.google.inject.Provides;
@@ -94,7 +94,7 @@
       @Singleton
       @Named(RackspaceConstants.PROPERTY_ACCOUNT_ID)
       protected Supplier<String> accountID(RegionIdToURISupplier.Factory factory, @ApiVersion String apiVersion) {
-         return Suppliers.compose(new Function<URI, String>() {
+         return Suppliers2.compose(new Function<URI, String>() {
 
             @Override
             public String apply(URI arg0) {
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/binders/BindTemplateMetadataToQueryParams.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/binders/BindTemplateMetadataToQueryParams.java
index db265ce..11c3907 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/binders/BindTemplateMetadataToQueryParams.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/binders/BindTemplateMetadataToQueryParams.java
@@ -57,8 +57,8 @@
       if (metadata.getVirtualMachineId() != null) {
 	      request = ModifyRequest.addQueryParam(request, "virtualmachineid", metadata.getVirtualMachineId(), uriBuilderProvider.get());
       }
-      if (metadata.getPasswordEnabled() != null) {
-	      request = ModifyRequest.addQueryParam(request, "passwordenabled", metadata.getPasswordEnabled(), uriBuilderProvider.get());
+      if (metadata.isPasswordEnabled() != null) {
+	      request = ModifyRequest.addQueryParam(request, "passwordenabled", metadata.isPasswordEnabled(), uriBuilderProvider.get());
       }
       return request;
    }
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/config/CloudStackComputeServiceContextModule.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/config/CloudStackComputeServiceContextModule.java
index abcc733..f36b771 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/config/CloudStackComputeServiceContextModule.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/config/CloudStackComputeServiceContextModule.java
@@ -18,7 +18,6 @@
  */
 package org.jclouds.cloudstack.compute.config;
 
-import static com.google.common.base.Preconditions.checkNotNull;
 import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL;
 
 import java.util.Map;
@@ -26,7 +25,6 @@
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicReference;
 
-import javax.inject.Inject;
 import javax.inject.Named;
 import javax.inject.Singleton;
 
@@ -56,7 +54,6 @@
 import org.jclouds.cloudstack.functions.GetIPForwardingRulesByVirtualMachine;
 import org.jclouds.cloudstack.functions.StaticNATVirtualMachineInNetwork;
 import org.jclouds.cloudstack.functions.ZoneIdToZone;
-import org.jclouds.cloudstack.options.ListFirewallRulesOptions;
 import org.jclouds.cloudstack.predicates.JobComplete;
 import org.jclouds.cloudstack.suppliers.GetCurrentUser;
 import org.jclouds.cloudstack.suppliers.NetworksForCurrentUser;
@@ -70,17 +67,16 @@
 import org.jclouds.domain.Location;
 import org.jclouds.predicates.RetryablePredicate;
 import org.jclouds.rest.AuthorizationException;
-import org.jclouds.rest.ResourceNotFoundException;
 import org.jclouds.rest.suppliers.MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier;
 
 import com.google.common.base.Function;
+import com.google.common.base.Objects;
 import com.google.common.base.Predicate;
 import com.google.common.base.Supplier;
 import com.google.common.cache.CacheBuilder;
 import com.google.common.cache.CacheLoader;
 import com.google.common.cache.LoadingCache;
 import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Maps;
 import com.google.inject.Provides;
 import com.google.inject.TypeLiteral;
@@ -127,14 +123,18 @@
    @Memoized
    public Supplier<Map<String, String>> listOSCategories(AtomicReference<AuthorizationException> authException, @Named(PROPERTY_SESSION_INTERVAL) long seconds,
          final CloudStackClient client) {
-      return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier<Map<String, String>>(authException,
-            seconds, new Supplier<Map<String, String>>() {
+      return MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(authException,
+            new Supplier<Map<String, String>>() {
                @Override
                public Map<String, String> get() {
                   GuestOSClient guestOSClient = client.getGuestOSClient();
                   return guestOSClient.listOSCategories();
                }
-            });
+               @Override
+               public String toString() {
+                  return Objects.toStringHelper(client.getGuestOSClient()).add("method", "listOSCategories").toString();
+               }
+            }, seconds, TimeUnit.SECONDS);
    }
 
    @Provides
@@ -142,8 +142,8 @@
    @Memoized
    public Supplier<Map<String, OSType>> listOSTypes(AtomicReference<AuthorizationException> authException, @Named(PROPERTY_SESSION_INTERVAL) long seconds,
          final CloudStackClient client) {
-      return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier<Map<String, OSType>>(authException,
-            seconds, new Supplier<Map<String, OSType>>() {
+      return MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(authException,
+            new Supplier<Map<String, OSType>>() {
                @Override
                public Map<String, OSType> get() {
                   GuestOSClient guestOSClient = client.getGuestOSClient();
@@ -155,7 +155,11 @@
                      }
                   });
                }
-            });
+               @Override
+               public String toString() {
+                  return Objects.toStringHelper(client.getGuestOSClient()).add("method", "listOSTypes").toString();
+               }
+            }, seconds, TimeUnit.SECONDS);
    }
 
    @Provides
@@ -163,8 +167,8 @@
    @Memoized
    public Supplier<Map<String, Network>> listNetworks(AtomicReference<AuthorizationException> authException, @Named(PROPERTY_SESSION_INTERVAL) long seconds,
          final NetworksForCurrentUser networksForCurrentUser) {
-      return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier<Map<String, Network>>(authException,
-            seconds, networksForCurrentUser);
+      return MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(authException, networksForCurrentUser,
+               seconds, TimeUnit.SECONDS);
    }
 
    @Provides
@@ -172,8 +176,8 @@
    @Memoized
    public Supplier<User> getCurrentUser(AtomicReference<AuthorizationException> authException, @Named(PROPERTY_SESSION_INTERVAL) long seconds,
          final GetCurrentUser getCurrentUser) {
-      return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier<User>(authException, seconds,
-            getCurrentUser);
+      return MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(authException, getCurrentUser,
+               seconds, TimeUnit.SECONDS);
    }
 
    @Provides
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/strategy/AdvancedNetworkOptionsConverter.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/strategy/AdvancedNetworkOptionsConverter.java
index 524aeb7..45eb4d2 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/strategy/AdvancedNetworkOptionsConverter.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/strategy/AdvancedNetworkOptionsConverter.java
@@ -48,7 +48,7 @@
       checkArgument(templateOptions.getSecurityGroupIds().isEmpty(), "security groups cannot be specified for locations (zones) that use advanced networking");
       if (templateOptions.getNetworkIds().size() > 0) {
          options.networkIds(templateOptions.getNetworkIds());
-      } else {
+      } else if (templateOptions.getIpsToNetworks().isEmpty()) {
          checkArgument(!networks.isEmpty(), "please setup a network for zone: " + zoneId);
          Network defaultNetworkInZone = Iterables.getFirst(filter(networks.values(), and(defaultNetworkInZone(zoneId), supportsStaticNAT())), null);
          if(defaultNetworkInZone == null) {
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Account.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Account.java
index 33dae7d..0cd19c6 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Account.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Account.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -20,207 +20,31 @@
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import java.beans.ConstructorProperties;
 import java.util.Map;
 import java.util.Set;
 
-import javax.annotation.Nullable;
+import javax.inject.Named;
+
+import org.jclouds.javax.annotation.Nullable;
 
 import com.google.common.base.CaseFormat;
 import com.google.common.base.Function;
 import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
 import com.google.common.collect.ForwardingSet;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Maps;
-import com.google.gson.annotations.SerializedName;
 
 /**
+ * Class Account
+ *
  * @author Adrian Cole
  */
-public class Account extends ForwardingSet<User> implements Comparable<Account> {
+public class Account extends ForwardingSet<User> {
 
-   public static Builder builder() {
-      return new Builder();
-   }
-
-   public static class Builder {
-      private String id;
-      private Type type;
-      private String networkDomain;
-      private String domain;
-      private String domainId;
-      private Long IPsAvailable;
-      private Long IPLimit;
-      private long IPs;
-      private boolean cleanupRequired;
-      private String name;
-      private long receivedBytes;
-      private long sentBytes;
-      private Long snapshotsAvailable;
-      private Long snapshotLimit;
-      private long snapshots;
-      private State state;
-      private Long templatesAvailable;
-      private Long templateLimit;
-      private long templates;
-      private Long VMsAvailable;
-      private Long VMLimit;
-      private long VMsRunning;
-      private long VMsStopped;
-      private long VMs;
-      private Long volumesAvailable;
-      private Long volumeLimit;
-      private long volumes;
-      private Set<User> users = ImmutableSet.of();
-
-      public Builder id(String id) {
-         this.id = id;
-         return this;
-      }
-
-      public Builder type(Type type) {
-         this.type = type;
-         return this;
-      }
-
-      public Builder networkDomain(String networkDomain) {
-         this.networkDomain = networkDomain;
-         return this;
-      }
-
-      public Builder domain(String domain) {
-         this.domain = domain;
-         return this;
-      }
-
-      public Builder domainId(String domainId) {
-         this.domainId = domainId;
-         return this;
-      }
-
-      public Builder IPsAvailable(Long IPsAvailable) {
-         this.IPsAvailable = IPsAvailable;
-         return this;
-      }
-
-      public Builder IPLimit(Long IPLimit) {
-         this.IPLimit = IPLimit;
-         return this;
-      }
-
-      public Builder IPs(long IPs) {
-         this.IPs = IPs;
-         return this;
-      }
-
-      public Builder cleanupRequired(boolean cleanupRequired) {
-         this.cleanupRequired = cleanupRequired;
-         return this;
-      }
-
-      public Builder name(String name) {
-         this.name = name;
-         return this;
-      }
-
-      public Builder receivedBytes(long receivedBytes) {
-         this.receivedBytes = receivedBytes;
-         return this;
-      }
-
-      public Builder sentBytes(long sentBytes) {
-         this.sentBytes = sentBytes;
-         return this;
-      }
-
-      public Builder snapshotsAvailable(Long snapshotsAvailable) {
-         this.snapshotsAvailable = snapshotsAvailable;
-         return this;
-      }
-
-      public Builder snapshotLimit(Long snapshotLimit) {
-         this.snapshotLimit = snapshotLimit;
-         return this;
-      }
-
-      public Builder snapshots(long snapshots) {
-         this.snapshots = snapshots;
-         return this;
-      }
-
-      public Builder state(State state) {
-         this.state = state;
-         return this;
-      }
-
-      public Builder templatesAvailable(Long templatesAvailable) {
-         this.templatesAvailable = templatesAvailable;
-         return this;
-      }
-
-      public Builder templateLimit(Long templateLimit) {
-         this.templateLimit = templateLimit;
-         return this;
-      }
-
-      public Builder templates(long templates) {
-         this.templates = templates;
-         return this;
-      }
-
-      public Builder VMsAvailable(Long VMsAvailable) {
-         this.VMsAvailable = VMsAvailable;
-         return this;
-      }
-
-      public Builder VMLimit(Long VMLimit) {
-         this.VMLimit = VMLimit;
-         return this;
-      }
-
-      public Builder VMsRunning(long VMsRunning) {
-         this.VMsRunning = VMsRunning;
-         return this;
-      }
-
-      public Builder VMsStopped(long VMsStopped) {
-         this.VMsStopped = VMsStopped;
-         return this;
-      }
-
-      public Builder VMs(long VMs) {
-         this.VMs = VMs;
-         return this;
-      }
-
-      public Builder volumesAvailable(Long volumesAvailable) {
-         this.volumesAvailable = volumesAvailable;
-         return this;
-      }
-
-      public Builder volumeLimit(Long volumeLimit) {
-         this.volumeLimit = volumeLimit;
-         return this;
-      }
-
-      public Builder volumes(long volumes) {
-         this.volumes = volumes;
-         return this;
-      }
-
-      public Builder users(Set<User> users) {
-         this.users = ImmutableSet.copyOf(checkNotNull(users, "users"));
-         return this;
-      }
-
-      public Account build() {
-         return new Account(id, type, networkDomain, domain, domainId, IPsAvailable, IPLimit, IPs, cleanupRequired, name,
-               receivedBytes, sentBytes, snapshotsAvailable, snapshotLimit, snapshots, state, templatesAvailable,
-               templateLimit, templates, VMsAvailable, VMLimit, VMsRunning, VMsStopped, VMs, volumesAvailable,
-               volumeLimit, volumes, users);
-      }
-
-   }
-
+   /**
+    */
    public static enum State {
       ENABLED, DISABLED, LOCKED, UNRECOGNIZED;
 
@@ -239,6 +63,8 @@
 
    }
 
+   /**
+    */
    public static enum Type {
       /**
        * API access for all the resources associated with their account. There
@@ -285,71 +111,386 @@
 
    }
 
-   private String id;
-   @SerializedName("accounttype")
-   private Type type;
-   @SerializedName("networkdomain")
-   private String networkDomain;
-   private String domain;
-   @SerializedName("domainId")
-   private String domainId;
-   @SerializedName("ipsavailable")
-   private Long IPsAvailable;
-   @SerializedName("iplimit")
-   private Long IPLimit;
-   @SerializedName("iptotal")
-   private long IPs;
-   @SerializedName("iscleanuprequired")
-   private boolean cleanupRequired;
-   private String name;
-   @SerializedName("receivedbytes")
-   private long receivedBytes;
-   @SerializedName("sentbytes")
-   private long sentBytes;
-   @SerializedName("snapshotavailable")
-   private Long snapshotsAvailable;
-   @SerializedName("snapshotLimit")
-   private Long snapshotLimit;
-   @SerializedName("snapshottotal")
-   private long snapshots;
-   private State state;
-   @SerializedName("templateavailable")
-   private Long templatesAvailable;
-   @SerializedName("templatelimit")
-   private Long templateLimit;
-   @SerializedName("templatetotal")
-   private long templates;
-   @SerializedName("vmavailable")
-   private Long VMsAvailable;
-   @SerializedName("vmlimit")
-   private Long VMLimit;
-   @SerializedName("vmrunning")
-   private long VMsRunning;
-   @SerializedName("vmstopped")
-   private long VMsStopped;
-   @SerializedName("vmtotal")
-   private long VMs;
-   @SerializedName("volumeavailable")
-   private Long volumesAvailable;
-   @SerializedName("volumelimit")
-   private Long volumeLimit;
-   @SerializedName("volumetotal")
-   private long volumes;
-   private Set<User> users;
+   public static Builder<?> builder() {
+      return new ConcreteBuilder();
+   }
 
-   public Account(String id, Type type, String networkDomain, String domain, String domainId, Long IPsAvailable, Long IPLimit, long iPs,
-                  boolean cleanupRequired, String name, long receivedBytes, long sentBytes, Long snapshotsAvailable,
-                  Long snapshotLimit, long snapshots, org.jclouds.cloudstack.domain.Account.State state,
-                  Long templatesAvailable, Long templateLimit, long templates, Long VMsAvailable, Long VMLimit, long vMsRunning,
-                  long vMsStopped, long vMs, Long volumesAvailable, Long volumeLimit, long volumes, Set<User> users) {
-      this.id = id;
+   public Builder<?> toBuilder() {
+      return new ConcreteBuilder().fromAccount(this);
+   }
+
+   public static abstract class Builder<T extends Builder<T>>  {
+      protected abstract T self();
+
+      protected String id;
+      protected Account.Type type;
+      protected String networkDomain;
+      protected String domain;
+      protected String domainId;
+      protected Long IPsAvailable;
+      protected Long IPLimit;
+      protected long IPs;
+      protected boolean cleanupRequired;
+      protected String name;
+      protected long receivedBytes;
+      protected long sentBytes;
+      protected Long snapshotsAvailable;
+      protected Long snapshotLimit;
+      protected long snapshots;
+      protected Account.State state;
+      protected Long templatesAvailable;
+      protected Long templateLimit;
+      protected long templates;
+      protected Long VMsAvailable;
+      protected Long VMLimit;
+      protected long VMsRunning;
+      protected long VMsStopped;
+      protected long VMs;
+      protected Long volumesAvailable;
+      protected Long volumeLimit;
+      protected long volumes;
+      protected Set<User> users = ImmutableSet.of();
+
+      /**
+       * @see Account#getId()
+       */
+      public T id(String id) {
+         this.id = id;
+         return self();
+      }
+
+      /**
+       * @see Account#getType()
+       */
+      public T type(Account.Type type) {
+         this.type = type;
+         return self();
+      }
+
+      /**
+       * @see Account#getNetworkDomain()
+       */
+      public T networkDomain(String networkDomain) {
+         this.networkDomain = networkDomain;
+         return self();
+      }
+
+      /**
+       * @see Account#getDomain()
+       */
+      public T domain(String domain) {
+         this.domain = domain;
+         return self();
+      }
+
+      /**
+       * @see Account#getDomainId()
+       */
+      public T domainId(String domainId) {
+         this.domainId = domainId;
+         return self();
+      }
+
+      /**
+       * @see Account#getIPsAvailable()
+       */
+      public T IPsAvailable(Long IPsAvailable) {
+         this.IPsAvailable = IPsAvailable;
+         return self();
+      }
+
+      /**
+       * @see Account#getIPLimit()
+       */
+      public T IPLimit(Long IPLimit) {
+         this.IPLimit = IPLimit;
+         return self();
+      }
+
+      /**
+       * @see Account#getIPs()
+       */
+      public T IPs(long IPs) {
+         this.IPs = IPs;
+         return self();
+      }
+
+      /**
+       * @see Account#isCleanupRequired()
+       */
+      public T cleanupRequired(boolean cleanupRequired) {
+         this.cleanupRequired = cleanupRequired;
+         return self();
+      }
+
+      /**
+       * @see Account#getName()
+       */
+      public T name(String name) {
+         this.name = name;
+         return self();
+      }
+
+      /**
+       * @see Account#getReceivedBytes()
+       */
+      public T receivedBytes(long receivedBytes) {
+         this.receivedBytes = receivedBytes;
+         return self();
+      }
+
+      /**
+       * @see Account#getSentBytes()
+       */
+      public T sentBytes(long sentBytes) {
+         this.sentBytes = sentBytes;
+         return self();
+      }
+
+      /**
+       * @see Account#getSnapshotsAvailable()
+       */
+      public T snapshotsAvailable(Long snapshotsAvailable) {
+         this.snapshotsAvailable = snapshotsAvailable;
+         return self();
+      }
+
+      /**
+       * @see Account#getSnapshotLimit()
+       */
+      public T snapshotLimit(Long snapshotLimit) {
+         this.snapshotLimit = snapshotLimit;
+         return self();
+      }
+
+      /**
+       * @see Account#getSnapshots()
+       */
+      public T snapshots(long snapshots) {
+         this.snapshots = snapshots;
+         return self();
+      }
+
+      /**
+       * @see Account#getState()
+       */
+      public T state(Account.State state) {
+         this.state = state;
+         return self();
+      }
+
+      /**
+       * @see Account#getTemplatesAvailable()
+       */
+      public T templatesAvailable(Long templatesAvailable) {
+         this.templatesAvailable = templatesAvailable;
+         return self();
+      }
+
+      /**
+       * @see Account#getTemplateLimit()
+       */
+      public T templateLimit(Long templateLimit) {
+         this.templateLimit = templateLimit;
+         return self();
+      }
+
+      /**
+       * @see Account#getTemplates()
+       */
+      public T templates(long templates) {
+         this.templates = templates;
+         return self();
+      }
+
+      /**
+       * @see Account#getVMsAvailable()
+       */
+      public T VMsAvailable(Long VMsAvailable) {
+         this.VMsAvailable = VMsAvailable;
+         return self();
+      }
+
+      /**
+       * @see Account#getVMLimit()
+       */
+      public T VMLimit(Long VMLimit) {
+         this.VMLimit = VMLimit;
+         return self();
+      }
+
+      /**
+       * @see Account#getVMsRunning()
+       */
+      public T VMsRunning(long VMsRunning) {
+         this.VMsRunning = VMsRunning;
+         return self();
+      }
+
+      /**
+       * @see Account#getVMsStopped()
+       */
+      public T VMsStopped(long VMsStopped) {
+         this.VMsStopped = VMsStopped;
+         return self();
+      }
+
+      /**
+       * @see Account#getVMs()
+       */
+      public T VMs(long VMs) {
+         this.VMs = VMs;
+         return self();
+      }
+
+      /**
+       * @see Account#getVolumesAvailable()
+       */
+      public T volumesAvailable(Long volumesAvailable) {
+         this.volumesAvailable = volumesAvailable;
+         return self();
+      }
+
+      /**
+       * @see Account#getVolumeLimit()
+       */
+      public T volumeLimit(Long volumeLimit) {
+         this.volumeLimit = volumeLimit;
+         return self();
+      }
+
+      /**
+       * @see Account#getVolumes()
+       */
+      public T volumes(long volumes) {
+         this.volumes = volumes;
+         return self();
+      }
+
+      /**
+       * @see Account#getUsers()
+       */
+      public T users(Set<User> users) {
+         this.users = ImmutableSet.copyOf(checkNotNull(users, "users"));
+         return self();
+      }
+
+      public T users(User... in) {
+         return users(ImmutableSet.copyOf(in));
+      }
+
+      public Account build() {
+         return new Account(id, type, networkDomain, domain, domainId, IPsAvailable, IPLimit, IPs, cleanupRequired, name, receivedBytes, sentBytes, snapshotsAvailable, snapshotLimit, snapshots, state, templatesAvailable, templateLimit, templates, VMsAvailable, VMLimit, VMsRunning, VMsStopped, VMs, volumesAvailable, volumeLimit, volumes, users);
+      }
+
+      public T fromAccount(Account in) {
+         return this
+               .id(in.getId())
+               .type(in.getType())
+               .networkDomain(in.getNetworkDomain())
+               .domain(in.getDomain())
+               .domainId(in.getDomainId())
+               .IPsAvailable(in.getIPsAvailable())
+               .IPLimit(in.getIPLimit())
+               .IPs(in.getIPs())
+               .cleanupRequired(in.isCleanupRequired())
+               .name(in.getName())
+               .receivedBytes(in.getReceivedBytes())
+               .sentBytes(in.getSentBytes())
+               .snapshotsAvailable(in.getSnapshotsAvailable())
+               .snapshotLimit(in.getSnapshotLimit())
+               .snapshots(in.getSnapshots())
+               .state(in.getState())
+               .templatesAvailable(in.getTemplatesAvailable())
+               .templateLimit(in.getTemplateLimit())
+               .templates(in.getTemplates())
+               .VMsAvailable(in.getVMsAvailable())
+               .VMLimit(in.getVMLimit())
+               .VMsRunning(in.getVMsRunning())
+               .VMsStopped(in.getVMsStopped())
+               .VMs(in.getVMs())
+               .volumesAvailable(in.getVolumesAvailable())
+               .volumeLimit(in.getVolumeLimit())
+               .volumes(in.getVolumes())
+               .users(in.getUsers());
+      }
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
+   }
+
+   private final String id;
+   @Named("accounttype")
+   private final Account.Type type;
+   @Named("networkdomain")
+   private final String networkDomain;
+   private final String domain;
+   private final String domainId;
+   @Named("ipsavailable")
+   private final Long IPsAvailable;
+   @Named("iplimit")
+   private final Long IPLimit;
+   @Named("iptotal")
+   private final long IPs;
+   @Named("iscleanuprequired")
+   private final boolean cleanupRequired;
+   private final String name;
+   @Named("receivedbytes")
+   private final long receivedBytes;
+   @Named("sentbytes")
+   private final long sentBytes;
+   @Named("snapshotavailable")
+   private final Long snapshotsAvailable;
+   private final Long snapshotLimit;
+   @Named("snapshottotal")
+   private final long snapshots;
+   private final Account.State state;
+   @Named("templateavailable")
+   private final Long templatesAvailable;
+   @Named("templatelimit")
+   private final Long templateLimit;
+   @Named("templatetotal")
+   private final long templates;
+   @Named("vmavailable")
+   private final Long VMsAvailable;
+   @Named("vmlimit")
+   private final Long VMLimit;
+   @Named("vmrunning")
+   private final long VMsRunning;
+   @Named("vmstopped")
+   private final long VMsStopped;
+   @Named("vmtotal")
+   private final long VMs;
+   @Named("volumeavailable")
+   private final Long volumesAvailable;
+   @Named("volumelimit")
+   private final Long volumeLimit;
+   @Named("volumetotal")
+   private final long volumes;
+   private final Set<User> users;
+
+   @ConstructorProperties({
+         "id", "accounttype", "networkdomain", "domain", "domainId", "ipsavailable", "iplimit", "iptotal", "iscleanuprequired", "name", "receivedbytes", "sentbytes", "snapshotavailable", "snapshotLimit", "snapshottotal", "state", "templateavailable", "templatelimit", "templatetotal", "vmavailable", "vmlimit", "vmrunning", "vmstopped", "vmtotal", "volumeavailable", "volumelimit", "volumetotal", "users"
+   })
+   protected Account(String id, @Nullable Account.Type type, @Nullable String networkDomain, @Nullable String domain,
+                     @Nullable String domainId, @Nullable Long IPsAvailable, @Nullable Long IPLimit, long IPs,
+                     boolean cleanupRequired, @Nullable String name, long receivedBytes, long sentBytes, @Nullable Long snapshotsAvailable,
+                     @Nullable Long snapshotLimit, long snapshots, @Nullable Account.State state, @Nullable Long templatesAvailable,
+                     @Nullable Long templateLimit, long templates, @Nullable Long VMsAvailable, @Nullable Long VMLimit, long VMsRunning,
+                     long VMsStopped, long VMs, @Nullable Long volumesAvailable, @Nullable Long volumeLimit, long volumes,
+                     @Nullable Set<User> users) {
+      this.id = checkNotNull(id, "id");
       this.type = type;
       this.networkDomain = networkDomain;
       this.domain = domain;
       this.domainId = domainId;
       this.IPsAvailable = IPsAvailable;
       this.IPLimit = IPLimit;
-      this.IPs = iPs;
+      this.IPs = IPs;
       this.cleanupRequired = cleanupRequired;
       this.name = name;
       this.receivedBytes = receivedBytes;
@@ -363,213 +504,204 @@
       this.templates = templates;
       this.VMsAvailable = VMsAvailable;
       this.VMLimit = VMLimit;
-      this.VMsRunning = vMsRunning;
-      this.VMsStopped = vMsStopped;
-      this.VMs = vMs;
+      this.VMsRunning = VMsRunning;
+      this.VMsStopped = VMsStopped;
+      this.VMs = VMs;
       this.volumesAvailable = volumesAvailable;
       this.volumeLimit = volumeLimit;
       this.volumes = volumes;
-      this.users = ImmutableSet.copyOf(checkNotNull(users, "users"));
-   }
-
-   /**
-    * present only for serializer
-    */
-   Account() {
-
+      this.users = users == null ? ImmutableSet.<User>of() : ImmutableSet.copyOf(users);
    }
 
    /**
     * @return the id of the account
     */
    public String getId() {
-      return id;
-   }
-
-   /**
-    * @return the name of the account
-    */
-
-   public String getName() {
-      return name;
+      return this.id;
    }
 
    /**
     * @return account type (admin, domain-admin, user)
     */
-   public Type getType() {
-      return type;
+   @Nullable
+   public Account.Type getType() {
+      return this.type;
    }
 
    /**
     * @return the network domain
     */
+   @Nullable
    public String getNetworkDomain() {
-      return networkDomain;
+      return this.networkDomain;
    }
 
    /**
     * @return name of the Domain the account belongs to
     */
+   @Nullable
    public String getDomain() {
-      return domain;
+      return this.domain;
    }
 
    /**
     * @return id of the Domain the account belongs to
     */
+   @Nullable
    public String getDomainId() {
-      return domainId;
-   }
-
-   /**
-    * @return true if the account requires cleanup
-    */
-   public boolean isCleanupRequired() {
-      return cleanupRequired;
-   }
-
-   /**
-    * @return the list of users associated with account
-    */
-   public Set<User> getUsers() {
-      return users;
+      return this.domainId;
    }
 
    /**
     * @return the total number of public ip addresses available for this account
-    *         to acquire, or null if unlimited
+   to acquire, or null if unlimited
     */
    @Nullable
    public Long getIPsAvailable() {
-      return IPsAvailable;
+      return this.IPsAvailable;
    }
 
    /**
     * @return the total number of public ip addresses this account can acquire,
-    *         or null if unlimited
+   or null if unlimited
     */
    @Nullable
    public Long getIPLimit() {
-      return IPLimit;
+      return this.IPLimit;
    }
 
    /**
     * @return the total number of public ip addresses allocated for this account
     */
    public long getIPs() {
-      return IPs;
+      return this.IPs;
+   }
+
+   /**
+    * @return true if the account requires cleanup
+    */
+   public boolean isCleanupRequired() {
+      return this.cleanupRequired;
+   }
+
+   /**
+    * @return the name of the account
+    */
+   @Nullable
+   public String getName() {
+      return this.name;
    }
 
    /**
     * @return the total number of network traffic bytes received
     */
    public long getReceivedBytes() {
-      return receivedBytes;
+      return this.receivedBytes;
    }
 
    /**
     * @return the total number of network traffic bytes sent
     */
    public long getSentBytes() {
-      return sentBytes;
+      return this.sentBytes;
    }
 
    /**
     * @return the total number of snapshots available for this account, or null
-    *         if unlimited
+   if unlimited
     */
    @Nullable
    public Long getSnapshotsAvailable() {
-      return snapshotsAvailable;
+      return this.snapshotsAvailable;
    }
 
    /**
     * @return the total number of snapshots which can be stored by this account,
-    *         or null if unlimited
+   or null if unlimited
     */
    @Nullable
    public Long getSnapshotLimit() {
-      return snapshotLimit;
+      return this.snapshotLimit;
    }
 
    /**
     * @return the total number of snapshots stored by this account
     */
    public long getSnapshots() {
-      return snapshots;
+      return this.snapshots;
    }
 
    /**
     * @return the state of the account
     */
+   @Nullable
    public State getState() {
-      return state;
+      return this.state;
    }
 
    /**
     * @return the total number of templates available to be created by this
-    *         account, or null if unlimited
+   account, or null if unlimited
     */
    @Nullable
    public Long getTemplatesAvailable() {
-      return templatesAvailable;
+      return this.templatesAvailable;
    }
 
    /**
     * @return the total number of templates which can be created by this
-    *         account, or null if unlimited
+   account, or null if unlimited
     */
    @Nullable
    public Long getTemplateLimit() {
-      return templateLimit;
+      return this.templateLimit;
    }
 
    /**
     * @return the total number of templates which have been created by this
-    *         account
+   account
     */
    public long getTemplates() {
-      return templates;
+      return this.templates;
    }
 
    /**
     * @return the total number of virtual machines available for this account to
-    *         acquire, or null if unlimited
+   acquire, or null if unlimited
     */
    @Nullable
    public Long getVMsAvailable() {
-      return VMsAvailable;
+      return this.VMsAvailable;
    }
 
    /**
     * @return the total number of virtual machines that can be deployed by this
-    *         account, or null if unlimited
+   account, or null if unlimited
     */
    @Nullable
    public Long getVMLimit() {
-      return VMLimit;
+      return this.VMLimit;
    }
 
    /**
     * @return the total number of virtual machines running for this account
     */
    public long getVMsRunning() {
-      return VMsRunning;
+      return this.VMsRunning;
    }
 
    /**
     * @return the total number of virtual machines stopped for this account
     */
    public long getVMsStopped() {
-      return VMsStopped;
+      return this.VMsStopped;
    }
 
    /**
     * @return the total number of virtual machines deployed by this account
     */
    public long getVMs() {
-      return VMs;
+      return this.VMs;
    }
 
    /**
@@ -577,88 +709,85 @@
     */
    @Nullable
    public Long getVolumesAvailable() {
-      return volumesAvailable;
+      return this.volumesAvailable;
    }
 
    /**
     * @return the total volume which can be used by this account, or null if
-    *         unlimited
+   unlimited
     */
    @Nullable
    public Long getVolumeLimit() {
-      return volumeLimit;
+      return this.volumeLimit;
    }
 
    /**
     * @return the total volume being used by this account
     */
    public long getVolumes() {
-      return volumes;
+      return this.volumes;
    }
 
-   @Override
-   public int compareTo(Account arg0) {
-      return id.compareTo(arg0.getId());
-   }
-
-
-   @Override
-   public String toString() {
-      return "Account{" +
-            "id=" + id +
-            ", type=" + type +
-            ", networkDomain='" + networkDomain + '\'' +
-            ", domain='" + domain + '\'' +
-            ", domainId=" + domainId +
-            ", IPsAvailable=" + IPsAvailable +
-            ", IPLimit=" + IPLimit +
-            ", IPs=" + IPs +
-            ", cleanupRequired=" + cleanupRequired +
-            ", name='" + name + '\'' +
-            ", receivedBytes=" + receivedBytes +
-            ", sentBytes=" + sentBytes +
-            ", snapshotsAvailable=" + snapshotsAvailable +
-            ", snapshotLimit=" + snapshotLimit +
-            ", snapshots=" + snapshots +
-            ", state=" + state +
-            ", templatesAvailable=" + templatesAvailable +
-            ", templateLimit=" + templateLimit +
-            ", templates=" + templates +
-            ", VMsAvailable=" + VMsAvailable +
-            ", VMLimit=" + VMLimit +
-            ", VMsRunning=" + VMsRunning +
-            ", VMsStopped=" + VMsStopped +
-            ", VMs=" + VMs +
-            ", volumesAvailable=" + volumesAvailable +
-            ", volumeLimit=" + volumeLimit +
-            ", volumes=" + volumes +
-            ", users=" + users +
-            '}';
-   }
-
-   @Override
-   public int hashCode() {
-       return Objects.hashCode(domainId, id, name);
-   }
-
-   @Override
-   public boolean equals(Object obj) {
-      if (this == obj)
-         return true;
-      if (obj == null)
-         return false;
-      if (getClass() != obj.getClass())
-         return false;
-      Account that = (Account) obj;
-      if (!Objects.equal(domainId, that.domainId)) return false;
-      if (!Objects.equal(id, that.id)) return false;
-      if (!Objects.equal(name, that.name)) return false;
-
-      return true;
+   /**
+    * @return the list of users associated with account
+    */
+   public Set<User> getUsers() {
+      return this.users;
    }
 
    @Override
    protected Set<User> delegate() {
-      return users;
+      return this.users;
    }
+
+   @Override
+   public int hashCode() {
+      return Objects.hashCode(id, type, networkDomain, domain, domainId, IPsAvailable, IPLimit, IPs, cleanupRequired, name, receivedBytes, sentBytes, snapshotsAvailable, snapshotLimit, snapshots, state, templatesAvailable, templateLimit, templates, VMsAvailable, VMLimit, VMsRunning, VMsStopped, VMs, volumesAvailable, volumeLimit, volumes, users);
+   }
+
+   @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      Account that = Account.class.cast(obj);
+      return Objects.equal(this.id, that.id)
+            && Objects.equal(this.type, that.type)
+            && Objects.equal(this.networkDomain, that.networkDomain)
+            && Objects.equal(this.domain, that.domain)
+            && Objects.equal(this.domainId, that.domainId)
+            && Objects.equal(this.IPsAvailable, that.IPsAvailable)
+            && Objects.equal(this.IPLimit, that.IPLimit)
+            && Objects.equal(this.IPs, that.IPs)
+            && Objects.equal(this.cleanupRequired, that.cleanupRequired)
+            && Objects.equal(this.name, that.name)
+            && Objects.equal(this.receivedBytes, that.receivedBytes)
+            && Objects.equal(this.sentBytes, that.sentBytes)
+            && Objects.equal(this.snapshotsAvailable, that.snapshotsAvailable)
+            && Objects.equal(this.snapshotLimit, that.snapshotLimit)
+            && Objects.equal(this.snapshots, that.snapshots)
+            && Objects.equal(this.state, that.state)
+            && Objects.equal(this.templatesAvailable, that.templatesAvailable)
+            && Objects.equal(this.templateLimit, that.templateLimit)
+            && Objects.equal(this.templates, that.templates)
+            && Objects.equal(this.VMsAvailable, that.VMsAvailable)
+            && Objects.equal(this.VMLimit, that.VMLimit)
+            && Objects.equal(this.VMsRunning, that.VMsRunning)
+            && Objects.equal(this.VMsStopped, that.VMsStopped)
+            && Objects.equal(this.VMs, that.VMs)
+            && Objects.equal(this.volumesAvailable, that.volumesAvailable)
+            && Objects.equal(this.volumeLimit, that.volumeLimit)
+            && Objects.equal(this.volumes, that.volumes)
+            && Objects.equal(this.users, that.users);
+   }
+
+   protected ToStringHelper string() {
+      return Objects.toStringHelper(this)
+            .add("id", id).add("type", type).add("networkDomain", networkDomain).add("domain", domain).add("domainId", domainId).add("IPsAvailable", IPsAvailable).add("IPLimit", IPLimit).add("IPs", IPs).add("cleanupRequired", cleanupRequired).add("name", name).add("receivedBytes", receivedBytes).add("sentBytes", sentBytes).add("snapshotsAvailable", snapshotsAvailable).add("snapshotLimit", snapshotLimit).add("snapshots", snapshots).add("state", state).add("templatesAvailable", templatesAvailable).add("templateLimit", templateLimit).add("templates", templates).add("VMsAvailable", VMsAvailable).add("VMLimit", VMLimit).add("VMsRunning", VMsRunning).add("VMsStopped", VMsStopped).add("VMs", VMs).add("volumesAvailable", volumesAvailable).add("volumeLimit", volumeLimit).add("volumes", volumes).add("users", users);
+   }
+
+   @Override
+   public String toString() {
+      return string().toString();
+   }
+
 }
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Alert.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Alert.java
index 88ee6bb..c28d0e0 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Alert.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Alert.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -18,117 +18,149 @@
  */
 package org.jclouds.cloudstack.domain;
 
-import com.google.common.base.Objects;
+import static com.google.common.base.Preconditions.checkNotNull;
 
+import java.beans.ConstructorProperties;
 import java.util.Date;
 
+import org.jclouds.javax.annotation.Nullable;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
+
 /**
  * Represents an alert issued by Cloudstack
  *
  * @author Richard Downer
  */
-public class Alert implements Comparable<Alert> {
+public class Alert {
 
-   public static Builder builder() {
-      return new Builder();
+   public static Builder<?> builder() {
+      return new ConcreteBuilder();
    }
 
-   public static class Builder {
-      private String id;
-      private String description;
-      private Date sent;
-      private String type;
+   public Builder<?> toBuilder() {
+      return new ConcreteBuilder().fromAlert(this);
+   }
 
-      public Builder id(String id) {
+   public static abstract class Builder<T extends Builder<T>>  {
+      protected abstract T self();
+
+      protected String id;
+      protected String description;
+      protected Date sent;
+      protected String type;
+
+      /**
+       * @see Alert#getId()
+       */
+      public T id(String id) {
          this.id = id;
-         return this;
+         return self();
       }
 
-      public Builder description(String description) {
+      /**
+       * @see Alert#getDescription()
+       */
+      public T description(String description) {
          this.description = description;
-         return this;
+         return self();
       }
 
-      public Builder sent(Date sent) {
+      /**
+       * @see Alert#getSent()
+       */
+      public T sent(Date sent) {
          this.sent = sent;
-         return this;
+         return self();
       }
 
-      public Builder type(String type) {
+      /**
+       * @see Alert#getType()
+       */
+      public T type(String type) {
          this.type = type;
-         return this;
+         return self();
       }
 
       public Alert build() {
          return new Alert(id, description, sent, type);
       }
+
+      public T fromAlert(Alert in) {
+         return this
+               .id(in.getId())
+               .description(in.getDescription())
+               .sent(in.getSent())
+               .type(in.getType());
+      }
    }
 
-   private String id;
-   private String description;
-   private Date sent;
-   private String type;
-
-   /* exists for the deserializer, only */
-   Alert() {
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
    }
 
-   private Alert(String id, String description, Date sent, String type) {
-      this.id = id;
+   private final String id;
+   private final String description;
+   private final Date sent;
+   private final String type;
+
+   @ConstructorProperties({
+         "id", "description", "sent", "type"
+   })
+   protected Alert(String id, @Nullable String description, @Nullable Date sent, @Nullable String type) {
+      this.id = checkNotNull(id, "id");
       this.description = description;
       this.sent = sent;
       this.type = type;
    }
 
    public String getId() {
-      return id;
+      return this.id;
    }
 
+   @Nullable
    public String getDescription() {
-      return description;
+      return this.description;
    }
 
+   @Nullable
    public Date getSent() {
-      return sent;
+      return this.sent;
    }
 
+   @Nullable
    public String getType() {
-      return type;
-   }
-
-   @Override
-   public boolean equals(Object o) {
-      if (this == o) return true;
-      if (o == null || getClass() != o.getClass()) return false;
-
-      Alert that = (Alert) o;
-
-      if (!Objects.equal(id, that.id)) return false;
-      if (!Objects.equal(description, that.description)) return false;
-      if (!Objects.equal(sent, that.sent)) return false;
-      if (!Objects.equal(type, that.type)) return false;
-
-      return true;
+      return this.type;
    }
 
    @Override
    public int hashCode() {
-       return Objects.hashCode(id, description, sent, type);
+      return Objects.hashCode(id, description, sent, type);
    }
 
+   @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      Alert that = Alert.class.cast(obj);
+      return Objects.equal(this.id, that.id)
+            && Objects.equal(this.description, that.description)
+            && Objects.equal(this.sent, that.sent)
+            && Objects.equal(this.type, that.type);
+   }
+
+   protected ToStringHelper string() {
+      return Objects.toStringHelper(this)
+            .add("id", id).add("description", description).add("sent", sent).add("type", type);
+   }
 
    @Override
    public String toString() {
-      return "Alert{" +
-            "id=" + id +
-            ", description='" + description + '\'' +
-            ", sent=" + sent +
-            ", type='" + type + '\'' +
-            '}';
+      return string().toString();
    }
 
-   @Override
-   public int compareTo(Alert other) {
-      return this.getId().compareTo(other.getId());
-   }
 }
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/ApiKeyPair.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/ApiKeyPair.java
index 4851c13..659e4f8 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/ApiKeyPair.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/ApiKeyPair.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -16,94 +16,117 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
 package org.jclouds.cloudstack.domain;
 
+import java.beans.ConstructorProperties;
+
+import javax.inject.Named;
+
+import org.jclouds.javax.annotation.Nullable;
+
 import com.google.common.base.Objects;
-import com.google.gson.annotations.SerializedName;
+import com.google.common.base.Objects.ToStringHelper;
 
 /**
  * Representation of the API keypair response
  *
  * @author Andrei Savu
  */
-public class ApiKeyPair implements Comparable<ApiKeyPair> {
+public class ApiKeyPair {
 
-   public static Builder builder() {
-      return new Builder();
+   public static Builder<?> builder() {
+      return new ConcreteBuilder();
    }
 
-   public static class Builder {
+   public Builder<?> toBuilder() {
+      return new ConcreteBuilder().fromApiKeyPair(this);
+   }
 
-      private String apiKey;
-      private String secretKey;
+   public static abstract class Builder<T extends Builder<T>>  {
+      protected abstract T self();
 
-      public Builder apiKey(String apiKey) {
+      protected String apiKey;
+      protected String secretKey;
+
+      /**
+       * @see ApiKeyPair#getApiKey()
+       */
+      public T apiKey(String apiKey) {
          this.apiKey = apiKey;
-         return this;
+         return self();
       }
 
-      public Builder secretKey(String secretKey) {
+      /**
+       * @see ApiKeyPair#getSecretKey()
+       */
+      public T secretKey(String secretKey) {
          this.secretKey = secretKey;
-         return this;
+         return self();
       }
 
       public ApiKeyPair build() {
          return new ApiKeyPair(apiKey, secretKey);
       }
+
+      public T fromApiKeyPair(ApiKeyPair in) {
+         return this
+               .apiKey(in.getApiKey())
+               .secretKey(in.getSecretKey());
+      }
    }
 
-   // for deserialization
-   ApiKeyPair() {
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
    }
 
-   @SerializedName("apikey")
-   private String apiKey;
-   @SerializedName("secretkey")
-   private String secretKey;
+   @Named("apikey")
+   private final String apiKey;
+   @Named("secretkey")
+   private final String secretKey;
 
-   public ApiKeyPair(String apiKey, String secretKey) {
+   @ConstructorProperties({
+         "apikey", "secretkey"
+   })
+   protected ApiKeyPair(@Nullable String apiKey, @Nullable String secretKey) {
       this.apiKey = apiKey;
       this.secretKey = secretKey;
    }
 
-   public String getSecretKey() {
-      return secretKey;
-   }
-
+   @Nullable
    public String getApiKey() {
-      return apiKey;
+      return this.apiKey;
    }
 
-   @Override
-   public boolean equals(Object o) {
-      if (this == o) return true;
-      if (o == null || getClass() != o.getClass()) return false;
-
-      ApiKeyPair that = (ApiKeyPair) o;
-
-      if (!Objects.equal(apiKey, that.apiKey)) return false;
-      if (!Objects.equal(secretKey, that.secretKey)) return false;
-
-      return true;
+   @Nullable
+   public String getSecretKey() {
+      return this.secretKey;
    }
 
    @Override
    public int hashCode() {
-       return Objects.hashCode(apiKey, secretKey);
+      return Objects.hashCode(apiKey, secretKey);
+   }
+
+   @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      ApiKeyPair that = ApiKeyPair.class.cast(obj);
+      return Objects.equal(this.apiKey, that.apiKey)
+            && Objects.equal(this.secretKey, that.secretKey);
+   }
+
+   protected ToStringHelper string() {
+      return Objects.toStringHelper(this)
+            .add("apiKey", apiKey).add("secretKey", secretKey);
    }
 
    @Override
    public String toString() {
-      return "ApiKeyPair{" +
-         "apiKey='" + apiKey + '\'' +
-         ", secretKey='" + secretKey + '\'' +
-         '}';
-   }
-
-   @Override
-   public int compareTo(ApiKeyPair arg0) {
-      return apiKey.compareTo(arg0.getApiKey());
+      return string().toString();
    }
 
 }
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/AsyncCreateResponse.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/AsyncCreateResponse.java
index 3c2ed00..613adfc 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/AsyncCreateResponse.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/AsyncCreateResponse.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -18,29 +18,79 @@
  */
 package org.jclouds.cloudstack.domain;
 
+import java.beans.ConstructorProperties;
+
+import javax.inject.Named;
+
+import org.jclouds.javax.annotation.Nullable;
+
 import com.google.common.base.Objects;
-import com.google.gson.annotations.SerializedName;
+import com.google.common.base.Objects.ToStringHelper;
 
 /**
- * 
+ * Class AsyncCreateResponse
+ *
  * @author Adrian Cole
  */
 public class AsyncCreateResponse {
-   public static final AsyncCreateResponse UNINITIALIZED = new AsyncCreateResponse();
-   
-   private String id;
-   @SerializedName("jobid")
-   private String jobId;
+   public static final AsyncCreateResponse UNINITIALIZED = new AsyncCreateResponse(null, null);
 
-   /**
-    * present only for serializer
-    * 
-    */
-   AsyncCreateResponse() {
-
+   public static Builder<?> builder() {
+      return new ConcreteBuilder();
    }
 
-   public AsyncCreateResponse(String id, String jobId) {
+   public Builder<?> toBuilder() {
+      return new ConcreteBuilder().fromAsyncCreateResponse(this);
+   }
+
+   public static abstract class Builder<T extends Builder<T>>  {
+      protected abstract T self();
+
+      protected String id;
+      protected String jobId;
+
+      /**
+       * @see AsyncCreateResponse#getId()
+       */
+      public T id(String id) {
+         this.id = id;
+         return self();
+      }
+
+      /**
+       * @see AsyncCreateResponse#getJobId()
+       */
+      public T jobId(String jobId) {
+         this.jobId = jobId;
+         return self();
+      }
+
+      public AsyncCreateResponse build() {
+         return new AsyncCreateResponse(id, jobId);
+      }
+
+      public T fromAsyncCreateResponse(AsyncCreateResponse in) {
+         return this
+               .id(in.getId())
+               .jobId(in.getJobId());
+      }
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
+   }
+
+   private final String id;
+   @Named("jobid")
+   private final String jobId;
+
+   @ConstructorProperties({
+         "id", "jobid"
+   })
+   protected AsyncCreateResponse(@Nullable String id, @Nullable String jobId) {
       this.id = id;
       this.jobId = jobId;
    }
@@ -48,44 +98,41 @@
    /**
     * @return id of the resource being created
     */
+   @Nullable
    public String getId() {
-      return id;
+      return this.id;
    }
 
    /**
     * @return id of the job in progress
     */
+   @Nullable
    public String getJobId() {
-      return jobId;
+      return this.jobId;
    }
 
    @Override
    public int hashCode() {
-       return Objects.hashCode(id, jobId);
+      return Objects.hashCode(id, jobId);
    }
 
    @Override
    public boolean equals(Object obj) {
-      if (this == obj)
-         return true;
-      if (obj == null)
-         return false;
-      if (getClass() != obj.getClass())
-         return false;
-      AsyncCreateResponse that = (AsyncCreateResponse) obj;
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      AsyncCreateResponse that = AsyncCreateResponse.class.cast(obj);
+      return Objects.equal(this.id, that.id)
+            && Objects.equal(this.jobId, that.jobId);
+   }
 
-      if (!Objects.equal(id, that.id)) return false;
-      if (!Objects.equal(jobId, that.jobId)) return false;
-
-      return true;
+   protected ToStringHelper string() {
+      return Objects.toStringHelper(this)
+            .add("id", id).add("jobId", jobId);
    }
 
    @Override
    public String toString() {
-      return "AsyncCreateResponse{" +
-            "id=" + id +
-            ", jobId=" + jobId +
-            '}';
+      return string().toString();
    }
 
 }
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/AsyncJob.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/AsyncJob.java
index 93551fc..0ecb1e9 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/AsyncJob.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/AsyncJob.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -18,16 +18,24 @@
  */
 package org.jclouds.cloudstack.domain;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.beans.ConstructorProperties;
 import java.util.Date;
 
+import javax.inject.Named;
+
+import org.jclouds.javax.annotation.Nullable;
+
 import com.google.common.base.Objects;
-import com.google.gson.annotations.SerializedName;
+import com.google.common.base.Objects.ToStringHelper;
 
 /**
- * 
+ * Class AsyncJob
+ *
  * @author Adrian Cole
  */
-public class AsyncJob<T> {
+public class AsyncJob<S> {
 
    /**
     * Valid job result codes
@@ -98,132 +106,205 @@
       }
    }
 
-   public static <T> Builder<T> builder() {
-      return new Builder<T>();
+   public static <T> Builder<?,T> builder() {
+      return new ConcreteBuilder<T>();
    }
 
-   public static class Builder<T> {
-      private String accountId;
-      private String cmd;
-      private Date created;
-      private String id;
-      private String instanceId;
-      private String instanceType;
-      private int progress;
-      private T result;
-      private ResultCode resultCode = ResultCode.UNKNOWN;
-      private String resultType;
-      private AsyncJobError error;
-      private Status status = Status.UNKNOWN;
-      private String userId;
+   public Builder toBuilder() {
+      return new ConcreteBuilder<S>().fromAsyncJob(this);
+   }
 
-      public Builder<T> accountId(String accountId) {
+   public static abstract class Builder<T extends Builder<T,S>, S>  {
+      protected abstract T self();
+
+      protected String accountId;
+      protected String cmd;
+      protected Date created;
+      protected String id;
+      protected String instanceId;
+      protected String instanceType;
+      protected int progress;
+      protected S result;
+      protected AsyncJob.ResultCode resultCode;
+      protected String resultType;
+      protected AsyncJob.Status status;
+      protected String userId;
+      protected AsyncJobError error;
+
+      /**
+       * @see AsyncJob#getAccountId()
+       */
+      public T accountId(String accountId) {
          this.accountId = accountId;
-         return this;
+         return self();
       }
 
-      public Builder<T> cmd(String cmd) {
+      /**
+       * @see AsyncJob#getCmd()
+       */
+      public T cmd(String cmd) {
          this.cmd = cmd;
-         return this;
+         return self();
       }
 
-      public Builder<T> created(Date created) {
+      /**
+       * @see AsyncJob#getCreated()
+       */
+      public T created(Date created) {
          this.created = created;
-         return this;
+         return self();
       }
 
-      public Builder<T> id(String id) {
+      /**
+       * @see AsyncJob#getId()
+       */
+      public T id(String id) {
          this.id = id;
-         return this;
+         return self();
       }
 
-      public Builder<T> instanceId(String instanceId) {
+      /**
+       * @see AsyncJob#getInstanceId()
+       */
+      public T instanceId(String instanceId) {
          this.instanceId = instanceId;
-         return this;
+         return self();
       }
 
-      public Builder<T> error(AsyncJobError error) {
-         this.error = error;
-         return this;
-      }
-
-      public Builder<T> instanceType(String instanceType) {
+      /**
+       * @see AsyncJob#getInstanceType()
+       */
+      public T instanceType(String instanceType) {
          this.instanceType = instanceType;
-         return this;
+         return self();
       }
 
-      public Builder<T> progress(int progress) {
+      /**
+       * @see AsyncJob#getProgress()
+       */
+      public T progress(int progress) {
          this.progress = progress;
-         return this;
+         return self();
       }
 
-      public Builder<T> result(T result) {
+      /**
+       * @see AsyncJob#getResult()
+       */
+      public T result(S result) {
          this.result = result;
-         return this;
+         return self();
       }
 
-      public Builder<T> resultCode(ResultCode resultCode) {
+      /**
+       * @see AsyncJob#getResultCode()
+       */
+      public T resultCode(AsyncJob.ResultCode resultCode) {
          this.resultCode = resultCode;
-         return this;
+         return self();
       }
 
-      public Builder<T> resultType(String resultType) {
+      /**
+       * @see AsyncJob#getResultType()
+       */
+      public T resultType(String resultType) {
          this.resultType = resultType;
-         return this;
+         return self();
       }
 
-      public Builder<T> status(Status status) {
+      /**
+       * @see AsyncJob#getStatus()
+       */
+      public T status(AsyncJob.Status status) {
          this.status = status;
-         return this;
+         return self();
       }
 
-      public Builder<T> userId(String userId) {
+      /**
+       * @see AsyncJob#getUserId()
+       */
+      public T userId(String userId) {
          this.userId = userId;
-         return this;
+         return self();
       }
 
-      public AsyncJob<T> build() {
-         return new AsyncJob<T>(accountId, cmd, created, id, instanceId, instanceType, progress, result, resultCode,
+      /**
+       * @see AsyncJob#getError()
+       */
+      public T error(AsyncJobError error) {
+         this.error = error;
+         return self();
+      }
+
+      public AsyncJob build() {
+         return new AsyncJob<S>(accountId, cmd, created, id, instanceId, instanceType, progress, result, resultCode,
                resultType, status, userId, error);
       }
 
-      public static <T> Builder<T> fromAsyncJobUntyped(AsyncJob<T> in) {
-         return new Builder<T>().accountId(in.accountId).cmd(in.cmd).created(in.created).id(in.id)
-               .instanceId(in.instanceId).instanceType(in.instanceType).progress(in.progress).result(in.result)
-               .resultCode(in.resultCode).resultType(in.resultType).status(in.status).userId(in.userId).error(in.error);
+      public T fromAsyncJob(AsyncJob<S> in) {
+         return this
+               .accountId(in.getAccountId())
+               .cmd(in.getCmd())
+               .created(in.getCreated())
+               .id(in.getId())
+               .instanceId(in.getInstanceId())
+               .instanceType(in.getInstanceType())
+               .progress(in.getProgress())
+               .result(in.getResult())
+               .resultCode(in.getResultCode())
+               .resultType(in.getResultType())
+               .status(in.getStatus())
+               .userId(in.getUserId())
+               .error(in.getError());
+      }
+
+      public static Builder<?, Object> fromAsyncJobUntyped(AsyncJob in) {
+         return new ConcreteBuilder().fromAsyncJob(in);
       }
    }
 
-   @SerializedName("accountid")
-   private String accountId;
-   private String cmd;
-   private Date created;
-   @SerializedName("jobid")
-   private String id;
-   @SerializedName("jobinstanceid")
-   private String instanceId;
-   @SerializedName("jobinstancetype")
-   private String instanceType;
-   @SerializedName("jobprocstatus")
-   private int progress;
-   @SerializedName("jobresult")
-   private T result;
-   @SerializedName("jobresultcode")
-   private ResultCode resultCode = ResultCode.UNKNOWN;
-   @SerializedName("jobresulttype")
-   private String resultType;
-   @SerializedName("jobstatus")
-   private Status status = Status.UNKNOWN;
-   @SerializedName("userid")
-   private String userId;
-   private AsyncJobError error;
+   private static class ConcreteBuilder<T> extends Builder<ConcreteBuilder<T>,T> {
+      @Override
+      protected ConcreteBuilder<T> self() {
+         return this;
+      }
+   }
 
-   public AsyncJob(String accountId, String cmd, Date created, String id, String instanceId, String instanceType,
-         int progress, T result, ResultCode resultCode, String resultType, Status status, String userId, AsyncJobError error) {
+   @Named("accountid")
+   private final String accountId;
+   private final String cmd;
+   private final Date created;
+   @Named("jobid")
+   private final String id;
+   @Named("jobinstanceid")
+   private final String instanceId;
+   @Named("jobinstancetype")
+   private final String instanceType;
+   @Named("jobprocstatus")
+   private final int progress;
+   @Named("jobresult")
+   private final S result;
+   @Named("jobresultcode")
+   private final AsyncJob.ResultCode resultCode;
+   @Named("jobresulttype")
+   private final String resultType;
+   @Named("jobstatus")
+   private final AsyncJob.Status status;
+   @Named("userid")
+   private final String userId;
+   private final AsyncJobError error;
+
+   @ConstructorProperties({
+         "accountid", "cmd", "created", "jobid", "jobinstanceid", "jobinstancetype", "jobprocstatus", "jobresult",
+         "jobresultcode", "jobresulttype", "jobstatus", "userid", "error"
+   })
+   protected AsyncJob(@Nullable String accountId, @Nullable String cmd, @Nullable Date created, String id,
+                      @Nullable String instanceId, @Nullable String instanceType, int progress, @Nullable S result,
+                      @Nullable AsyncJob.ResultCode resultCode, @Nullable String resultType, @Nullable AsyncJob.Status status,
+                      @Nullable String userId, @Nullable AsyncJobError error) {
       this.accountId = accountId;
       this.cmd = cmd;
       this.created = created;
-      this.id = id;
+      this.id = checkNotNull(id, "id");
       this.instanceId = instanceId;
       this.instanceType = instanceType;
       this.progress = progress;
@@ -236,105 +317,106 @@
    }
 
    /**
-    * present only for serializer
-    * 
-    */
-   AsyncJob() {
-
-   }
-
-   /**
     * @return the account that executed the async command
     */
+   @Nullable
    public String getAccountId() {
-      return accountId;
+      return this.accountId;
    }
 
    /**
     * @return the async command executed
     */
+   @Nullable
    public String getCmd() {
-      return cmd;
+      return this.cmd;
    }
 
    /**
     * @return the created date of the job
     */
+   @Nullable
    public Date getCreated() {
-      return created;
+      return this.created;
    }
 
    /**
     * @return async job ID
     */
    public String getId() {
-      return id;
+      return this.id;
    }
 
    /**
     * @return the unique ID of the instance/entity object related to the job
     */
+   @Nullable
    public String getInstanceId() {
-      return instanceId;
+      return this.instanceId;
    }
 
    /**
     * @return the instance/entity object related to the job
     */
+   @Nullable
    public String getInstanceType() {
-      return instanceType;
+      return this.instanceType;
    }
 
    /**
     * @return the progress information of the PENDING job
     */
    public int getProgress() {
-      return progress;
+      return this.progress;
    }
 
    /**
     * @return the result reason
     */
-   public T getResult() {
-      return result;
+   @Nullable
+   public S getResult() {
+      return this.result;
    }
 
    /**
     * @return the result code for the job
     */
-   public ResultCode getResultCode() {
-      return resultCode;
+   @Nullable
+   public AsyncJob.ResultCode getResultCode() {
+      return this.resultCode;
    }
 
    /**
     * @return the result type
     */
+   @Nullable
    public String getResultType() {
-      return resultType;
+      return this.resultType;
    }
 
    /**
     * @return the current job status-should be 0 for PENDING
     */
-   public Status getStatus() {
-      return status;
+   @Nullable
+   public AsyncJob.Status getStatus() {
+      return this.status;
    }
 
    /**
     * @return the user that executed the async command
     */
+   @Nullable
    public String getUserId() {
-      return userId;
+      return this.userId;
    }
 
    /**
-    * 
-    * 
     * @return the error related to this command, or null if no error or error
-    *         not yet encountered.
+   not yet encountered.
     */
+   @Nullable
    public AsyncJobError getError() {
-      return error;
+      return this.error;
    }
 
    public boolean hasFailed() {
@@ -347,55 +429,39 @@
 
    @Override
    public int hashCode() {
-       return Objects.hashCode(accountId, cmd, created, id, instanceId, instanceType, error, progress,
-                               result, resultCode, resultType, status, userId);
+      return Objects.hashCode(accountId, cmd, created, id, instanceId, instanceType, progress, result, resultCode, resultType, status, userId, error);
    }
 
    @Override
    public boolean equals(Object obj) {
-      if (this == obj)
-         return true;
-      if (obj == null)
-         return false;
-      if (getClass() != obj.getClass())
-         return false;
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      AsyncJob that = AsyncJob.class.cast(obj);
+      return Objects.equal(this.accountId, that.accountId)
+            && Objects.equal(this.cmd, that.cmd)
+            && Objects.equal(this.created, that.created)
+            && Objects.equal(this.id, that.id)
+            && Objects.equal(this.instanceId, that.instanceId)
+            && Objects.equal(this.instanceType, that.instanceType)
+            && Objects.equal(this.progress, that.progress)
+            && Objects.equal(this.result, that.result)
+            && Objects.equal(this.resultCode, that.resultCode)
+            && Objects.equal(this.resultType, that.resultType)
+            && Objects.equal(this.status, that.status)
+            && Objects.equal(this.userId, that.userId)
+            && Objects.equal(this.error, that.error);
+   }
 
-      AsyncJob<?> that = (AsyncJob<?>) obj;
-
-      if (!Objects.equal(accountId, that.accountId)) return false;
-      if (!Objects.equal(cmd, that.cmd)) return false;
-      if (!Objects.equal(created, that.created)) return false;
-      if (!Objects.equal(id, that.id)) return false;
-      if (!Objects.equal(instanceId, that.instanceId)) return false;
-      if (!Objects.equal(instanceType, that.instanceType)) return false;
-      if (!Objects.equal(error, that.error)) return false;
-      if (!Objects.equal(progress, that.progress)) return false;
-      if (!Objects.equal(result, that.result)) return false;
-      if (!Objects.equal(resultCode, that.resultCode)) return false;
-      if (!Objects.equal(resultType, that.resultType)) return false;
-      if (!Objects.equal(status, that.status)) return false;
-      if (!Objects.equal(userId, that.userId)) return false;
-
-      return true;
+   protected ToStringHelper string() {
+      return Objects.toStringHelper(this)
+            .add("accountId", accountId).add("cmd", cmd).add("created", created).add("id", id).add("instanceId", instanceId)
+            .add("instanceType", instanceType).add("progress", progress).add("result", result).add("resultCode", resultCode)
+            .add("resultType", resultType).add("status", status).add("userId", userId).add("error", error);
    }
 
    @Override
    public String toString() {
-      return "AsyncJob{" +
-            "accountId=" + accountId +
-            ", cmd='" + cmd + '\'' +
-            ", created=" + created +
-            ", id=" + id +
-            ", instanceId=" + instanceId +
-            ", instanceType='" + instanceType + '\'' +
-            ", progress=" + progress +
-            ", result=" + result +
-            ", resultCode=" + resultCode +
-            ", resultType='" + resultType + '\'' +
-            ", status=" + status +
-            ", userId=" + userId +
-            ", error=" + error +
-            '}';
+      return string().toString();
    }
 
 }
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/AsyncJobError.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/AsyncJobError.java
index f57fbd5..5e9b14a 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/AsyncJobError.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/AsyncJobError.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -18,8 +18,14 @@
  */
 package org.jclouds.cloudstack.domain;
 
+import java.beans.ConstructorProperties;
+
+import javax.inject.Named;
+
+import org.jclouds.javax.annotation.Nullable;
+
 import com.google.common.base.Objects;
-import com.google.gson.annotations.SerializedName;
+import com.google.common.base.Objects.ToStringHelper;
 
 /**
  * 
@@ -65,59 +71,99 @@
       }
    }
 
-   @SerializedName("errorcode")
-   private ErrorCode errorCode;
-   @SerializedName("errortext")
-   private String errorText;
-
-   /**
-    * present only for serializer
-    * 
-    */
-   AsyncJobError() {
-
+   public static Builder<?> builder() {
+      return new ConcreteBuilder();
    }
 
-   public AsyncJobError(ErrorCode errorCode, String errorText) {
+   public Builder<?> toBuilder() {
+      return new ConcreteBuilder().fromAsyncJobError(this);
+   }
+
+   public static abstract class Builder<T extends Builder<T>>  {
+      protected abstract T self();
+
+      protected AsyncJobError.ErrorCode errorCode;
+      protected String errorText;
+
+      /**
+       * @see AsyncJobError#getErrorCode()
+       */
+      public T errorCode(ErrorCode errorCode) {
+         this.errorCode = errorCode;
+         return self();
+      }
+
+      /**
+       * @see AsyncJobError#getErrorText()
+       */
+      public T errorText(String errorText) {
+         this.errorText = errorText;
+         return self();
+      }
+
+      public AsyncJobError build() {
+         return new AsyncJobError(errorCode, errorText);
+      }
+
+      public T fromAsyncJobError(AsyncJobError in) {
+         return this
+               .errorCode(in.getErrorCode())
+               .errorText(in.getErrorText());
+      }
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
+   }
+
+   @Named("errorcode")
+   private final ErrorCode errorCode;
+   @Named("errortext")
+   private final String errorText;
+
+   @ConstructorProperties({
+         "errorcode", "errortext"
+   })
+   protected AsyncJobError(@Nullable ErrorCode errorCode, @Nullable String errorText) {
       this.errorCode = errorCode;
       this.errorText = errorText;
    }
 
+   @Nullable
    public ErrorCode getErrorCode() {
-      return errorCode;
+      return this.errorCode;
    }
 
+   @Nullable
    public String getErrorText() {
-      return errorText;
+      return this.errorText;
    }
 
    @Override
    public int hashCode() {
-       return Objects.hashCode(errorCode, errorText);
+      return Objects.hashCode(errorCode, errorText);
    }
 
    @Override
    public boolean equals(Object obj) {
-      if (this == obj)
-         return true;
-      if (obj == null)
-         return false;
-      if (getClass() != obj.getClass())
-         return false;
-      AsyncJobError that = (AsyncJobError) obj;
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      AsyncJobError that = AsyncJobError.class.cast(obj);
+      return Objects.equal(this.errorCode, that.errorCode)
+            && Objects.equal(this.errorText, that.errorText);
+   }
 
-      if (!Objects.equal(errorCode, that.errorCode)) return false;
-      if (!Objects.equal(errorText, that.errorText)) return false;
-
-      return true;
+   protected ToStringHelper string() {
+      return Objects.toStringHelper(this)
+            .add("errorCode", errorCode).add("errorText", errorText);
    }
 
    @Override
    public String toString() {
-      return "AsyncJobError{" +
-            "errorCode=" + errorCode +
-            ", errorText='" + errorText + '\'' +
-            '}';
+      return string().toString();
    }
 
 }
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Capabilities.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Capabilities.java
index 4d83b8b..ab0a92d 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Capabilities.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Capabilities.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -18,73 +18,114 @@
  */
 package org.jclouds.cloudstack.domain;
 
+import java.beans.ConstructorProperties;
+
+import javax.inject.Named;
+
+import org.jclouds.javax.annotation.Nullable;
+
 import com.google.common.base.Objects;
-import com.google.gson.annotations.SerializedName;
+import com.google.common.base.Objects.ToStringHelper;
 
 /**
+ * Class Capabilities
+ *
  * @author Adrian Cole
  */
 public class Capabilities {
-   public static Builder builder() {
-      return new Builder();
+
+   public static Builder<?> builder() {
+      return new ConcreteBuilder();
    }
 
-   public static class Builder {
-      private String cloudStackVersion;
-      private boolean securityGroupsEnabled;
-      private boolean canShareTemplates;
-      private boolean firewallRuleUiEnabled;
-      private boolean supportELB;
+   public Builder<?> toBuilder() {
+      return new ConcreteBuilder().fromCapabilities(this);
+   }
 
-      public Builder cloudStackVersion(String cloudStackVersion) {
+   public static abstract class Builder<T extends Builder<T>>  {
+      protected abstract T self();
+
+      protected String cloudStackVersion;
+      protected boolean securityGroupsEnabled;
+      protected boolean canShareTemplates;
+      protected boolean firewallRuleUiEnabled;
+      protected boolean supportELB;
+
+      /**
+       * @see Capabilities#getCloudStackVersion()
+       */
+      public T cloudStackVersion(String cloudStackVersion) {
          this.cloudStackVersion = cloudStackVersion;
-         return this;
+         return self();
       }
 
-      public Builder securityGroupsEnabled(boolean securityGroupsEnabled) {
+      /**
+       * @see Capabilities#isSecurityGroupsEnabled()
+       */
+      public T securityGroupsEnabled(boolean securityGroupsEnabled) {
          this.securityGroupsEnabled = securityGroupsEnabled;
-         return this;
+         return self();
       }
 
-      public Builder sharedTemplatesEnabled(boolean canShareTemplates) {
+      /**
+       * @see Capabilities#canShareTemplates()
+       */
+      public T canShareTemplates(boolean canShareTemplates) {
          this.canShareTemplates = canShareTemplates;
-         return this;
+         return self();
       }
 
-      public Builder firewallRuleUiEnabled(boolean firewallRuleUiEnabled) {
+      /**
+       * @see Capabilities#isFirewallRuleUiEnabled()
+       */
+      public T firewallRuleUiEnabled(boolean firewallRuleUiEnabled) {
          this.firewallRuleUiEnabled = firewallRuleUiEnabled;
-         return this;
+         return self();
       }
 
-      public Builder supportELB(boolean supportELB) {
+      /**
+       * @see Capabilities#isSupportELB()
+       */
+      public T supportELB(boolean supportELB) {
          this.supportELB = supportELB;
-         return this;
+         return self();
       }
 
       public Capabilities build() {
          return new Capabilities(cloudStackVersion, securityGroupsEnabled, canShareTemplates, firewallRuleUiEnabled, supportELB);
       }
+
+      public T fromCapabilities(Capabilities in) {
+         return this
+               .cloudStackVersion(in.getCloudStackVersion())
+               .securityGroupsEnabled(in.isSecurityGroupsEnabled())
+               .canShareTemplates(in.canShareTemplates())
+               .firewallRuleUiEnabled(in.isFirewallRuleUiEnabled())
+               .supportELB(in.isSupportELB());
+      }
    }
 
-   @SerializedName("cloudstackversion")
-   private String cloudStackVersion;
-   @SerializedName("securitygroupsenabled")
-   private boolean securityGroupsEnabled;
-   @SerializedName("userpublictemplateenabled")
-   private boolean canShareTemplates;
-   private boolean firewallRuleUiEnabled;
-   private boolean supportELB;
-
-
-   /**
-    * present only for serializer
-    */
-   Capabilities() {
-
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
    }
 
-   public Capabilities(String cloudStackVersion, boolean securityGroupsEnabled, boolean canShareTemplates,
-                       boolean firewallRuleUiEnabled, boolean supportELB) {
+   @Named("cloudstackversion")
+   private final String cloudStackVersion;
+   @Named("securitygroupsenabled")
+   private final boolean securityGroupsEnabled;
+   @Named("userpublictemplateenabled")
+   private final boolean canShareTemplates;
+   private final boolean firewallRuleUiEnabled;
+   private final boolean supportELB;
+
+   @ConstructorProperties({
+         "cloudstackversion", "securitygroupsenabled", "userpublictemplateenabled", "firewallRuleUiEnabled", "supportELB"
+   })
+   protected Capabilities(@Nullable String cloudStackVersion, boolean securityGroupsEnabled, boolean canShareTemplates,
+                          boolean firewallRuleUiEnabled, boolean supportELB) {
       this.cloudStackVersion = cloudStackVersion;
       this.securityGroupsEnabled = securityGroupsEnabled;
       this.canShareTemplates = canShareTemplates;
@@ -95,71 +136,61 @@
    /**
     * @return version of the cloud stack
     */
+   @Nullable
    public String getCloudStackVersion() {
-      return cloudStackVersion;
+      return this.cloudStackVersion;
    }
 
    /**
     * @return true if security groups support is enabled, false otherwise
     */
    public boolean isSecurityGroupsEnabled() {
-      return securityGroupsEnabled;
+      return this.securityGroupsEnabled;
    }
 
-   /**
-    * @return true if user and domain admins can set templates to be shared,
-    *         false otherwise
-    */
-   public boolean isSharedTemplatesEnabled() {
-      return canShareTemplates;
+   public boolean canShareTemplates() {
+      return this.canShareTemplates;
    }
 
    /**
     * @return true if the firewall rule UI is enabled
     */
    public boolean isFirewallRuleUiEnabled() {
-      return firewallRuleUiEnabled;
+      return this.firewallRuleUiEnabled;
    }
 
    /**
     * @return true if region supports elastic load balancer on basic zones
     */
    public boolean isSupportELB() {
-      return supportELB;
+      return this.supportELB;
    }
 
    @Override
    public int hashCode() {
-       return Objects.hashCode(canShareTemplates, cloudStackVersion, securityGroupsEnabled, firewallRuleUiEnabled, supportELB);
+      return Objects.hashCode(cloudStackVersion, securityGroupsEnabled, canShareTemplates, firewallRuleUiEnabled, supportELB);
    }
 
    @Override
    public boolean equals(Object obj) {
-      if (this == obj)
-         return true;
-      if (obj == null)
-         return false;
-      if (getClass() != obj.getClass())
-         return false;
-      Capabilities that = (Capabilities) obj;
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      Capabilities that = Capabilities.class.cast(obj);
+      return Objects.equal(this.cloudStackVersion, that.cloudStackVersion)
+            && Objects.equal(this.securityGroupsEnabled, that.securityGroupsEnabled)
+            && Objects.equal(this.canShareTemplates, that.canShareTemplates)
+            && Objects.equal(this.firewallRuleUiEnabled, that.firewallRuleUiEnabled)
+            && Objects.equal(this.supportELB, that.supportELB);
+   }
 
-      if (!Objects.equal(canShareTemplates, that.canShareTemplates)) return false;
-      if (!Objects.equal(cloudStackVersion, that.cloudStackVersion)) return false;
-      if (!Objects.equal(securityGroupsEnabled, that.securityGroupsEnabled)) return false;
-      if (!Objects.equal(firewallRuleUiEnabled, that.firewallRuleUiEnabled)) return false;
-      if (!Objects.equal(supportELB, that.supportELB)) return false;
-
-      return true;
+   protected ToStringHelper string() {
+      return Objects.toStringHelper(this)
+            .add("cloudStackVersion", cloudStackVersion).add("securityGroupsEnabled", securityGroupsEnabled).add("canShareTemplates", canShareTemplates).add("firewallRuleUiEnabled", firewallRuleUiEnabled).add("supportELB", supportELB);
    }
 
    @Override
    public String toString() {
-      return "Capabilities{" +
-            "cloudStackVersion='" + cloudStackVersion + '\'' +
-            ", securityGroupsEnabled=" + securityGroupsEnabled +
-            ", canShareTemplates=" + canShareTemplates +
-            ", firewallRuleUiEnabled=" + firewallRuleUiEnabled +
-            ", supportELB=" + supportELB +
-            '}';
+      return string().toString();
    }
+
 }
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Capacity.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Capacity.java
index 1f39af8..c2852fb 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Capacity.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Capacity.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -20,13 +20,18 @@
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import java.beans.ConstructorProperties;
 import java.util.Map;
 
+import javax.inject.Named;
+
+import org.jclouds.javax.annotation.Nullable;
+
 import com.google.common.base.Function;
 import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Maps;
-import com.google.gson.annotations.SerializedName;
 
 /**
  * Information about a dimension of the capacity
@@ -35,67 +40,9 @@
  */
 public class Capacity implements Comparable<Capacity> {
 
-   public static Builder builder() {
-      return new Builder();
-   }
-
-   public static class Builder {
-
-      private long capacityTotal;
-      private long capacityUsed;
-      private double percentUsed;
-      private String podId;
-      private String podName;
-      private Type type;
-      private String zoneId;
-      private String zoneName;
-
-      public Builder capacityTotal(long capacityTotal) {
-         this.capacityTotal = capacityTotal;
-         return this;
-      }
-
-      public Builder capacityUsed(long capacityUsed) {
-         this.capacityUsed = capacityUsed;
-         return this;
-      }
-
-      public Builder percentUsed(double percentUsed) {
-         this.percentUsed = percentUsed;
-         return this;
-      }
-
-      public Builder podId(String podId) {
-         this.podId = podId;
-         return this;
-      }
-
-      public Builder podName(String podName) {
-         this.podName = podName;
-         return this;
-      }
-
-      public Builder type(Type type) {
-         this.type = type;
-         return this;
-      }
-
-      public Builder zoneId(String zoneId) {
-         this.zoneId = zoneId;
-         return this;
-      }
-
-      public Builder zoneName(String zoneName) {
-         this.zoneName = zoneName;
-         return this;
-      }
-
-      public Capacity build() {
-         return new Capacity(capacityTotal, capacityUsed, percentUsed, podId, podName, type, zoneId, zoneName);
-      }
-   }
-
-   public enum Type {
+   /**
+    */
+   public static enum Type {
       MEMORY_ALLOCATED_BYTES(0),
       CPU_ALLOCATED_MHZ(1),
       PRIMARY_STORAGE_USED_BYTES(2),
@@ -111,14 +58,14 @@
       private int code;
 
       private static final Map<Integer, Type> INDEX = Maps.uniqueIndex(ImmutableSet.copyOf(Type.values()),
-         new Function<Type, Integer>() {
+            new Function<Type, Integer>() {
 
-            @Override
-            public Integer apply(Type input) {
-               return input.code;
-            }
+               @Override
+               public Integer apply(Type input) {
+                  return input.code;
+               }
 
-         });
+            });
 
       Type(int code) {
          this.code = code;
@@ -135,27 +82,135 @@
       }
    }
 
-   @SerializedName("capacitytotal")
-   private long capacityTotal;
-   @SerializedName("capacityused")
-   private long capacityUsed;
-   @SerializedName("percentused")
-   private double percentUsed;
-   @SerializedName("podid")
-   private String podId;
-   @SerializedName("podname")
-   private String podName;
-   private Capacity.Type type;
-   @SerializedName("zoneid")
-   private String zoneId;
-   @SerializedName("zonename")
-   private String zoneName;
-   
-   /* exists for the deserializer, only */
-   Capacity() {
+   public static Builder<?> builder() {
+      return new ConcreteBuilder();
    }
 
-   public Capacity(long capacityTotal, long capacityUsed, double percentUsed, String podId, String podName, Type type, String zoneId, String zoneName) {
+   public Builder<?> toBuilder() {
+      return new ConcreteBuilder().fromCapacity(this);
+   }
+
+   public static abstract class Builder<T extends Builder<T>>  {
+      protected abstract T self();
+
+      protected long capacityTotal;
+      protected long capacityUsed;
+      protected double percentUsed;
+      protected String podId;
+      protected String podName;
+      protected Capacity.Type type;
+      protected String zoneId;
+      protected String zoneName;
+
+      /**
+       * @see Capacity#getCapacityTotal()
+       */
+      public T capacityTotal(long capacityTotal) {
+         this.capacityTotal = capacityTotal;
+         return self();
+      }
+
+      /**
+       * @see Capacity#getCapacityUsed()
+       */
+      public T capacityUsed(long capacityUsed) {
+         this.capacityUsed = capacityUsed;
+         return self();
+      }
+
+      /**
+       * @see Capacity#getPercentUsed()
+       */
+      public T percentUsed(double percentUsed) {
+         this.percentUsed = percentUsed;
+         return self();
+      }
+
+      /**
+       * @see Capacity#getPodId()
+       */
+      public T podId(String podId) {
+         this.podId = podId;
+         return self();
+      }
+
+      /**
+       * @see Capacity#getPodName()
+       */
+      public T podName(String podName) {
+         this.podName = podName;
+         return self();
+      }
+
+      /**
+       * @see Capacity#getType()
+       */
+      public T type(Capacity.Type type) {
+         this.type = type;
+         return self();
+      }
+
+      /**
+       * @see Capacity#getZoneId()
+       */
+      public T zoneId(String zoneId) {
+         this.zoneId = zoneId;
+         return self();
+      }
+
+      /**
+       * @see Capacity#getZoneName()
+       */
+      public T zoneName(String zoneName) {
+         this.zoneName = zoneName;
+         return self();
+      }
+
+      public Capacity build() {
+         return new Capacity(capacityTotal, capacityUsed, percentUsed, podId, podName, type, zoneId, zoneName);
+      }
+
+      public T fromCapacity(Capacity in) {
+         return this
+               .capacityTotal(in.getCapacityTotal())
+               .capacityUsed(in.getCapacityUsed())
+               .percentUsed(in.getPercentUsed())
+               .podId(in.getPodId())
+               .podName(in.getPodName())
+               .type(in.getType())
+               .zoneId(in.getZoneId())
+               .zoneName(in.getZoneName());
+      }
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
+   }
+
+   @Named("capacitytotal")
+   private final long capacityTotal;
+   @Named("capacityused")
+   private final long capacityUsed;
+   @Named("percentused")
+   private final double percentUsed;
+   @Named("podid")
+   private final String podId;
+   @Named("podname")
+   private final String podName;
+   private final Capacity.Type type;
+   @Named("zoneid")
+   private final String zoneId;
+   @Named("zonename")
+   private final String zoneName;
+
+   @ConstructorProperties({
+         "capacitytotal", "capacityused", "percentused", "podid", "podname", "type", "zoneid", "zonename"
+   })
+   protected Capacity(long capacityTotal, long capacityUsed, double percentUsed, @Nullable String podId,
+                      @Nullable String podName, @Nullable Capacity.Type type, @Nullable String zoneId, @Nullable String zoneName) {
       this.capacityTotal = capacityTotal;
       this.capacityUsed = capacityUsed;
       this.percentUsed = percentUsed;
@@ -167,83 +222,78 @@
    }
 
    public long getCapacityTotal() {
-      return capacityTotal;
+      return this.capacityTotal;
    }
 
    public long getCapacityUsed() {
-      return capacityUsed;
+      return this.capacityUsed;
    }
 
    public double getPercentUsed() {
-      return percentUsed;
+      return this.percentUsed;
    }
 
+   @Nullable
    public String getPodId() {
-      return podId;
+      return this.podId;
    }
 
+   @Nullable
    public String getPodName() {
-      return podName;
+      return this.podName;
    }
 
-   public Type getType() {
-      return type;
+   @Nullable
+   public Capacity.Type getType() {
+      return this.type;
    }
 
+   @Nullable
    public String getZoneId() {
-      return zoneId;
+      return this.zoneId;
    }
 
+   @Nullable
    public String getZoneName() {
-      return zoneName;
-   }
-
-   @Override
-   public boolean equals(Object o) {
-      if (this == o) return true;
-      if (o == null || getClass() != o.getClass()) return false;
-
-      Capacity that = (Capacity) o;
-
-      if (!Objects.equal(capacityTotal, that.capacityTotal)) return false;
-      if (!Objects.equal(capacityUsed, that.capacityUsed)) return false;
-      if (!Objects.equal(percentUsed, that.percentUsed)) return false;
-      if (!Objects.equal(podId, that.podId)) return false;
-      if (!Objects.equal(zoneId, that.zoneId)) return false;
-      if (!Objects.equal(podName, that.podName)) return false;
-      if (!Objects.equal(type, that.type)) return false;
-      if (!Objects.equal(zoneName, that.zoneName)) return false;
-
-      return true;
+      return this.zoneName;
    }
 
    @Override
    public int hashCode() {
-       return Objects.hashCode(capacityTotal, capacityUsed, percentUsed, podId, podName,
-                               type, zoneId, zoneName);
+      return Objects.hashCode(capacityTotal, capacityUsed, percentUsed, podId, podName, type, zoneId, zoneName);
+   }
+
+   @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      Capacity that = Capacity.class.cast(obj);
+      return Objects.equal(this.capacityTotal, that.capacityTotal)
+            && Objects.equal(this.capacityUsed, that.capacityUsed)
+            && Objects.equal(this.percentUsed, that.percentUsed)
+            && Objects.equal(this.podId, that.podId)
+            && Objects.equal(this.podName, that.podName)
+            && Objects.equal(this.type, that.type)
+            && Objects.equal(this.zoneId, that.zoneId)
+            && Objects.equal(this.zoneName, that.zoneName);
+   }
+
+   protected ToStringHelper string() {
+      return Objects.toStringHelper(this)
+            .add("capacityTotal", capacityTotal).add("capacityUsed", capacityUsed).add("percentUsed", percentUsed)
+            .add("podId", podId).add("podName", podName).add("type", type).add("zoneId", zoneId).add("zoneName", zoneName);
    }
 
    @Override
    public String toString() {
-      return "Capacity{" +
-            "capacityTotal=" + capacityTotal +
-            ", capacityUsed=" + capacityUsed +
-            ", percentUsed=" + percentUsed +
-            ", podId=" + podId +
-            ", podName='" + podName + '\'' +
-            ", type=" + type +
-            ", zoneId=" + zoneId +
-            ", zoneName='" + zoneName + '\'' +
-            '}';
+      return string().toString();
    }
 
    @Override
    public int compareTo(Capacity other) {
       int comparison = this.zoneId.compareTo(other.zoneId);
-      if (comparison != 0) return comparison;
-      comparison = this.podId.compareTo(other.podId);
-      if (comparison != 0) return comparison;
-      return Integer.valueOf(this.type.code).compareTo(other.type.code);
+      if (comparison == 0) comparison = this.podId.compareTo(other.podId);
+      if (comparison == 0) Integer.valueOf(this.type.code).compareTo(other.type.code);
+      return comparison;
    }
-
 }
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Cluster.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Cluster.java
index cb167a9..969dce1 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Cluster.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Cluster.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -20,9 +20,16 @@
 
 import static com.google.common.base.CaseFormat.UPPER_CAMEL;
 import static com.google.common.base.CaseFormat.UPPER_UNDERSCORE;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.beans.ConstructorProperties;
+
+import javax.inject.Named;
+
+import org.jclouds.javax.annotation.Nullable;
 
 import com.google.common.base.Objects;
-import com.google.gson.annotations.SerializedName;
+import com.google.common.base.Objects.ToStringHelper;
 
 /**
  * Represents a CloudStack Cluster.
@@ -31,7 +38,9 @@
  */
 public class Cluster implements Comparable<Cluster> {
 
-   public enum ManagedState {
+   /**
+    */
+   public static enum ManagedState {
       MANAGED,
       PREPARE_UNMANAGED,
       UNMANAGED,
@@ -52,93 +61,160 @@
       }
    }
 
-   public static Builder builder() {
-      return new Builder();
+   public static Builder<?> builder() {
+      return new ConcreteBuilder();
    }
 
-   public static class Builder {
-      private String id;
-      private AllocationState allocationState;
-      private Host.ClusterType clusterType;
-      private String hypervisor;
-      private ManagedState managedState;
-      private String name;
-      private String podId;
-      private String podName;
-      private String zoneId;
-      private String zoneName;
+   public Builder<?> toBuilder() {
+      return new ConcreteBuilder().fromCluster(this);
+   }
 
-      public Builder id(String id) {
+   public static abstract class Builder<T extends Builder<T>>  {
+      protected abstract T self();
+
+      protected String id;
+      protected AllocationState allocationState;
+      protected Host.ClusterType clusterType;
+      protected String hypervisor;
+      protected Cluster.ManagedState managedState;
+      protected String name;
+      protected String podId;
+      protected String podName;
+      protected String zoneId;
+      protected String zoneName;
+
+      /**
+       * @see Cluster#getId()
+       */
+      public T id(String id) {
          this.id = id;
-         return this;
+         return self();
       }
 
-      public Builder allocationState(AllocationState allocationState) {
+      /**
+       * @see Cluster#getAllocationState()
+       */
+      public T allocationState(AllocationState allocationState) {
          this.allocationState = allocationState;
-         return this;
+         return self();
       }
 
-      public Builder clusterType(Host.ClusterType clusterType) {
+      /**
+       * @see Cluster#getClusterType()
+       */
+      public T clusterType(Host.ClusterType clusterType) {
          this.clusterType = clusterType;
-         return this;
+         return self();
       }
 
-      public Builder hypervisor(String hypervisor) {
+      /**
+       * @see Cluster#getHypervisor()
+       */
+      public T hypervisor(String hypervisor) {
          this.hypervisor = hypervisor;
-         return this;
+         return self();
       }
 
-      public Builder managedState(ManagedState managedState) {
+      /**
+       * @see Cluster#getManagedState()
+       */
+      public T managedState(Cluster.ManagedState managedState) {
          this.managedState = managedState;
-         return this;
+         return self();
       }
 
-      public Builder name(String name) {
+      /**
+       * @see Cluster#getName()
+       */
+      public T name(String name) {
          this.name = name;
-         return this;
+         return self();
       }
 
-      public Builder podId(String podId) {
+      /**
+       * @see Cluster#getPodId()
+       */
+      public T podId(String podId) {
          this.podId = podId;
-         return this;
+         return self();
       }
 
-      public Builder podName(String podName) {
+      /**
+       * @see Cluster#getPodName()
+       */
+      public T podName(String podName) {
          this.podName = podName;
-         return this;
+         return self();
       }
 
-      public Builder zoneId(String zoneId) {
+      /**
+       * @see Cluster#getZoneId()
+       */
+      public T zoneId(String zoneId) {
          this.zoneId = zoneId;
-         return this;
+         return self();
       }
 
-      public Builder zoneName(String zoneName) {
+      /**
+       * @see Cluster#getZoneName()
+       */
+      public T zoneName(String zoneName) {
          this.zoneName = zoneName;
-         return this;
+         return self();
       }
 
       public Cluster build() {
          return new Cluster(id, allocationState, clusterType, hypervisor, managedState, name, podId, podName, zoneId, zoneName);
       }
+
+      public T fromCluster(Cluster in) {
+         return this
+               .id(in.getId())
+               .allocationState(in.getAllocationState())
+               .clusterType(in.getClusterType())
+               .hypervisor(in.getHypervisor())
+               .managedState(in.getManagedState())
+               .name(in.getName())
+               .podId(in.getPodId())
+               .podName(in.getPodName())
+               .zoneId(in.getZoneId())
+               .zoneName(in.getZoneName());
+      }
    }
 
-   private String id;
-   @SerializedName("allocationstate") private AllocationState allocationState;
-   @SerializedName("clustertype") private Host.ClusterType clusterType;
-   @SerializedName("hypervisortype") private String hypervisor;
-   @SerializedName("managedstate") private ManagedState managedState;
-   private String name;
-   @SerializedName("podid") private String podId;
-   @SerializedName("podname") private String podName;
-   @SerializedName("zoneid") private String zoneId;
-   @SerializedName("zonename") private String zoneName;
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
+   }
 
-   // Just for the serializer
-   Cluster() {}
+   private final String id;
+   @Named("allocationstate")
+   private final AllocationState allocationState;
+   @Named("clustertype")
+   private final Host.ClusterType clusterType;
+   @Named("hypervisortype")
+   private final String hypervisor;
+   @Named("managedstate")
+   private final Cluster.ManagedState managedState;
+   private final String name;
+   @Named("podid")
+   private final String podId;
+   @Named("podname")
+   private final String podName;
+   @Named("zoneid")
+   private final String zoneId;
+   @Named("zonename")
+   private final String zoneName;
 
-   public Cluster(String id, AllocationState allocationState, Host.ClusterType clusterType, String hypervisor, ManagedState managedState, String name, String podId, String podName, String zoneId, String zoneName) {
-      this.id = id;
+   @ConstructorProperties({
+         "id", "allocationstate", "clustertype", "hypervisortype", "managedstate", "name", "podid", "podname", "zoneid", "zonename"
+   })
+   protected Cluster(String id, @Nullable AllocationState allocationState, @Nullable Host.ClusterType clusterType,
+                     @Nullable String hypervisor, @Nullable Cluster.ManagedState managedState, @Nullable String name,
+                     @Nullable String podId, @Nullable String podName, @Nullable String zoneId, @Nullable String zoneName) {
+      this.id = checkNotNull(id, "id");
       this.allocationState = allocationState;
       this.clusterType = clusterType;
       this.hypervisor = hypervisor;
@@ -151,86 +227,85 @@
    }
 
    public String getId() {
-      return id;
+      return this.id;
    }
 
+   @Nullable
    public AllocationState getAllocationState() {
-      return allocationState;
+      return this.allocationState;
    }
 
+   @Nullable
    public Host.ClusterType getClusterType() {
-      return clusterType;
+      return this.clusterType;
    }
 
+   @Nullable
    public String getHypervisor() {
-      return hypervisor;
+      return this.hypervisor;
    }
 
-   public ManagedState getManagedState() {
-      return managedState;
+   @Nullable
+   public Cluster.ManagedState getManagedState() {
+      return this.managedState;
    }
 
+   @Nullable
    public String getName() {
-      return name;
+      return this.name;
    }
 
+   @Nullable
    public String getPodId() {
-      return podId;
+      return this.podId;
    }
 
+   @Nullable
    public String getPodName() {
-      return podName;
+      return this.podName;
    }
 
+   @Nullable
    public String getZoneId() {
-      return zoneId;
+      return this.zoneId;
    }
 
+   @Nullable
    public String getZoneName() {
-      return zoneName;
-   }
-
-   @Override
-   public boolean equals(Object o) {
-      if (this == o) return true;
-      if (o == null || getClass() != o.getClass()) return false;
-
-      Cluster that = (Cluster) o;
-
-      if (!Objects.equal(id, that.id)) return false;
-      if (!Objects.equal(podId, that.podId)) return false;
-      if (!Objects.equal(zoneId, that.zoneId)) return false;
-      if (!Objects.equal(allocationState, that.allocationState)) return false;
-      if (!Objects.equal(clusterType, that.clusterType)) return false;
-      if (!Objects.equal(hypervisor, that.hypervisor)) return false;
-      if (!Objects.equal(managedState, that.managedState)) return false;
-      if (!Objects.equal(name, that.name)) return false;
-      if (!Objects.equal(podName, that.podName)) return false;
-      if (!Objects.equal(zoneName, that.zoneName)) return false;
-
-      return true;
+      return this.zoneName;
    }
 
    @Override
    public int hashCode() {
-       return Objects.hashCode(id, allocationState, clusterType, hypervisor, managedState, name, podId, podName,
-                               zoneId, zoneName);
+      return Objects.hashCode(id, allocationState, clusterType, hypervisor, managedState, name, podId, podName, zoneId, zoneName);
+   }
+
+   @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      Cluster that = Cluster.class.cast(obj);
+      return Objects.equal(this.id, that.id)
+            && Objects.equal(this.allocationState, that.allocationState)
+            && Objects.equal(this.clusterType, that.clusterType)
+            && Objects.equal(this.hypervisor, that.hypervisor)
+            && Objects.equal(this.managedState, that.managedState)
+            && Objects.equal(this.name, that.name)
+            && Objects.equal(this.podId, that.podId)
+            && Objects.equal(this.podName, that.podName)
+            && Objects.equal(this.zoneId, that.zoneId)
+            && Objects.equal(this.zoneName, that.zoneName);
+   }
+
+   protected ToStringHelper string() {
+      return Objects.toStringHelper(this)
+            .add("id", id).add("allocationState", allocationState).add("clusterType", clusterType).add("hypervisor", hypervisor)
+            .add("managedState", managedState).add("name", name).add("podId", podId).add("podName", podName).add("zoneId", zoneId).add("zoneName", zoneName);
    }
 
    @Override
    public String toString() {
-      return "Cluster{" +
-         "id=" + id +
-         ", allocationState=" + allocationState +
-         ", clusterType=" + clusterType +
-         ", hypervisor='" + hypervisor + '\'' +
-         ", managedState=" + managedState +
-         ", name='" + name + '\'' +
-         ", podId=" + podId +
-         ", podName='" + podName + '\'' +
-         ", zoneId=" + zoneId +
-         ", zoneName='" + zoneName + '\'' +
-         '}';
+      return string().toString();
    }
 
    @Override
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/ConfigurationEntry.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/ConfigurationEntry.java
index d3a655b..129c911 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/ConfigurationEntry.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/ConfigurationEntry.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -16,10 +16,16 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
 package org.jclouds.cloudstack.domain;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.beans.ConstructorProperties;
+
+import org.jclouds.javax.annotation.Nullable;
+
 import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
 
 /**
  * Representation of the API configuration entry response
@@ -28,106 +34,136 @@
  */
 public class ConfigurationEntry implements Comparable<ConfigurationEntry> {
 
-   public static Builder builder() {
-      return new Builder();
+   public static Builder<?> builder() {
+      return new ConcreteBuilder();
    }
 
-   public static class Builder {
+   public Builder<?> toBuilder() {
+      return new ConcreteBuilder().fromConfigurationEntry(this);
+   }
 
-      private String category;
-      private String description;
-      private String name;
-      private String value;
+   public static abstract class Builder<T extends Builder<T>>  {
+      protected abstract T self();
 
-      public Builder category(String category) {
+      protected String category;
+      protected String description;
+      protected String name;
+      protected String value;
+
+      /**
+       * @see ConfigurationEntry#getCategory()
+       */
+      public T category(String category) {
          this.category = category;
-         return this;
+         return self();
       }
 
-      public Builder description(String description) {
+      /**
+       * @see ConfigurationEntry#getDescription()
+       */
+      public T description(String description) {
          this.description = description;
-         return this;
+         return self();
       }
 
-      public Builder name(String name) {
+      /**
+       * @see ConfigurationEntry#getName()
+       */
+      public T name(String name) {
          this.name = name;
-         return this;
+         return self();
       }
 
-      public Builder value(String value) {
+      /**
+       * @see ConfigurationEntry#getValue()
+       */
+      public T value(String value) {
          this.value = value;
-         return this;
+         return self();
       }
 
       public ConfigurationEntry build() {
          return new ConfigurationEntry(category, description, name, value);
       }
+
+      public T fromConfigurationEntry(ConfigurationEntry in) {
+         return this
+               .category(in.getCategory())
+               .description(in.getDescription())
+               .name(in.getName())
+               .value(in.getValue());
+      }
    }
 
-   // for deserialization
-   ConfigurationEntry() {
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
    }
 
-   private String category;
-   private String description;
-   private String name;
-   private String value;
+   private final String category;
+   private final String description;
+   private final String name;
+   private final String value;
 
-   public ConfigurationEntry(String category, String description, String name, String value) {
+   @ConstructorProperties({
+         "category", "description", "name", "value"
+   })
+   protected ConfigurationEntry(@Nullable String category, @Nullable String description, String name, @Nullable String value) {
       this.category = category;
       this.description = description;
-      this.name = name;
+      this.name = checkNotNull(name, "name");
       this.value = value;
    }
 
-   @Override
-   public int compareTo(ConfigurationEntry arg0) {
-      return name.compareTo(arg0.getName());
-   }
-
+   @Nullable
    public String getCategory() {
-      return category;
+      return this.category;
    }
 
+   @Nullable
    public String getDescription() {
-      return description;
+      return this.description;
    }
 
    public String getName() {
-      return name;
+      return this.name;
    }
 
+   @Nullable
    public String getValue() {
-      return value;
-   }
-
-   @Override
-   public boolean equals(Object o) {
-      if (this == o) return true;
-      if (o == null || getClass() != o.getClass()) return false;
-
-      ConfigurationEntry that = (ConfigurationEntry) o;
-
-      if (!Objects.equal(category, that.category)) return false;
-      if (!Objects.equal(description, that.description)) return false;
-      if (!Objects.equal(name, that.name)) return false;
-      if (!Objects.equal(value, that.value)) return false;
-
-      return true;
+      return this.value;
    }
 
    @Override
    public int hashCode() {
-       return Objects.hashCode(category, description, name, value);
+      return Objects.hashCode(category, description, name, value);
+   }
+
+   @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      ConfigurationEntry that = ConfigurationEntry.class.cast(obj);
+      return Objects.equal(this.category, that.category)
+            && Objects.equal(this.description, that.description)
+            && Objects.equal(this.name, that.name)
+            && Objects.equal(this.value, that.value);
+   }
+
+   protected ToStringHelper string() {
+      return Objects.toStringHelper(this)
+            .add("category", category).add("description", description).add("name", name).add("value", value);
    }
 
    @Override
    public String toString() {
-      return "ConfigurationEntry{" +
-         "category='" + category + '\'' +
-         ", description='" + description + '\'' +
-         ", name='" + name + '\'' +
-         ", value='" + value + '\'' +
-         '}';
+      return string().toString();
+   }
+
+   @Override
+   public int compareTo(ConfigurationEntry other) {
+      return name.compareTo(other.getName());
    }
 }
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/DiskOffering.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/DiskOffering.java
index 5128fae..262a36c 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/DiskOffering.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/DiskOffering.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -20,103 +20,162 @@
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import java.beans.ConstructorProperties;
 import java.util.Date;
-import java.util.Set;
 
-import com.google.common.base.Joiner;
+import javax.inject.Named;
+
+import org.jclouds.javax.annotation.Nullable;
+
 import com.google.common.base.Objects;
-import com.google.common.base.Splitter;
-import com.google.common.collect.ImmutableSet;
-import com.google.gson.annotations.SerializedName;
+import com.google.common.base.Objects.ToStringHelper;
 
 /**
- * 
+ * Class DiskOffering
+ *
  * @author Adrian Cole
  */
 public class DiskOffering implements Comparable<DiskOffering> {
 
-   public static Builder builder() {
-      return new Builder();
+   public static Builder<?> builder() {
+      return new ConcreteBuilder();
    }
 
-   public static class Builder {
-      private String id;
-      private String name;
-      private String displayText;
-      private Date created;
-      private String domain;
-      private String domainId;
-      private int diskSize;
-      private boolean customized;
-      private Set<String> tags = ImmutableSet.of();
+   public Builder<?> toBuilder() {
+      return new ConcreteBuilder().fromDiskOffering(this);
+   }
 
-      public Builder id(String id) {
+   public static abstract class Builder<T extends Builder<T>>  {
+      protected abstract T self();
+
+      protected String id;
+      protected String name;
+      protected String displayText;
+      protected Date created;
+      protected String domain;
+      protected String domainId;
+      protected int diskSize;
+      protected boolean customized;
+      protected String tags;
+
+      /**
+       * @see DiskOffering#getId()
+       */
+      public T id(String id) {
          this.id = id;
-         return this;
+         return self();
       }
 
-      public Builder name(String name) {
+      /**
+       * @see DiskOffering#getName()
+       */
+      public T name(String name) {
          this.name = name;
-         return this;
+         return self();
       }
 
-      public Builder displayText(String displayText) {
+      /**
+       * @see DiskOffering#getDisplayText()
+       */
+      public T displayText(String displayText) {
          this.displayText = displayText;
-         return this;
+         return self();
       }
 
-      public Builder created(Date created) {
+      /**
+       * @see DiskOffering#getCreated()
+       */
+      public T created(Date created) {
          this.created = created;
-         return this;
+         return self();
       }
 
-      public Builder domain(String domain) {
+      /**
+       * @see DiskOffering#getDomain()
+       */
+      public T domain(String domain) {
          this.domain = domain;
-         return this;
+         return self();
       }
 
-      public Builder domainId(String domainId) {
+      /**
+       * @see DiskOffering#getDomainId()
+       */
+      public T domainId(String domainId) {
          this.domainId = domainId;
-         return this;
+         return self();
       }
 
-      public Builder diskSize(int diskSize) {
+      /**
+       * @see DiskOffering#getDiskSize()
+       */
+      public T diskSize(int diskSize) {
          this.diskSize = diskSize;
-         return this;
+         return self();
       }
 
-      public Builder customized(boolean customized) {
+      /**
+       * @see DiskOffering#isCustomized()
+       */
+      public T customized(boolean customized) {
          this.customized = customized;
-         return this;
+         return self();
       }
 
-      public Builder tags(Set<String> tags) {
-         this.tags = ImmutableSet.copyOf(checkNotNull(tags, "tags"));
-         return this;
+      /**
+       * @see DiskOffering#getTags()
+       */
+      public T tags(String tags) {
+         this.tags = tags;
+         return self();
       }
 
       public DiskOffering build() {
          return new DiskOffering(id, name, displayText, created, domain, domainId, diskSize, customized, tags);
       }
+
+      public T fromDiskOffering(DiskOffering in) {
+         return this
+               .id(in.getId())
+               .name(in.getName())
+               .displayText(in.getDisplayText())
+               .created(in.getCreated())
+               .domain(in.getDomain())
+               .domainId(in.getDomainId())
+               .diskSize(in.getDiskSize())
+               .customized(in.isCustomized())
+               .tags(in.getTags());
+      }
    }
 
-   private String id;
-   private String name;
-   @SerializedName("displaytext")
-   private String displayText;
-   private Date created;
-   private String domain;
-   @SerializedName("domainid")
-   private String domainId;
-   @SerializedName("disksize")
-   private int diskSize;
-   @SerializedName("iscustomized")
-   private boolean customized;
-   private String tags;
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
+   }
 
-   public DiskOffering(String id, String name, String displayText, Date created, String domain, String domainId,
-         int diskSize, boolean customized, Set<String> tags) {
-      this.id = id;
+   private final String id;
+   private final String name;
+   @Named("displaytext")
+   private final String displayText;
+   private final Date created;
+   private final String domain;
+   @Named("domainid")
+   private final String domainId;
+   @Named("disksize")
+   private final int diskSize;
+   @Named("iscustomized")
+   private final boolean customized;
+   private final String tags;
+
+   @ConstructorProperties({
+         "id", "name", "displaytext", "created", "domain", "domainid", "disksize", "iscustomized", "tags"
+   })
+   protected DiskOffering(String id, @Nullable String name, @Nullable String displayText, @Nullable Date created,
+                          @Nullable String domain, @Nullable String domainId, int diskSize, boolean customized,
+                          @Nullable String tags) {
+      this.id = checkNotNull(id, "id");
       this.name = name;
       this.displayText = displayText;
       this.created = created;
@@ -124,136 +183,113 @@
       this.domainId = domainId;
       this.diskSize = diskSize;
       this.customized = customized;
-      this.tags = tags.size() == 0 ? null : Joiner.on(',').join(tags);
+      this.tags = tags;
    }
 
    /**
-    * present only for serializer
-    * 
-    */
-   DiskOffering() {
-
-   }
-
-   /**
-    * 
     * @return the id of the disk offering
     */
    public String getId() {
-      return id;
+      return this.id;
    }
 
    /**
-    * 
     * @return the name of the disk offering
     */
-
+   @Nullable
    public String getName() {
-      return name;
+      return this.name;
    }
 
    /**
-    * 
     * @return an alternate display text of the disk offering.
     */
+   @Nullable
    public String getDisplayText() {
-      return displayText;
+      return this.displayText;
    }
 
    /**
-    * 
     * @return the date this disk offering was created
     */
+   @Nullable
    public Date getCreated() {
-      return created;
+      return this.created;
    }
 
    /**
-    * 
     * @return Domain name for the offering
     */
+   @Nullable
    public String getDomain() {
-      return domain;
+      return this.domain;
    }
 
    /**
-    * 
     * @return the domain id of the disk offering
     */
+   @Nullable
    public String getDomainId() {
-      return domainId;
+      return this.domainId;
    }
 
    /**
-    * 
     * @return the size of the disk offering in GB
     */
    public int getDiskSize() {
-      return diskSize;
+      return this.diskSize;
    }
 
    /**
-    * 
     * @return the ha support in the disk offering
     */
    public boolean isCustomized() {
-      return customized;
+      return this.customized;
    }
 
    /**
-    * 
     * @return the tags for the disk offering
     */
-   public Set<String> getTags() {
-      return tags != null ? ImmutableSet.copyOf(Splitter.on(',').split(tags)) : ImmutableSet.<String> of();
+   @Nullable
+   public String getTags() {
+      return this.tags;
    }
 
    @Override
    public int hashCode() {
-       return Objects.hashCode(created, customized, diskSize, displayText, domain, domainId, id, name, tags);
+      return Objects.hashCode(id, name, displayText, created, domain, domainId, diskSize, customized, tags);
    }
 
    @Override
    public boolean equals(Object obj) {
-      if (this == obj)
-         return true;
-      if (obj == null)
-         return false;
-      if (getClass() != obj.getClass())
-         return false;
-      DiskOffering that = (DiskOffering) obj;
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      DiskOffering that = DiskOffering.class.cast(obj);
+      return Objects.equal(this.id, that.id)
+            && Objects.equal(this.name, that.name)
+            && Objects.equal(this.displayText, that.displayText)
+            && Objects.equal(this.created, that.created)
+            && Objects.equal(this.domain, that.domain)
+            && Objects.equal(this.domainId, that.domainId)
+            && Objects.equal(this.diskSize, that.diskSize)
+            && Objects.equal(this.customized, that.customized)
+            && Objects.equal(this.tags, that.tags);
+   }
 
-      if (!Objects.equal(created, that.created)) return false;
-      if (!Objects.equal(customized, that.customized)) return false;
-      if (!Objects.equal(diskSize, that.diskSize)) return false;
-      if (!Objects.equal(displayText, that.displayText)) return false;
-      if (!Objects.equal(domain, that.domain)) return false;
-      if (!Objects.equal(domainId, that.domainId)) return false;
-      if (!Objects.equal(id, that.id)) return false;
-      if (!Objects.equal(name, that.name)) return false;
-      if (!Objects.equal(tags, that.tags)) return false;
-
-      return true;
+   protected ToStringHelper string() {
+      return Objects.toStringHelper(this)
+            .add("id", id).add("name", name).add("displayText", displayText).add("created", created).add("domain", domain)
+            .add("domainId", domainId).add("diskSize", diskSize).add("customized", customized).add("tags", tags);
    }
 
    @Override
    public String toString() {
-      return "DiskOffering{" +
-            "id=" + id +
-            ", name='" + name + '\'' +
-            ", displayText='" + displayText + '\'' +
-            ", created=" + created +
-            ", domain='" + domain + '\'' +
-            ", domainId=" + domainId +
-            ", diskSize=" + diskSize +
-            ", customized=" + customized +
-            ", tags='" + tags + '\'' +
-            '}';
+      return string().toString();
    }
 
    @Override
-   public int compareTo(DiskOffering arg0) {
-      return id.compareTo(arg0.getId());
+   public int compareTo(DiskOffering other) {
+      return id.compareTo(other.getId());
    }
 
 }
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Domain.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Domain.java
index 565e1f6..c127bc9 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Domain.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Domain.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -16,11 +16,18 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
 package org.jclouds.cloudstack.domain;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.beans.ConstructorProperties;
+
+import javax.inject.Named;
+
+import org.jclouds.javax.annotation.Nullable;
+
 import com.google.common.base.Objects;
-import com.google.gson.annotations.SerializedName;
+import com.google.common.base.Objects.ToStringHelper;
 
 /**
  * Representation of the API domain response
@@ -29,80 +36,122 @@
  */
 public class Domain implements Comparable<Domain> {
 
-   public static Builder builder() {
-      return new Builder();
+   public static Builder<?> builder() {
+      return new ConcreteBuilder();
    }
 
-   public static class Builder {
+   public Builder<?> toBuilder() {
+      return new ConcreteBuilder().fromDomain(this);
+   }
 
-      private String id;
-      private boolean hasChild;
-      private long level;
-      private String name;
-      private String networkDomain;
-      private String parentDomainId;
-      private String parentDomainName;
+   public static abstract class Builder<T extends Builder<T>>  {
+      protected abstract T self();
 
-      public Builder id(String id) {
+      protected String id;
+      protected boolean hasChild;
+      protected long level;
+      protected String name;
+      protected String networkDomain;
+      protected String parentDomainId;
+      protected String parentDomainName;
+
+      /**
+       * @see Domain#getId()
+       */
+      public T id(String id) {
          this.id = id;
-         return this;
+         return self();
       }
 
-      public Builder hasChild(boolean hasChild) {
+      /**
+       * @see Domain#hasChild()
+       */
+      public T hasChild(boolean hasChild) {
          this.hasChild = hasChild;
-         return this;
+         return self();
       }
 
-      public Builder level(long level) {
+      /**
+       * @see Domain#getLevel()
+       */
+      public T level(long level) {
          this.level = level;
-         return this;
+         return self();
       }
 
-      public Builder name(String name) {
+      /**
+       * @see Domain#getName()
+       */
+      public T name(String name) {
          this.name = name;
-         return this;
+         return self();
       }
 
-      public Builder networkDomain(String networkDomain) {
+      /**
+       * @see Domain#getNetworkDomain()
+       */
+      public T networkDomain(String networkDomain) {
          this.networkDomain = networkDomain;
-         return this;
+         return self();
       }
 
-      public Builder parentDomainId(String parentDomainId) {
+      /**
+       * @see Domain#getParentDomainId()
+       */
+      public T parentDomainId(String parentDomainId) {
          this.parentDomainId = parentDomainId;
-         return this;
+         return self();
       }
 
-      public Builder parentDomainName(String parentDomainName) {
+      /**
+       * @see Domain#getParentDomainName()
+       */
+      public T parentDomainName(String parentDomainName) {
          this.parentDomainName = parentDomainName;
-         return this;
+         return self();
       }
 
       public Domain build() {
-         return new Domain(id, hasChild, level, name, networkDomain,
-            parentDomainId, parentDomainName);
+         return new Domain(id, hasChild, level, name, networkDomain, parentDomainId, parentDomainName);
+      }
+
+      public T fromDomain(Domain in) {
+         return this
+               .id(in.getId())
+               .hasChild(in.hasChild())
+               .level(in.getLevel())
+               .name(in.getName())
+               .networkDomain(in.getNetworkDomain())
+               .parentDomainId(in.getParentDomainId())
+               .parentDomainName(in.getParentDomainName());
       }
    }
 
-   // for deserialization
-   Domain() {
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
    }
 
-   private String id;
-   @SerializedName("haschild")
-   private boolean hasChild;
-   private long level;
-   private String name;
-   @SerializedName("networkdomain")
-   private String networkDomain;
-   @SerializedName("parentdomainid")
-   private String parentDomainId;
-   @SerializedName("parentdomainname")
-   private String parentDomainName;
+   private final String id;
+   @Named("haschild")
+   private final boolean hasChild;
+   private final long level;
+   private final String name;
+   @Named("networkdomain")
+   private final String networkDomain;
+   @Named("parentdomainid")
+   private final String parentDomainId;
+   @Named("parentdomainname")
+   private final String parentDomainName;
 
-   public Domain(String id, boolean hasChild, long level, String name, String networkDomain,
-         String parentDomainId, String parentDomainName) {
-      this.id = id;
+   @ConstructorProperties({
+         "id", "haschild", "level", "name", "networkdomain", "parentdomainid", "parentdomainname"
+   })
+   protected Domain(String id, boolean hasChild, long level, @Nullable String name, @Nullable String networkDomain,
+                    @Nullable String parentDomainId, @Nullable String parentDomainName) {
+      this.id = checkNotNull(id, "id");
       this.hasChild = hasChild;
       this.level = level;
       this.name = name;
@@ -112,72 +161,69 @@
    }
 
    public String getId() {
-      return id;
+      return this.id;
    }
 
    public boolean hasChild() {
-      return hasChild;
+      return this.hasChild;
    }
 
    public long getLevel() {
-      return level;
+      return this.level;
    }
 
+   @Nullable
    public String getName() {
-      return name;
+      return this.name;
    }
 
+   @Nullable
    public String getNetworkDomain() {
-      return networkDomain;
+      return this.networkDomain;
    }
 
+   @Nullable
    public String getParentDomainId() {
-      return parentDomainId;
+      return this.parentDomainId;
    }
 
+   @Nullable
    public String getParentDomainName() {
-      return parentDomainName;
-   }
-
-   @Override
-   public boolean equals(Object o) {
-      if (this == o) return true;
-      if (o == null || getClass() != o.getClass()) return false;
-
-      Domain that = (Domain) o;
-
-      if (!Objects.equal(hasChild, that.hasChild)) return false;
-      if (!Objects.equal(id, that.id)) return false;
-      if (!Objects.equal(level, that.level)) return false;
-      if (!Objects.equal(parentDomainId, that.parentDomainId)) return false;
-      if (!Objects.equal(name, that.name)) return false;
-      if (!Objects.equal(networkDomain, that.networkDomain)) return false;
-      if (!Objects.equal(parentDomainName, that.parentDomainName)) return false;
-
-      return true;
+      return this.parentDomainName;
    }
 
    @Override
    public int hashCode() {
-       return Objects.hashCode(id, hasChild, level, name, networkDomain, parentDomainId, parentDomainName);
+      return Objects.hashCode(id, hasChild, level, name, networkDomain, parentDomainId, parentDomainName);
+   }
+
+   @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      Domain that = Domain.class.cast(obj);
+      return Objects.equal(this.id, that.id)
+            && Objects.equal(this.hasChild, that.hasChild)
+            && Objects.equal(this.level, that.level)
+            && Objects.equal(this.name, that.name)
+            && Objects.equal(this.networkDomain, that.networkDomain)
+            && Objects.equal(this.parentDomainId, that.parentDomainId)
+            && Objects.equal(this.parentDomainName, that.parentDomainName);
+   }
+
+   protected ToStringHelper string() {
+      return Objects.toStringHelper(this)
+            .add("id", id).add("hasChild", hasChild).add("level", level).add("name", name).add("networkDomain", networkDomain).add("parentDomainId", parentDomainId).add("parentDomainName", parentDomainName);
    }
 
    @Override
    public String toString() {
-      return "Domain{" +
-         "id='" + id + '\'' +
-         ", hasChild=" + hasChild +
-         ", level=" + level +
-         ", name='" + name + '\'' +
-         ", networkDomain='" + networkDomain + '\'' +
-         ", parentDomainId='" + parentDomainId + '\'' +
-         ", parentDomainName='" + parentDomainName + '\'' +
-         '}';
+      return string().toString();
    }
 
    @Override
-   public int compareTo(Domain arg0) {
-      return id.compareTo(arg0.getId());
+   public int compareTo(Domain other) {
+      return id.compareTo(other.getId());
    }
 
 }
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/EncryptedPasswordAndPrivateKey.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/EncryptedPasswordAndPrivateKey.java
index fcec6d9..cf2ae21 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/EncryptedPasswordAndPrivateKey.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/EncryptedPasswordAndPrivateKey.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -18,17 +18,75 @@
  */
 package org.jclouds.cloudstack.domain;
 
+import java.beans.ConstructorProperties;
+
+import org.jclouds.javax.annotation.Nullable;
+
 import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
 
 /**
+ * Class EncryptedPasswordAndPrivateKey
+ *
  * @author Andrei Savu
  */
 public class EncryptedPasswordAndPrivateKey {
 
+   public static Builder<?> builder() {
+      return new ConcreteBuilder();
+   }
+
+   public Builder<?> toBuilder() {
+      return new ConcreteBuilder().fromEncryptedPasswordAndPrivateKey(this);
+   }
+
+   public static abstract class Builder<T extends Builder<T>>  {
+      protected abstract T self();
+
+      protected String encryptedPassword;
+      protected String privateKey;
+
+      /**
+       * @see EncryptedPasswordAndPrivateKey#getEncryptedPassword()
+       */
+      public T encryptedPassword(String encryptedPassword) {
+         this.encryptedPassword = encryptedPassword;
+         return self();
+      }
+
+      /**
+       * @see EncryptedPasswordAndPrivateKey#getPrivateKey()
+       */
+      public T privateKey(String privateKey) {
+         this.privateKey = privateKey;
+         return self();
+      }
+
+      public EncryptedPasswordAndPrivateKey build() {
+         return new EncryptedPasswordAndPrivateKey(encryptedPassword, privateKey);
+      }
+
+      public T fromEncryptedPasswordAndPrivateKey(EncryptedPasswordAndPrivateKey in) {
+         return this
+               .encryptedPassword(in.getEncryptedPassword())
+               .privateKey(in.getPrivateKey());
+      }
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
+   }
+
    private final String encryptedPassword;
    private final String privateKey;
 
-   public EncryptedPasswordAndPrivateKey(String encryptedPassword, String privateKey) {
+   @ConstructorProperties({
+         "encryptedPassword", "privateKey"
+   })
+   protected EncryptedPasswordAndPrivateKey(@Nullable String encryptedPassword, @Nullable String privateKey) {
       this.encryptedPassword = encryptedPassword;
       this.privateKey = privateKey;
    }
@@ -36,40 +94,41 @@
    /**
     * @return the encrypted password String representation
     */
+   @Nullable
    public String getEncryptedPassword() {
-      return encryptedPassword;
+      return this.encryptedPassword;
    }
 
    /**
     * @return get the string representation of the private key
     */
+   @Nullable
    public String getPrivateKey() {
-      return privateKey;
-   }
-
-   @Override
-   public boolean equals(Object o) {
-      if (this == o) return true;
-      if (o == null || getClass() != o.getClass()) return false;
-
-      EncryptedPasswordAndPrivateKey that = (EncryptedPasswordAndPrivateKey) o;
-
-      if (!Objects.equal(encryptedPassword, that.encryptedPassword)) return false;
-      if (!Objects.equal(privateKey, that.privateKey)) return false;
-
-      return true;
+      return this.privateKey;
    }
 
    @Override
    public int hashCode() {
-       return Objects.hashCode(encryptedPassword, privateKey);
+      return Objects.hashCode(encryptedPassword, privateKey);
+   }
+
+   @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      EncryptedPasswordAndPrivateKey that = EncryptedPasswordAndPrivateKey.class.cast(obj);
+      return Objects.equal(this.encryptedPassword, that.encryptedPassword)
+            && Objects.equal(this.privateKey, that.privateKey);
+   }
+
+   protected ToStringHelper string() {
+      return Objects.toStringHelper(this)
+            .add("encryptedPassword", encryptedPassword).add("privateKey", privateKey);
    }
 
    @Override
    public String toString() {
-      return "EncryptedPasswordAndPrivateKey{" +
-         "encryptedPassword='" + encryptedPassword + '\'' +
-         ", privateKey='" + privateKey + '\'' +
-         '}';
+      return string().toString();
    }
+
 }
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Event.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Event.java
index c1b6c1a..27645bb 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Event.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Event.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -18,112 +18,180 @@
  */
 package org.jclouds.cloudstack.domain;
 
-import com.google.common.base.Objects;
+import static com.google.common.base.Preconditions.checkNotNull;
 
+import java.beans.ConstructorProperties;
 import java.util.Date;
 
+import org.jclouds.javax.annotation.Nullable;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
+
 /**
+ * Class Event
+ *
  * @author Vijay Kiran
  */
 public class Event implements Comparable<Event> {
 
-   public static Builder builder() {
-      return new Builder();
+   public static Builder<?> builder() {
+      return new ConcreteBuilder();
    }
 
-   public static class Builder {
-      private String id;
-      private String account;
-      private String description;
-      private Date created;
-      private String domain;
-      private String domainId;
-      //TODO Change to enum : the event level (INFO, WARN, ERROR)
-      private String level;
-      private String parentId;
-      private String state;
-      //Event Type
-      private String type;
-      private String username;
+   public Builder<?> toBuilder() {
+      return new ConcreteBuilder().fromEvent(this);
+   }
 
-      public Builder id(String id) {
+   public static abstract class Builder<T extends Builder<T>>  {
+      protected abstract T self();
+
+      protected String id;
+      protected String account;
+      protected String description;
+      protected Date created;
+      protected String domain;
+      protected String domainId;
+      protected String level;
+      protected String parentId;
+      protected String state;
+      protected String type;
+      protected String username;
+
+      /**
+       * @see Event#getId()
+       */
+      public T id(String id) {
          this.id = id;
-         return this;
+         return self();
       }
 
-      public Builder account(String account) {
+      /**
+       * @see Event#getAccount()
+       */
+      public T account(String account) {
          this.account = account;
-         return this;
+         return self();
       }
 
-      public Builder description(String description) {
+      /**
+       * @see Event#getDescription()
+       */
+      public T description(String description) {
          this.description = description;
-         return this;
+         return self();
       }
 
-      public Builder created(Date created) {
+      /**
+       * @see Event#getCreated()
+       */
+      public T created(Date created) {
          this.created = created;
-         return this;
+         return self();
       }
 
-      public Builder domain(String domain) {
+      /**
+       * @see Event#getDomain()
+       */
+      public T domain(String domain) {
          this.domain = domain;
-         return this;
+         return self();
       }
 
-      public Builder domainId(String domainId) {
+      /**
+       * @see Event#getDomainId()
+       */
+      public T domainId(String domainId) {
          this.domainId = domainId;
-         return this;
+         return self();
       }
 
-      public Builder level(String level) {
+      /**
+       * @see Event#getLevel()
+       */
+      public T level(String level) {
          this.level = level;
-         return this;
+         return self();
       }
 
-      public Builder parentId(String parentId) {
+      /**
+       * @see Event#getParentId()
+       */
+      public T parentId(String parentId) {
          this.parentId = parentId;
-         return this;
+         return self();
       }
 
-      public Builder state(String state) {
+      /**
+       * @see Event#getState()
+       */
+      public T state(String state) {
          this.state = state;
-         return this;
+         return self();
       }
 
-      public Builder type(String type) {
+      /**
+       * @see Event#getType()
+       */
+      public T type(String type) {
          this.type = type;
-         return this;
+         return self();
       }
 
-      public Builder username(String username) {
+      /**
+       * @see Event#getUsername()
+       */
+      public T username(String username) {
          this.username = username;
-         return this;
+         return self();
       }
 
       public Event build() {
          return new Event(id, account, description, created, domain, domainId, level, parentId, state, type, username);
       }
 
+      public T fromEvent(Event in) {
+         return this
+               .id(in.getId())
+               .account(in.getAccount())
+               .description(in.getDescription())
+               .created(in.getCreated())
+               .domain(in.getDomain())
+               .domainId(in.getDomainId())
+               .level(in.getLevel())
+               .parentId(in.getParentId())
+               .state(in.getState())
+               .type(in.getType())
+               .username(in.getUsername());
+      }
    }
 
-   private String id;
-   private String account;
-   private String description;
-   private Date created;
-   private String domain;
-   private String domainId;
-   //TODO Change to enum : the event level (INFO, WARN, ERROR)
-   private String level;
-   private String parentId;
-   private String state;
-   //Event Type
-   private String type;
-   private String username;
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
+   }
 
-   public Event(String id, String account, String description, Date created, String domain, String domainId, String level,
-                String parentId, String state, String type, String username) {
-      this.id = id;
+   private final String id;
+   private final String account;
+   private final String description;
+   private final Date created;
+   private final String domain;
+   private final String domainId;
+   private final String level;
+   private final String parentId;
+   private final String state;
+   private final String type;
+   private final String username;
+
+   @ConstructorProperties({
+         "id", "account", "description", "created", "domain", "domainId", "level", "parentId", "state", "type", "username"
+   })
+   protected Event(String id, @Nullable String account, @Nullable String description, @Nullable Date created,
+                   @Nullable String domain, @Nullable String domainId, @Nullable String level, @Nullable String parentId,
+                   @Nullable String state, @Nullable String type, @Nullable String username) {
+      this.id = checkNotNull(id, "id");
       this.account = account;
       this.description = description;
       this.created = created;
@@ -137,139 +205,132 @@
    }
 
    /**
-    * present only for serializer
-    */
-   Event() {
-
-   }
-
-   /**
     * @return the ID of the event
     */
    public String getId() {
-      return id;
+      return this.id;
    }
 
    /**
     * @return the account name for the account that owns the object being acted on in the event
-    *         (e.g. the owner of the virtual machine, ip address, or security group)
+   (e.g. the owner of the virtual machine, ip address, or security group)
     */
+   @Nullable
    public String getAccount() {
-      return account;
-   }
-
-   /**
-    * @return the date the event was created
-    */
-   public Date getCreated() {
-      return created;
+      return this.account;
    }
 
    /**
     * @return the description of the event
     */
+   @Nullable
    public String getDescription() {
-      return description;
+      return this.description;
+   }
+
+   /**
+    * @return the date the event was created
+    */
+   @Nullable
+   public Date getCreated() {
+      return this.created;
    }
 
    /**
     * @return the name of the account's domain
     */
+   @Nullable
    public String getDomain() {
-      return domain;
+      return this.domain;
    }
 
    /**
     * @return the id of the account's domain
     */
+   @Nullable
    public String getDomainId() {
-      return domainId;
+      return this.domainId;
    }
 
    /**
     * @return the event level (INFO, WARN, ERROR)
     */
+   @Nullable
    public String getLevel() {
-      return level;
+      return this.level;
    }
 
    /**
     * @return whether the event is parented
     */
+   @Nullable
    public String getParentId() {
-      return parentId;
+      return this.parentId;
    }
 
    /**
     * @return the state of the event
     */
+   @Nullable
    public String getState() {
-      return state;
+      return this.state;
    }
 
    /**
     * @return the type of the event (see event types)
     */
+   @Nullable
    public String getType() {
-      return type;
+      return this.type;
    }
 
    /**
     * @return the name of the user who performed the action (can be different from the account if
-    *         an admin is performing an action for a user, e.g. starting/stopping a user's virtual machine)
+   an admin is performing an action for a user, e.g. starting/stopping a user's virtual machine)
     */
+   @Nullable
    public String getUsername() {
-      return username;
-   }
-
-   @Override
-   public int compareTo(Event arg0) {
-      return id.compareTo(arg0.getId());
-   }
-
-   @Override
-   public boolean equals(Object o) {
-      if (this == o) return true;
-      if (o == null || getClass() != o.getClass()) return false;
-
-      Event that = (Event) o;
-
-      if (!Objects.equal(domainId, that.domainId)) return false;
-      if (!Objects.equal(id, that.id)) return false;
-      if (!Objects.equal(account, that.account)) return false;
-      if (!Objects.equal(created, that.created)) return false;
-      if (!Objects.equal(description, that.description)) return false;
-      if (!Objects.equal(domain, that.domain)) return false;
-      if (!Objects.equal(level, that.level)) return false;
-      if (!Objects.equal(parentId, that.parentId)) return false;
-      if (!Objects.equal(state, that.state)) return false;
-      if (!Objects.equal(type, that.type)) return false;
-      if (!Objects.equal(username, that.username)) return false;
-
-      return true;
+      return this.username;
    }
 
    @Override
    public int hashCode() {
-       return Objects.hashCode(id, account, description, created, domain, domainId, level, parentId, state, type, username);
+      return Objects.hashCode(id, account, description, created, domain, domainId, level, parentId, state, type, username);
+   }
+
+   @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      Event that = Event.class.cast(obj);
+      return Objects.equal(this.id, that.id)
+            && Objects.equal(this.account, that.account)
+            && Objects.equal(this.description, that.description)
+            && Objects.equal(this.created, that.created)
+            && Objects.equal(this.domain, that.domain)
+            && Objects.equal(this.domainId, that.domainId)
+            && Objects.equal(this.level, that.level)
+            && Objects.equal(this.parentId, that.parentId)
+            && Objects.equal(this.state, that.state)
+            && Objects.equal(this.type, that.type)
+            && Objects.equal(this.username, that.username);
+   }
+
+   protected ToStringHelper string() {
+      return Objects.toStringHelper(this)
+            .add("id", id).add("account", account).add("description", description).add("created", created)
+            .add("domain", domain).add("domainId", domainId).add("level", level).add("parentId", parentId)
+            .add("state", state).add("type", type).add("username", username);
    }
 
    @Override
    public String toString() {
-      return "Event{" +
-            "id=" + id +
-            ", account='" + account + '\'' +
-            ", description='" + description + '\'' +
-            ", created=" + created +
-            ", domain='" + domain + '\'' +
-            ", domainId=" + domainId +
-            ", level='" + level + '\'' +
-            ", parentId='" + parentId + '\'' +
-            ", state='" + state + '\'' +
-            ", type='" + type + '\'' +
-            ", username='" + username + '\'' +
-            '}';
+      return string().toString();
    }
 
+   @Override
+   public int compareTo(Event other) {
+      return id.compareTo(other.getId());
+   }
 
 }
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/FirewallRule.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/FirewallRule.java
index 8b94d67..12c43e7 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/FirewallRule.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/FirewallRule.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -18,18 +18,29 @@
  */
 package org.jclouds.cloudstack.domain;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.beans.ConstructorProperties;
 import java.util.Set;
 
+import javax.inject.Named;
+
+import org.jclouds.javax.annotation.Nullable;
+
 import com.google.common.base.CaseFormat;
 import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
 import com.google.common.collect.ImmutableSet;
-import com.google.gson.annotations.SerializedName;
 
 /**
+ * Class FirewallRule
+ *
  * @author Andrei Savu
  */
 public class FirewallRule implements Comparable<FirewallRule> {
 
+   /**
+    */
    public static enum Protocol {
       TCP,
       UDP,
@@ -73,105 +84,164 @@
       }
    }
 
-   public static Builder builder() {
-      return new Builder();
+   public static Builder<?> builder() {
+      return new ConcreteBuilder();
    }
 
-   public static class Builder {
-      private String id;
-      private Set<String> CIDRs;
+   public Builder<?> toBuilder() {
+      return new ConcreteBuilder().fromFirewallRule(this);
+   }
 
-      private int startPort;
-      private int endPort;
+   public static abstract class Builder<T extends Builder<T>>  {
+      protected abstract T self();
 
-      private String icmpCode;
-      private String icmpType;
+      protected String id;
+      protected Set<String> CIDRs = ImmutableSet.of();
+      protected int startPort;
+      protected int endPort;
+      protected String icmpCode;
+      protected String icmpType;
+      protected String ipAddress;
+      protected String ipAddressId;
+      protected FirewallRule.Protocol protocol;
+      protected FirewallRule.State state;
 
-      private String ipAddress;
-      private String ipAddressId;
-
-      private Protocol protocol;
-      private State state;
-
-      public Builder id(String id) {
+      /**
+       * @see FirewallRule#getId()
+       */
+      public T id(String id) {
          this.id = id;
-         return this;
+         return self();
       }
 
-      public Builder CIDRs(Set<String> CIDRs) {
-         this.CIDRs = ImmutableSet.copyOf(CIDRs);
-         return this;
+      /**
+       * @see FirewallRule#getCIDRs()
+       */
+      public T CIDRs(Set<String> CIDRs) {
+         this.CIDRs = ImmutableSet.copyOf(checkNotNull(CIDRs, "CIDRs"));
+         return self();
       }
 
-      public Builder startPort(int startPort) {
+      public T CIDRs(String... in) {
+         return CIDRs(ImmutableSet.copyOf(in));
+      }
+
+      /**
+       * @see FirewallRule#getStartPort()
+       */
+      public T startPort(int startPort) {
          this.startPort = startPort;
-         return this;
+         return self();
       }
 
-      public Builder endPort(int endPort) {
+      /**
+       * @see FirewallRule#getEndPort()
+       */
+      public T endPort(int endPort) {
          this.endPort = endPort;
-         return this;
+         return self();
       }
 
-      public Builder icmpCode(String icmpCode) {
+      /**
+       * @see FirewallRule#getIcmpCode()
+       */
+      public T icmpCode(String icmpCode) {
          this.icmpCode = icmpCode;
-         return this;
+         return self();
       }
 
-      public Builder icmpType(String icmpType) {
+      /**
+       * @see FirewallRule#getIcmpType()
+       */
+      public T icmpType(String icmpType) {
          this.icmpType = icmpType;
-         return this;
+         return self();
       }
 
-      public Builder ipAddress(String ipAddress) {
+      /**
+       * @see FirewallRule#getIpAddress()
+       */
+      public T ipAddress(String ipAddress) {
          this.ipAddress = ipAddress;
-         return this;
+         return self();
       }
 
-      public Builder ipAddressId(String ipAddressId) {
+      /**
+       * @see FirewallRule#getIpAddressId()
+       */
+      public T ipAddressId(String ipAddressId) {
          this.ipAddressId = ipAddressId;
-         return this;
+         return self();
       }
 
-      public Builder protocol(Protocol protocol) {
+      /**
+       * @see FirewallRule#getProtocol()
+       */
+      public T protocol(FirewallRule.Protocol protocol) {
          this.protocol = protocol;
-         return this;
+         return self();
       }
 
-      public Builder state(State state) {
+      /**
+       * @see FirewallRule#getState()
+       */
+      public T state(FirewallRule.State state) {
          this.state = state;
-         return this;
+         return self();
       }
 
       public FirewallRule build() {
-         return new FirewallRule(id, CIDRs, startPort, endPort, icmpCode,
-            icmpType, ipAddress, ipAddressId, protocol, state);
+         return new FirewallRule(id, CIDRs, startPort, endPort, icmpCode, icmpType, ipAddress, ipAddressId, protocol, state);
+      }
+
+      public T fromFirewallRule(FirewallRule in) {
+         return this
+               .id(in.getId())
+               .CIDRs(in.getCIDRs())
+               .startPort(in.getStartPort())
+               .endPort(in.getEndPort())
+               .icmpCode(in.getIcmpCode())
+               .icmpType(in.getIcmpType())
+               .ipAddress(in.getIpAddress())
+               .ipAddressId(in.getIpAddressId())
+               .protocol(in.getProtocol())
+               .state(in.getState());
       }
    }
 
-   private String id;
-   @SerializedName("cidrlist")
-   private Set<String> CIDRs;
-   @SerializedName("startport")
-   private int startPort;
-   @SerializedName("endport")
-   private int endPort;
-   @SerializedName("icmpcode")
-   private String icmpCode;
-   @SerializedName("icmptype")
-   private String icmpType;
-   @SerializedName("ipaddress")
-   private String ipAddress;
-   @SerializedName("ipaddressid")
-   private String ipAddressId;
-   private Protocol protocol;
-   private State state;
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
+   }
 
-   public FirewallRule(String id, Set<String> CIDRs, int startPort, int endPort,
-         String icmpCode, String icmpType, String ipAddress, String ipAddressId,
-         Protocol protocol, State state) {
-      this.id = id;
-      this.CIDRs = ImmutableSet.copyOf(CIDRs);
+   private final String id;
+   @Named("cidrlist")
+   private final Set<String> CIDRs;
+   @Named("startport")
+   private final int startPort;
+   @Named("endport")
+   private final int endPort;
+   @Named("icmpcode")
+   private final String icmpCode;
+   @Named("icmptype")
+   private final String icmpType;
+   @Named("ipaddress")
+   private final String ipAddress;
+   @Named("ipaddressid")
+   private final String ipAddressId;
+   private final FirewallRule.Protocol protocol;
+   private final FirewallRule.State state;
+
+   @ConstructorProperties({
+         "id", "cidrlist", "startport", "endport", "icmpcode", "icmptype", "ipaddress", "ipaddressid", "protocol", "state"
+   })
+   protected FirewallRule(String id, @Nullable Set<String> CIDRs, int startPort, int endPort, @Nullable String icmpCode,
+                          @Nullable String icmpType, @Nullable String ipAddress, @Nullable String ipAddressId,
+                          @Nullable FirewallRule.Protocol protocol, @Nullable FirewallRule.State state) {
+      this.id = checkNotNull(id, "id");
+      this.CIDRs = CIDRs == null ? ImmutableSet.<String>of() : ImmutableSet.copyOf(CIDRs);
       this.startPort = startPort;
       this.endPort = endPort;
       this.icmpCode = icmpCode;
@@ -182,90 +252,88 @@
       this.state = state;
    }
 
-   @Override
-   public int compareTo(FirewallRule arg0) {
-      return id.compareTo(arg0.getId());
-   }
-
    public String getId() {
-      return id;
+      return this.id;
    }
 
    public Set<String> getCIDRs() {
-      return CIDRs;
+      return this.CIDRs;
    }
 
    public int getStartPort() {
-      return startPort;
+      return this.startPort;
    }
 
    public int getEndPort() {
-      return endPort;
+      return this.endPort;
    }
 
+   @Nullable
    public String getIcmpCode() {
-      return icmpCode;
+      return this.icmpCode;
    }
 
+   @Nullable
    public String getIcmpType() {
-      return icmpType;
+      return this.icmpType;
    }
 
+   @Nullable
    public String getIpAddress() {
-      return ipAddress;
+      return this.ipAddress;
    }
 
+   @Nullable
    public String getIpAddressId() {
-      return ipAddressId;
+      return this.ipAddressId;
    }
 
-   public Protocol getProtocol() {
-      return protocol;
+   @Nullable
+   public FirewallRule.Protocol getProtocol() {
+      return this.protocol;
    }
 
-   public State getState() {
-      return state;
-   }
-
-   @Override
-   public boolean equals(Object o) {
-      if (this == o) return true;
-      if (o == null || getClass() != o.getClass()) return false;
-
-      FirewallRule that = (FirewallRule) o;
-
-      if (!Objects.equal(endPort, that.endPort)) return false;
-      if (!Objects.equal(id, that.id)) return false;
-      if (!Objects.equal(startPort, that.startPort)) return false;
-      if (!Objects.equal(CIDRs, that.CIDRs)) return false;
-      if (!Objects.equal(icmpCode, that.icmpCode)) return false;
-      if (!Objects.equal(icmpType, that.icmpType)) return false;
-      if (!Objects.equal(ipAddress, that.ipAddress)) return false;
-      if (!Objects.equal(ipAddressId, that.ipAddressId)) return false;
-      if (!Objects.equal(protocol, that.protocol)) return false;
-      if (!Objects.equal(state, that.state)) return false;
-
-      return true;
+   @Nullable
+   public FirewallRule.State getState() {
+      return this.state;
    }
 
    @Override
    public int hashCode() {
-       return Objects.hashCode(endPort, id, startPort, CIDRs, icmpCode, icmpType, ipAddress, ipAddressId, protocol, state);
+      return Objects.hashCode(id, CIDRs, startPort, endPort, icmpCode, icmpType, ipAddress, ipAddressId, protocol, state);
+   }
+
+   @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      FirewallRule that = FirewallRule.class.cast(obj);
+      return Objects.equal(this.id, that.id)
+            && Objects.equal(this.CIDRs, that.CIDRs)
+            && Objects.equal(this.startPort, that.startPort)
+            && Objects.equal(this.endPort, that.endPort)
+            && Objects.equal(this.icmpCode, that.icmpCode)
+            && Objects.equal(this.icmpType, that.icmpType)
+            && Objects.equal(this.ipAddress, that.ipAddress)
+            && Objects.equal(this.ipAddressId, that.ipAddressId)
+            && Objects.equal(this.protocol, that.protocol)
+            && Objects.equal(this.state, that.state);
+   }
+
+   protected ToStringHelper string() {
+      return Objects.toStringHelper(this)
+            .add("id", id).add("CIDRs", CIDRs).add("startPort", startPort).add("endPort", endPort).add("icmpCode", icmpCode)
+            .add("icmpType", icmpType).add("ipAddress", ipAddress).add("ipAddressId", ipAddressId).add("protocol", protocol).add("state", state);
    }
 
    @Override
    public String toString() {
-      return "FirewallRule{" +
-         "id=" + id +
-         ", CIDRs='" + CIDRs + '\'' +
-         ", startPort=" + startPort +
-         ", endPort=" + endPort +
-         ", icmpCode='" + icmpCode + '\'' +
-         ", icmpType='" + icmpType + '\'' +
-         ", ipAddress='" + ipAddress + '\'' +
-         ", ipAddressId='" + ipAddressId + '\'' +
-         ", protocol='" + protocol + '\'' +
-         ", state='" + state + '\'' +
-         '}';
+      return string().toString();
    }
+
+   @Override
+   public int compareTo(FirewallRule other) {
+      return id.compareTo(other.getId());
+   }
+
 }
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/GuestIPType.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/GuestIPType.java
index 60f378f..76f1274 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/GuestIPType.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/GuestIPType.java
@@ -25,7 +25,7 @@
 /**
  * 
  * @author Adrian Cole
- * @see NetworkOfferingClient#listNetworkOfferings
+ * @see org.jclouds.cloudstack.features.OfferingClient#listNetworkOfferings
  */
 public enum GuestIPType {
 
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Host.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Host.java
index a92ba74..00f822f 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Host.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Host.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -20,11 +20,17 @@
 
 import static com.google.common.base.CaseFormat.UPPER_CAMEL;
 import static com.google.common.base.CaseFormat.UPPER_UNDERSCORE;
+import static com.google.common.base.Preconditions.checkNotNull;
 
+import java.beans.ConstructorProperties;
 import java.util.Date;
 
+import javax.inject.Named;
+
+import org.jclouds.javax.annotation.Nullable;
+
 import com.google.common.base.Objects;
-import com.google.gson.annotations.SerializedName;
+import com.google.common.base.Objects.ToStringHelper;
 
 /**
  * Represents a host issued by Cloudstack
@@ -41,7 +47,7 @@
       public static ClusterType fromValue(String value) {
          try {
             return valueOf(UPPER_CAMEL.to(UPPER_UNDERSCORE, value));
-         } catch(IllegalArgumentException e) {
+         } catch (IllegalArgumentException e) {
             return UNKNOWN;
          }
       }
@@ -116,368 +122,544 @@
       }
    }
 
-   public static Builder builder() {
-      return new Builder();
+   public static Builder<?> builder() {
+      return new ConcreteBuilder();
    }
 
-   public static class Builder {
-      private String id;
-      private AllocationState allocationState;
-      private int averageLoad;
-      private String capabilities;
-      private String clusterId;
-      private String clusterName;
-      private ClusterType clusterType;
-      private String cpuAllocated;
-      private int cpuNumber;
-      private int cpuSpeed;
-      private String cpuUsed;
-      private float cpuWithOverProvisioning;
-      private Date created;
-      private Date disconnected;
-      private long diskSizeAllocated;
-      private long diskSizeTotal;
-      private String events;
-      private boolean hasEnoughCapacity;
-      private String hostTags;
-      private String hypervisor;
-      private String ipAddress;
-      private boolean localStorageActive;
-      private String jobId;
-      private AsyncJob.Status jobStatus;
-      private Date lastPinged;
-      private String managementServerId;
-      private long memoryAllocated;
-      private long memoryTotal;
-      private long memoryUsed;
-      private String name;
-      private long networkKbsRead;
-      private long networkKbsWrite;
-      private String osCategoryId;
-      private String osCategoryName;
-      private String podId;
-      private String podName;
-      private Date removed;
-      private State state;
-      private Type type;
-      private String version;
-      private String zoneId;
-      private String zoneName;
+   public Builder<?> toBuilder() {
+      return new ConcreteBuilder().fromHost(this);
+   }
 
-      public Builder id(String id) {
+   public static abstract class Builder<T extends Builder<T>> {
+      protected abstract T self();
+
+      protected String id;
+      protected AllocationState allocationState;
+      protected int averageLoad;
+      protected String capabilities;
+      protected String clusterId;
+      protected String clusterName;
+      protected Host.ClusterType clusterType;
+      protected String cpuAllocated;
+      protected int cpuNumber;
+      protected int cpuSpeed;
+      protected String cpuUsed;
+      protected float cpuWithOverProvisioning;
+      protected Date created;
+      protected Date disconnected;
+      protected long diskSizeAllocated;
+      protected long diskSizeTotal;
+      protected String events;
+      protected boolean hasEnoughCapacity;
+      protected String hostTags;
+      protected String hypervisor;
+      protected String ipAddress;
+      protected boolean localStorageActive;
+      protected String jobId;
+      protected AsyncJob.Status jobStatus;
+      protected Date lastPinged;
+      protected String managementServerId;
+      protected long memoryAllocated;
+      protected long memoryTotal;
+      protected long memoryUsed;
+      protected String name;
+      protected long networkKbsRead;
+      protected long networkKbsWrite;
+      protected String osCategoryId;
+      protected String osCategoryName;
+      protected String podId;
+      protected String podName;
+      protected Date removed;
+      protected Host.State state;
+      protected Host.Type type;
+      protected String version;
+      protected String zoneId;
+      protected String zoneName;
+
+      /**
+       * @see Host#getId()
+       */
+      public T id(String id) {
          this.id = id;
-         return this;
+         return self();
       }
 
-      public Builder allocationState(AllocationState allocationState) {
+      /**
+       * @see Host#getAllocationState()
+       */
+      public T allocationState(AllocationState allocationState) {
          this.allocationState = allocationState;
-         return this;
+         return self();
       }
 
-      public Builder averageLoad(int averageLoad) {
+      /**
+       * @see Host#getAverageLoad()
+       */
+      public T averageLoad(int averageLoad) {
          this.averageLoad = averageLoad;
-         return this;
+         return self();
       }
 
-      public Builder capabilities(String capabilities) {
+      /**
+       * @see Host#getCapabilities()
+       */
+      public T capabilities(String capabilities) {
          this.capabilities = capabilities;
-         return this;
+         return self();
       }
 
-      public Builder clusterId(String clusterId) {
+      /**
+       * @see Host#getClusterId()
+       */
+      public T clusterId(String clusterId) {
          this.clusterId = clusterId;
-         return this;
+         return self();
       }
 
-      public Builder clusterName(String clusterName) {
+      /**
+       * @see Host#getClusterName()
+       */
+      public T clusterName(String clusterName) {
          this.clusterName = clusterName;
-         return this;
+         return self();
       }
 
-      public Builder clusterType(ClusterType clusterType) {
+      /**
+       * @see Host#getClusterType()
+       */
+      public T clusterType(Host.ClusterType clusterType) {
          this.clusterType = clusterType;
-         return this;
+         return self();
       }
 
-      public Builder cpuAllocated(String cpuAllocated) {
+      /**
+       * @see Host#getCpuAllocated()
+       */
+      public T cpuAllocated(String cpuAllocated) {
          this.cpuAllocated = cpuAllocated;
-         return this;
+         return self();
       }
 
-      public Builder cpuNumber(int cpuNumber) {
+      /**
+       * @see Host#getCpuNumber()
+       */
+      public T cpuNumber(int cpuNumber) {
          this.cpuNumber = cpuNumber;
-         return this;
+         return self();
       }
 
-      public Builder cpuSpeed(int cpuSpeed) {
+      /**
+       * @see Host#getCpuSpeed()
+       */
+      public T cpuSpeed(int cpuSpeed) {
          this.cpuSpeed = cpuSpeed;
-         return this;
+         return self();
       }
 
-      public Builder cpuUsed(String cpuUsed) {
+      /**
+       * @see Host#getCpuUsed()
+       */
+      public T cpuUsed(String cpuUsed) {
          this.cpuUsed = cpuUsed;
-         return this;
+         return self();
       }
 
-      public Builder cpuWithOverProvisioning(float cpuWithOverProvisioning) {
+      /**
+       * @see Host#getCpuWithOverProvisioning()
+       */
+      public T cpuWithOverProvisioning(float cpuWithOverProvisioning) {
          this.cpuWithOverProvisioning = cpuWithOverProvisioning;
-         return this;
+         return self();
       }
 
-      public Builder created(Date created) {
+      /**
+       * @see Host#getCreated()
+       */
+      public T created(Date created) {
          this.created = created;
-         return this;
+         return self();
       }
 
-      public Builder disconnected(Date disconnected) {
+      /**
+       * @see Host#getDisconnected()
+       */
+      public T disconnected(Date disconnected) {
          this.disconnected = disconnected;
-         return this;
+         return self();
       }
 
-      public Builder diskSizeAllocated(long diskSizeAllocated) {
+      /**
+       * @see Host#getDiskSizeAllocated()
+       */
+      public T diskSizeAllocated(long diskSizeAllocated) {
          this.diskSizeAllocated = diskSizeAllocated;
-         return this;
+         return self();
       }
 
-      public Builder diskSizeTotal(long diskSizeTotal) {
+      /**
+       * @see Host#getDiskSizeTotal()
+       */
+      public T diskSizeTotal(long diskSizeTotal) {
          this.diskSizeTotal = diskSizeTotal;
-         return this;
+         return self();
       }
 
-      public Builder events(String events) {
+      /**
+       * @see Host#getEvents()
+       */
+      public T events(String events) {
          this.events = events;
-         return this;
+         return self();
       }
 
-      public Builder hasEnoughCapacity(boolean hasEnoughCapacity) {
+      /**
+       * @see Host#isHasEnoughCapacity()
+       */
+      public T hasEnoughCapacity(boolean hasEnoughCapacity) {
          this.hasEnoughCapacity = hasEnoughCapacity;
-         return this;
+         return self();
       }
 
-      public Builder hostTags(String hostTags) {
+      /**
+       * @see Host#getHostTags()
+       */
+      public T hostTags(String hostTags) {
          this.hostTags = hostTags;
-         return this;
+         return self();
       }
 
-      public Builder hypervisor(String hypervisor) {
+      /**
+       * @see Host#getHypervisor()
+       */
+      public T hypervisor(String hypervisor) {
          this.hypervisor = hypervisor;
-         return this;
+         return self();
       }
 
-      public Builder ipAddress(String ipAddress) {
+      /**
+       * @see Host#getIpAddress()
+       */
+      public T ipAddress(String ipAddress) {
          this.ipAddress = ipAddress;
-         return this;
+         return self();
       }
 
-      public Builder localStorageActive(boolean localStorageActive) {
+      /**
+       * @see Host#isLocalStorageActive()
+       */
+      public T localStorageActive(boolean localStorageActive) {
          this.localStorageActive = localStorageActive;
-         return this;
+         return self();
       }
 
-      public Builder jobId(String jobId) {
+      /**
+       * @see Host#getJobId()
+       */
+      public T jobId(String jobId) {
          this.jobId = jobId;
-         return this;
+         return self();
       }
 
-      public Builder jobStatus(AsyncJob.Status jobStatus) {
+      /**
+       * @see Host#getJobStatus()
+       */
+      public T jobStatus(AsyncJob.Status jobStatus) {
          this.jobStatus = jobStatus;
-         return this;
+         return self();
       }
 
-      public Builder lastPinged(Date lastPinged) {
+      /**
+       * @see Host#getLastPinged()
+       */
+      public T lastPinged(Date lastPinged) {
          this.lastPinged = lastPinged;
-         return this;
+         return self();
       }
 
-      public Builder managementServerId(String managementServerId) {
+      /**
+       * @see Host#getManagementServerId()
+       */
+      public T managementServerId(String managementServerId) {
          this.managementServerId = managementServerId;
-         return this;
+         return self();
       }
 
-      public Builder memoryAllocated(long memoryAllocated) {
+      /**
+       * @see Host#getMemoryAllocated()
+       */
+      public T memoryAllocated(long memoryAllocated) {
          this.memoryAllocated = memoryAllocated;
-         return this;
+         return self();
       }
 
-      public Builder memoryTotal(long memoryTotal) {
+      /**
+       * @see Host#getMemoryTotal()
+       */
+      public T memoryTotal(long memoryTotal) {
          this.memoryTotal = memoryTotal;
-         return this;
+         return self();
       }
 
-      public Builder memoryUsed(long memoryUsed) {
+      /**
+       * @see Host#getMemoryUsed()
+       */
+      public T memoryUsed(long memoryUsed) {
          this.memoryUsed = memoryUsed;
-         return this;
+         return self();
       }
 
-      public Builder name(String name) {
+      /**
+       * @see Host#getName()
+       */
+      public T name(String name) {
          this.name = name;
-         return this;
+         return self();
       }
 
-      public Builder networkKbsRead(long networkKbsRead) {
+      /**
+       * @see Host#getNetworkKbsRead()
+       */
+      public T networkKbsRead(long networkKbsRead) {
          this.networkKbsRead = networkKbsRead;
-         return this;
+         return self();
       }
 
-      public Builder networkKbsWrite(long networkKbsWrite) {
+      /**
+       * @see Host#getNetworkKbsWrite()
+       */
+      public T networkKbsWrite(long networkKbsWrite) {
          this.networkKbsWrite = networkKbsWrite;
-         return this;
+         return self();
       }
 
-      public Builder osCategoryId(String osCategoryId) {
+      /**
+       * @see Host#getOsCategoryId()
+       */
+      public T osCategoryId(String osCategoryId) {
          this.osCategoryId = osCategoryId;
-         return this;
+         return self();
       }
 
-      public Builder osCategoryName(String osCategoryName) {
+      /**
+       * @see Host#getOsCategoryName()
+       */
+      public T osCategoryName(String osCategoryName) {
          this.osCategoryName = osCategoryName;
-         return this;
+         return self();
       }
 
-      public Builder podId(String podId) {
+      /**
+       * @see Host#getPodId()
+       */
+      public T podId(String podId) {
          this.podId = podId;
-         return this;
+         return self();
       }
 
-      public Builder podName(String podName) {
+      /**
+       * @see Host#getPodName()
+       */
+      public T podName(String podName) {
          this.podName = podName;
-         return this;
+         return self();
       }
 
-      public Builder removed(Date removed) {
+      /**
+       * @see Host#getRemoved()
+       */
+      public T removed(Date removed) {
          this.removed = removed;
-         return this;
+         return self();
       }
 
-      public Builder state(State state) {
+      /**
+       * @see Host#getState()
+       */
+      public T state(Host.State state) {
          this.state = state;
-         return this;
+         return self();
       }
 
-      public Builder type(Type type) {
+      /**
+       * @see Host#getType()
+       */
+      public T type(Host.Type type) {
          this.type = type;
-         return this;
+         return self();
       }
 
-      public Builder version(String version) {
+      /**
+       * @see Host#getVersion()
+       */
+      public T version(String version) {
          this.version = version;
-         return this;
+         return self();
       }
 
-      public Builder zoneId(String zoneId) {
+      /**
+       * @see Host#getZoneId()
+       */
+      public T zoneId(String zoneId) {
          this.zoneId = zoneId;
-         return this;
+         return self();
       }
 
-      public Builder zoneName(String zoneName) {
+      /**
+       * @see Host#getZoneName()
+       */
+      public T zoneName(String zoneName) {
          this.zoneName = zoneName;
-         return this;
+         return self();
       }
 
+
       public Host build() {
-         return new Host(id, allocationState, averageLoad, capabilities,
-            clusterId, clusterName, clusterType, cpuAllocated,
-            cpuNumber, cpuSpeed, cpuUsed, cpuWithOverProvisioning,
-            created, disconnected, diskSizeAllocated, diskSizeTotal,
-            events, hasEnoughCapacity, hostTags, hypervisor,
-            ipAddress, localStorageActive, jobId, jobStatus,
-            lastPinged, managementServerId, memoryAllocated, memoryTotal,
-            memoryUsed, name, networkKbsRead, networkKbsWrite,
-            osCategoryId, osCategoryName, podId, podName, removed,
-            state, type, version, zoneId, zoneName);
+         return new Host(id, allocationState, averageLoad, capabilities, clusterId, clusterName, clusterType, cpuAllocated, cpuNumber, cpuSpeed, cpuUsed, cpuWithOverProvisioning, created, disconnected, diskSizeAllocated, diskSizeTotal, events, hasEnoughCapacity, hostTags, hypervisor, ipAddress, localStorageActive, jobId, jobStatus, lastPinged, managementServerId, memoryAllocated, memoryTotal, memoryUsed, name, networkKbsRead, networkKbsWrite, osCategoryId, osCategoryName, podId, podName, removed, state, type, version, zoneId, zoneName);
+      }
+
+      public T fromHost(Host in) {
+         return this
+               .id(in.getId())
+               .allocationState(in.getAllocationState())
+               .averageLoad(in.getAverageLoad())
+               .capabilities(in.getCapabilities())
+               .clusterId(in.getClusterId())
+               .clusterName(in.getClusterName())
+               .clusterType(in.getClusterType())
+               .cpuAllocated(in.getCpuAllocated())
+               .cpuNumber(in.getCpuNumber())
+               .cpuSpeed(in.getCpuSpeed())
+               .cpuUsed(in.getCpuUsed())
+               .cpuWithOverProvisioning(in.getCpuWithOverProvisioning())
+               .created(in.getCreated())
+               .disconnected(in.getDisconnected())
+               .diskSizeAllocated(in.getDiskSizeAllocated())
+               .diskSizeTotal(in.getDiskSizeTotal())
+               .events(in.getEvents())
+               .hasEnoughCapacity(in.isHasEnoughCapacity())
+               .hostTags(in.getHostTags())
+               .hypervisor(in.getHypervisor())
+               .ipAddress(in.getIpAddress())
+               .localStorageActive(in.isLocalStorageActive())
+               .jobId(in.getJobId())
+               .jobStatus(in.getJobStatus())
+               .lastPinged(in.getLastPinged())
+               .managementServerId(in.getManagementServerId())
+               .memoryAllocated(in.getMemoryAllocated())
+               .memoryTotal(in.getMemoryTotal())
+               .memoryUsed(in.getMemoryUsed())
+               .name(in.getName())
+               .networkKbsRead(in.getNetworkKbsRead())
+               .networkKbsWrite(in.getNetworkKbsWrite())
+               .osCategoryId(in.getOsCategoryId())
+               .osCategoryName(in.getOsCategoryName())
+               .podId(in.getPodId())
+               .podName(in.getPodName())
+               .removed(in.getRemoved())
+               .state(in.getState())
+               .type(in.getType())
+               .version(in.getVersion())
+               .zoneId(in.getZoneId())
+               .zoneName(in.getZoneName());
       }
    }
 
-   private String id;
-   @SerializedName("allocationstate")
-   private AllocationState allocationState;
-   @SerializedName("averageload")
-   private int averageLoad;
-   @SerializedName("capabilities")
-   private String capabilities;
-   @SerializedName("clusterid")
-   private String clusterId;
-   @SerializedName("clustername")
-   private String clusterName;
-   @SerializedName("clustertype")
-   private ClusterType clusterType;
-   @SerializedName("cpuallocated")
-   private String cpuAllocated;
-   @SerializedName("cpunumber")
-   private int cpuNumber;
-   @SerializedName("cpuspeed")
-   private int cpuSpeed;
-   @SerializedName("cpuused")
-   private String cpuUsed;
-   @SerializedName("cpuwithoverprovisioning")
-   private float cpuWithOverProvisioning;
-   private Date created;
-   private Date disconnected;
-   @SerializedName("disksizeallocated")
-   private long diskSizeAllocated;
-   @SerializedName("disksizetotal")
-   private long diskSizeTotal;
-   private String events;
-   @SerializedName("hasenoughcapacity")
-   private boolean hasEnoughCapacity;
-   @SerializedName("hosttags")
-   private String hostTags;
-   private String hypervisor;
-   @SerializedName("ipaddress")
-   private String ipAddress;
-   @SerializedName("islocalstorageactive")
-   private boolean localStorageActive;
-   @SerializedName("jobid")
-   private String jobId;
-   @SerializedName("jobstatus")
-   private AsyncJob.Status jobStatus;
-   @SerializedName("lastpinged")
-   private Date lastPinged;
-   @SerializedName("managementserverid")
-   private String managementServerId;
-   @SerializedName("memoryallocated")
-   private long memoryAllocated;
-   @SerializedName("memorytotal")
-   private long memoryTotal;
-   @SerializedName("memoryused")
-   private long memoryUsed;
-   private String name;
-   @SerializedName("networkkbsread")
-   private long networkKbsRead;
-   @SerializedName("networkkbswrite")
-   private long networkKbsWrite;
-   @SerializedName("oscategoryid")
-   private String osCategoryId;
-   @SerializedName("oscategoryname")
-   private String osCategoryName;
-   @SerializedName("podid")
-   private String podId;
-   @SerializedName("podname")
-   private String podName;
-   private Date removed;
-   private State state;
-   private Type type;
-   private String version;
-   @SerializedName("zoneid")
-   private String zoneId;
-   @SerializedName("zonename")
-   private String zoneName;
-
-   /* exists for the deserializer, only */
-   Host() {
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
    }
 
-   public Host(String id, AllocationState allocationState, int averageLoad, String capabilities,
-               String clusterId, String clusterName, ClusterType clusterType, String cpuAllocated,
-               int cpuNumber, int cpuSpeed, String cpuUsed, float cpuWithOverProvisioning,
-               Date created, Date disconnected, long diskSizeAllocated, long diskSizeTotal,
-               String events, boolean hasEnoughCapacity, String hostTags, String hypervisor,
-               String ipAddress, boolean localStorageActive, String jobId, AsyncJob.Status jobStatus,
-               Date lastPinged, String managementServerId, long memoryAllocated, long memoryTotal,
-               long memoryUsed, String name, long networkKbsRead, long networkKbsWrite,
-               String osCategoryId, String osCategoryName, String podId, String podName, Date removed,
-               State state, Type type, String version, String zoneId, String zoneName) {
-      this.id = id;
+   private final String id;
+   @Named("allocationstate")
+   private final AllocationState allocationState;
+   @Named("averageload")
+   private final int averageLoad;
+   private final String capabilities;
+   @Named("clusterid")
+   private final String clusterId;
+   @Named("clustername")
+   private final String clusterName;
+   @Named("clustertype")
+   private final Host.ClusterType clusterType;
+   @Named("cpuallocated")
+   private final String cpuAllocated;
+   @Named("cpunumber")
+   private final int cpuNumber;
+   @Named("cpuspeed")
+   private final int cpuSpeed;
+   @Named("cpuused")
+   private final String cpuUsed;
+   @Named("cpuwithoverprovisioning")
+   private final float cpuWithOverProvisioning;
+   private final Date created;
+   private final Date disconnected;
+   @Named("disksizeallocated")
+   private final long diskSizeAllocated;
+   @Named("disksizetotal")
+   private final long diskSizeTotal;
+   private final String events;
+   @Named("hasenoughcapacity")
+   private final boolean hasEnoughCapacity;
+   @Named("hosttags")
+   private final String hostTags;
+   private final String hypervisor;
+   @Named("ipaddress")
+   private final String ipAddress;
+   @Named("islocalstorageactive")
+   private final boolean localStorageActive;
+   @Named("jobid")
+   private final String jobId;
+   @Named("jobstatus")
+   private final AsyncJob.Status jobStatus;
+   @Named("lastpinged")
+   private final Date lastPinged;
+   @Named("managementserverid")
+   private final String managementServerId;
+   @Named("memoryallocated")
+   private final long memoryAllocated;
+   @Named("memorytotal")
+   private final long memoryTotal;
+   @Named("memoryused")
+   private final long memoryUsed;
+   private final String name;
+   @Named("networkkbsread")
+   private final long networkKbsRead;
+   @Named("networkkbswrite")
+   private final long networkKbsWrite;
+   @Named("oscategoryid")
+   private final String osCategoryId;
+   @Named("oscategoryname")
+   private final String osCategoryName;
+   @Named("podid")
+   private final String podId;
+   @Named("podname")
+   private final String podName;
+   private final Date removed;
+   private final Host.State state;
+   private final Host.Type type;
+   private final String version;
+   @Named("zoneid")
+   private final String zoneId;
+   @Named("zonename")
+   private final String zoneName;
+
+   @ConstructorProperties({
+         "id", "allocationstate", "averageload", "capabilities", "clusterid", "clustername", "clustertype", "cpuallocated", "cpunumber", "cpuspeed", "cpuused", "cpuwithoverprovisioning", "created", "disconnected", "disksizeallocated", "disksizetotal", "events", "hasenoughcapacity", "hosttags", "hypervisor", "ipaddress", "islocalstorageactive", "jobid", "jobstatus", "lastpinged", "managementserverid", "memoryallocated", "memorytotal", "memoryused", "name", "networkkbsread", "networkkbswrite", "oscategoryid", "oscategoryname", "podid", "podname", "removed", "state", "type", "version", "zoneid", "zonename"
+   })
+   protected Host(String id, @Nullable AllocationState allocationState, int averageLoad, @Nullable String capabilities,
+                  @Nullable String clusterId, @Nullable String clusterName, @Nullable Host.ClusterType clusterType,
+                  @Nullable String cpuAllocated, int cpuNumber, int cpuSpeed, @Nullable String cpuUsed,
+                  float cpuWithOverProvisioning, @Nullable Date created, @Nullable Date disconnected, long diskSizeAllocated,
+                  long diskSizeTotal, @Nullable String events, boolean hasEnoughCapacity, @Nullable String hostTags,
+                  @Nullable String hypervisor, @Nullable String ipAddress, boolean localStorageActive, @Nullable String jobId,
+                  @Nullable AsyncJob.Status jobStatus, @Nullable Date lastPinged, @Nullable String managementServerId,
+                  long memoryAllocated, long memoryTotal, long memoryUsed, @Nullable String name, long networkKbsRead, long networkKbsWrite,
+                  @Nullable String osCategoryId, @Nullable String osCategoryName, @Nullable String podId, @Nullable String podName,
+                  @Nullable Date removed, @Nullable Host.State state, @Nullable Host.Type type, @Nullable String version, @Nullable String zoneId,
+                  @Nullable String zoneName) {
+      this.id = checkNotNull(id, "id");
       this.allocationState = allocationState;
       this.averageLoad = averageLoad;
       this.capabilities = capabilities;
@@ -522,282 +704,275 @@
    }
 
    public String getId() {
-      return id;
+      return this.id;
    }
 
+   @Nullable
    public AllocationState getAllocationState() {
-      return allocationState;
+      return this.allocationState;
    }
 
    public int getAverageLoad() {
-      return averageLoad;
+      return this.averageLoad;
    }
 
+   @Nullable
    public String getCapabilities() {
-      return capabilities;
+      return this.capabilities;
    }
 
+   @Nullable
    public String getClusterId() {
-      return clusterId;
+      return this.clusterId;
    }
 
+   @Nullable
    public String getClusterName() {
-      return clusterName;
+      return this.clusterName;
    }
 
-   public ClusterType getClusterType() {
-      return clusterType;
+   @Nullable
+   public Host.ClusterType getClusterType() {
+      return this.clusterType;
    }
 
+   @Nullable
    public String getCpuAllocated() {
-      return cpuAllocated;
+      return this.cpuAllocated;
    }
 
    public int getCpuNumber() {
-      return cpuNumber;
+      return this.cpuNumber;
    }
 
    public int getCpuSpeed() {
-      return cpuSpeed;
+      return this.cpuSpeed;
    }
 
+   @Nullable
    public String getCpuUsed() {
-      return cpuUsed;
+      return this.cpuUsed;
    }
 
    public float getCpuWithOverProvisioning() {
-      return cpuWithOverProvisioning;
+      return this.cpuWithOverProvisioning;
    }
 
+   @Nullable
    public Date getCreated() {
-      return created;
+      return this.created;
    }
 
+   @Nullable
    public Date getDisconnected() {
-      return disconnected;
+      return this.disconnected;
    }
 
    public long getDiskSizeAllocated() {
-      return diskSizeAllocated;
+      return this.diskSizeAllocated;
    }
 
    public long getDiskSizeTotal() {
-      return diskSizeTotal;
+      return this.diskSizeTotal;
    }
 
+   @Nullable
    public String getEvents() {
-      return events;
+      return this.events;
    }
 
    public boolean isHasEnoughCapacity() {
-      return hasEnoughCapacity;
+      return this.hasEnoughCapacity;
    }
 
+   @Nullable
    public String getHostTags() {
-      return hostTags;
+      return this.hostTags;
    }
 
+   @Nullable
    public String getHypervisor() {
-      return hypervisor;
+      return this.hypervisor;
    }
 
+   @Nullable
    public String getIpAddress() {
-      return ipAddress;
+      return this.ipAddress;
    }
 
    public boolean isLocalStorageActive() {
-      return localStorageActive;
+      return this.localStorageActive;
    }
 
+   @Nullable
    public String getJobId() {
-      return jobId;
+      return this.jobId;
    }
 
+   @Nullable
    public AsyncJob.Status getJobStatus() {
-      return jobStatus;
+      return this.jobStatus;
    }
 
+   @Nullable
    public Date getLastPinged() {
-      return lastPinged;
+      return this.lastPinged;
    }
 
+   @Nullable
    public String getManagementServerId() {
-      return managementServerId;
+      return this.managementServerId;
    }
 
    public long getMemoryAllocated() {
-      return memoryAllocated;
+      return this.memoryAllocated;
    }
 
    public long getMemoryTotal() {
-      return memoryTotal;
+      return this.memoryTotal;
    }
 
    public long getMemoryUsed() {
-      return memoryUsed;
+      return this.memoryUsed;
    }
-
+   
+   @Nullable
    public String getName() {
-      return name;
+      return this.name;
    }
 
    public long getNetworkKbsRead() {
-      return networkKbsRead;
+      return this.networkKbsRead;
    }
 
    public long getNetworkKbsWrite() {
-      return networkKbsWrite;
+      return this.networkKbsWrite;
    }
 
+   @Nullable
    public String getOsCategoryId() {
-      return osCategoryId;
+      return this.osCategoryId;
    }
 
+   @Nullable
    public String getOsCategoryName() {
-      return osCategoryName;
+      return this.osCategoryName;
    }
 
+   @Nullable
    public String getPodId() {
-      return podId;
+      return this.podId;
    }
 
+   @Nullable
    public String getPodName() {
-      return podName;
+      return this.podName;
    }
 
+   @Nullable
    public Date getRemoved() {
-      return removed;
+      return this.removed;
    }
 
-   public State getState() {
-      return state;
+   @Nullable
+   public Host.State getState() {
+      return this.state;
    }
 
-   public Type getType() {
-      return type;
+   @Nullable
+   public Host.Type getType() {
+      return this.type;
    }
 
+   @Nullable
    public String getVersion() {
-      return version;
+      return this.version;
    }
 
+   @Nullable
    public String getZoneId() {
-      return zoneId;
+      return this.zoneId;
    }
 
+   @Nullable
    public String getZoneName() {
-      return zoneName;
-   }
-
-   @Override
-   public boolean equals(Object o) {
-      if (this == o) return true;
-      if (o == null || getClass() != o.getClass()) return false;
-
-      Host that = (Host) o;
-
-      if (!Objects.equal(averageLoad, that.averageLoad)) return false;
-      if (!Objects.equal(clusterId, that.clusterId)) return false;
-      if (!Objects.equal(cpuNumber, that.cpuNumber)) return false;
-      if (!Objects.equal(cpuSpeed, that.cpuSpeed)) return false;
-      if (!Objects.equal(cpuWithOverProvisioning, that.cpuWithOverProvisioning)) return false;
-      if (!Objects.equal(diskSizeAllocated, that.diskSizeAllocated)) return false;
-      if (!Objects.equal(diskSizeTotal, that.diskSizeTotal)) return false;
-      if (!Objects.equal(hasEnoughCapacity, that.hasEnoughCapacity)) return false;
-      if (!Objects.equal(id, that.id)) return false;
-      if (!Objects.equal(jobId, that.jobId)) return false;
-      if (!Objects.equal(localStorageActive, that.localStorageActive)) return false;
-      if (!Objects.equal(managementServerId, that.managementServerId)) return false;
-      if (!Objects.equal(memoryAllocated, that.memoryAllocated)) return false;
-      if (!Objects.equal(memoryTotal, that.memoryTotal)) return false;
-      if (!Objects.equal(memoryUsed, that.memoryUsed)) return false;
-      if (!Objects.equal(networkKbsRead, that.networkKbsRead)) return false;
-      if (!Objects.equal(networkKbsWrite, that.networkKbsWrite)) return false;
-      if (!Objects.equal(osCategoryId, that.osCategoryId)) return false;
-      if (!Objects.equal(osCategoryName, that.osCategoryName)) return false;
-      if (!Objects.equal(podId, that.podId)) return false;
-      if (!Objects.equal(zoneId, that.zoneId)) return false;
-      if (!Objects.equal(allocationState, that.allocationState)) return false;
-      if (!Objects.equal(capabilities, that.capabilities)) return false;
-      if (!Objects.equal(clusterName, that.clusterName)) return false;
-      if (!Objects.equal(clusterType, that.clusterType)) return false;
-      if (!Objects.equal(cpuAllocated, that.cpuAllocated)) return false;
-      if (!Objects.equal(cpuUsed, that.cpuUsed)) return false;
-      if (!Objects.equal(created, that.created)) return false;
-      if (!Objects.equal(disconnected, that.disconnected)) return false;
-      if (!Objects.equal(events, that.events)) return false;
-      if (!Objects.equal(hostTags, that.hostTags)) return false;
-      if (!Objects.equal(hypervisor, that.hypervisor)) return false;
-      if (!Objects.equal(ipAddress, that.ipAddress)) return false;
-      if (!Objects.equal(jobStatus, that.jobStatus)) return false;
-      if (!Objects.equal(lastPinged, that.lastPinged)) return false;
-      if (!Objects.equal(name, that.name)) return false;
-      if (!Objects.equal(podName, that.podName)) return false;
-      if (!Objects.equal(removed, that.removed)) return false;
-      if (!Objects.equal(state, that.state)) return false;
-      if (!Objects.equal(type, that.type)) return false;
-      if (!Objects.equal(version, that.version)) return false;
-      if (!Objects.equal(zoneName, that.zoneName)) return false;
-
-      return true;
+      return this.zoneName;
    }
 
    @Override
    public int hashCode() {
-       return Objects.hashCode(averageLoad, clusterId, cpuNumber, cpuSpeed, cpuWithOverProvisioning, diskSizeAllocated,
-                               diskSizeTotal, hasEnoughCapacity, id, jobId, localStorageActive, managementServerId,
-                               memoryAllocated, memoryTotal, memoryUsed, networkKbsRead, networkKbsWrite, osCategoryId,
-                               osCategoryName, podId, zoneId, allocationState, capabilities, clusterName, clusterType,
-                               cpuAllocated, cpuUsed, created, disconnected, events, hostTags, hypervisor, ipAddress,
-                               jobStatus, lastPinged, name, podName, removed, state, type, version, zoneName);
+      return Objects.hashCode(id, allocationState, averageLoad, capabilities, clusterId, clusterName, clusterType, cpuAllocated, cpuNumber, cpuSpeed, cpuUsed, cpuWithOverProvisioning, created, disconnected, diskSizeAllocated, diskSizeTotal, events, hasEnoughCapacity, hostTags, hypervisor, ipAddress, localStorageActive, jobId, jobStatus, lastPinged, managementServerId, memoryAllocated, memoryTotal, memoryUsed, name, networkKbsRead, networkKbsWrite, osCategoryId, osCategoryName, podId, podName, removed, state, type, version, zoneId, zoneName);
    }
-   
+
+   @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      Host that = Host.class.cast(obj);
+      return Objects.equal(this.id, that.id)
+            && Objects.equal(this.allocationState, that.allocationState)
+            && Objects.equal(this.averageLoad, that.averageLoad)
+            && Objects.equal(this.capabilities, that.capabilities)
+            && Objects.equal(this.clusterId, that.clusterId)
+            && Objects.equal(this.clusterName, that.clusterName)
+            && Objects.equal(this.clusterType, that.clusterType)
+            && Objects.equal(this.cpuAllocated, that.cpuAllocated)
+            && Objects.equal(this.cpuNumber, that.cpuNumber)
+            && Objects.equal(this.cpuSpeed, that.cpuSpeed)
+            && Objects.equal(this.cpuUsed, that.cpuUsed)
+            && Objects.equal(this.cpuWithOverProvisioning, that.cpuWithOverProvisioning)
+            && Objects.equal(this.created, that.created)
+            && Objects.equal(this.disconnected, that.disconnected)
+            && Objects.equal(this.diskSizeAllocated, that.diskSizeAllocated)
+            && Objects.equal(this.diskSizeTotal, that.diskSizeTotal)
+            && Objects.equal(this.events, that.events)
+            && Objects.equal(this.hasEnoughCapacity, that.hasEnoughCapacity)
+            && Objects.equal(this.hostTags, that.hostTags)
+            && Objects.equal(this.hypervisor, that.hypervisor)
+            && Objects.equal(this.ipAddress, that.ipAddress)
+            && Objects.equal(this.localStorageActive, that.localStorageActive)
+            && Objects.equal(this.jobId, that.jobId)
+            && Objects.equal(this.jobStatus, that.jobStatus)
+            && Objects.equal(this.lastPinged, that.lastPinged)
+            && Objects.equal(this.managementServerId, that.managementServerId)
+            && Objects.equal(this.memoryAllocated, that.memoryAllocated)
+            && Objects.equal(this.memoryTotal, that.memoryTotal)
+            && Objects.equal(this.memoryUsed, that.memoryUsed)
+            && Objects.equal(this.name, that.name)
+            && Objects.equal(this.networkKbsRead, that.networkKbsRead)
+            && Objects.equal(this.networkKbsWrite, that.networkKbsWrite)
+            && Objects.equal(this.osCategoryId, that.osCategoryId)
+            && Objects.equal(this.osCategoryName, that.osCategoryName)
+            && Objects.equal(this.podId, that.podId)
+            && Objects.equal(this.podName, that.podName)
+            && Objects.equal(this.removed, that.removed)
+            && Objects.equal(this.state, that.state)
+            && Objects.equal(this.type, that.type)
+            && Objects.equal(this.version, that.version)
+            && Objects.equal(this.zoneId, that.zoneId)
+            && Objects.equal(this.zoneName, that.zoneName);
+   }
+
+   protected ToStringHelper string() {
+      return Objects.toStringHelper(this)
+            .add("id", id).add("allocationState", allocationState).add("averageLoad", averageLoad)
+            .add("capabilities", capabilities).add("clusterId", clusterId).add("clusterName", clusterName)
+            .add("clusterType", clusterType).add("cpuAllocated", cpuAllocated).add("cpuNumber", cpuNumber)
+            .add("cpuSpeed", cpuSpeed).add("cpuUsed", cpuUsed).add("cpuWithOverProvisioning", cpuWithOverProvisioning)
+            .add("created", created).add("disconnected", disconnected).add("diskSizeAllocated", diskSizeAllocated)
+            .add("diskSizeTotal", diskSizeTotal).add("events", events).add("hasEnoughCapacity", hasEnoughCapacity)
+            .add("hostTags", hostTags).add("hypervisor", hypervisor).add("ipAddress", ipAddress)
+            .add("localStorageActive", localStorageActive).add("jobId", jobId).add("jobStatus", jobStatus)
+            .add("lastPinged", lastPinged).add("managementServerId", managementServerId).add("memoryAllocated", memoryAllocated)
+            .add("memoryTotal", memoryTotal).add("memoryUsed", memoryUsed).add("name", name).add("networkKbsRead", networkKbsRead)
+            .add("networkKbsWrite", networkKbsWrite).add("osCategoryId", osCategoryId).add("osCategoryName", osCategoryName)
+            .add("podId", podId).add("podName", podName).add("removed", removed).add("state", state).add("type", type)
+            .add("version", version).add("zoneId", zoneId).add("zoneName", zoneName);
+   }
+
    @Override
    public String toString() {
-      return "Host{" +
-         "id=" + id +
-         ", allocationState=" + allocationState +
-         ", averageLoad=" + averageLoad +
-         ", capabilities='" + capabilities + '\'' +
-         ", clusterId=" + clusterId +
-         ", clusterName='" + clusterName + '\'' +
-         ", clusterType=" + clusterType +
-         ", cpuAllocated='" + cpuAllocated + '\'' +
-         ", cpuNumber=" + cpuNumber +
-         ", cpuSpeed=" + cpuSpeed +
-         ", cpuUsed='" + cpuUsed + '\'' +
-         ", cpuWithOverProvisioning=" + cpuWithOverProvisioning +
-         ", created=" + created +
-         ", disconnected=" + disconnected +
-         ", diskSizeAllocated=" + diskSizeAllocated +
-         ", diskSizeTotal=" + diskSizeTotal +
-         ", events='" + events + '\'' +
-         ", hasEnoughCapacity=" + hasEnoughCapacity +
-         ", hostTags='" + hostTags + '\'' +
-         ", hypervisor='" + hypervisor + '\'' +
-         ", ipAddress='" + ipAddress + '\'' +
-         ", localStorageActive=" + localStorageActive +
-         ", jobId=" + jobId +
-         ", jobStatus=" + jobStatus +
-         ", lastPinged=" + lastPinged +
-         ", managementServerId=" + managementServerId +
-         ", memoryAllocated=" + memoryAllocated +
-         ", memoryTotal=" + memoryTotal +
-         ", memoryUsed=" + memoryUsed +
-         ", name='" + name + '\'' +
-         ", networkKbsRead=" + networkKbsRead +
-         ", networkKbsWrite=" + networkKbsWrite +
-         ", osCategoryId=" + osCategoryId +
-         ", osCategoryName=" + osCategoryName +
-         ", podId=" + podId +
-         ", podName='" + podName + '\'' +
-         ", removed=" + removed +
-         ", state=" + state +
-         ", type=" + type +
-         ", version='" + version + '\'' +
-         ", zoneId=" + zoneId +
-         ", zoneName='" + zoneName + '\'' +
-         '}';
+      return string().toString();
    }
 
    @Override
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/IPForwardingRule.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/IPForwardingRule.java
index 46a0b11..f11ff41 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/IPForwardingRule.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/IPForwardingRule.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -18,151 +18,236 @@
  */
 package org.jclouds.cloudstack.domain;
 
-import java.util.Collections;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.beans.ConstructorProperties;
 import java.util.Set;
 
+import javax.inject.Named;
+
+import org.jclouds.javax.annotation.Nullable;
+
 import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
 import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Sets;
-import com.google.gson.annotations.SerializedName;
 
 /**
+ * Class IPForwardingRule
+ * 
  * @author Adrian Cole
- */
+*/
 public class IPForwardingRule implements Comparable<IPForwardingRule> {
-   public static Builder builder() {
-      return new Builder();
+
+   public static Builder<?> builder() { 
+      return new ConcreteBuilder();
+   }
+   
+   public Builder<?> toBuilder() { 
+      return new ConcreteBuilder().fromIPForwardingRule(this);
    }
 
-   public static class Builder {
-      private String id;
-      private String IPAddress;
-      private String IPAddressId;
-      private int startPort;
-      private String protocol;
-      public int endPort;
-      private String state;
-      private String virtualMachineDisplayName;
-      public String virtualMachineId;
-      private String virtualMachineName;
-      private Set<String> CIDRs = ImmutableSet.of();
-      private int privateEndPort;
-      private int publicEndPort;
-      public int publicPort;
+   public static abstract class Builder<T extends Builder<T>>  {
+      protected abstract T self();
 
-      public Builder id(String id) {
+      protected String id;
+      protected String IPAddress;
+      protected String IPAddressId;
+      protected int startPort;
+      protected String protocol;
+      protected int endPort;
+      protected String state;
+      protected String virtualMachineDisplayName;
+      protected String virtualMachineId;
+      protected String virtualMachineName;
+      protected int publicPort;
+      protected Set<String> CIDRs = ImmutableSet.of();
+      protected int privateEndPort;
+      protected int publicEndPort;
+   
+      /** 
+       * @see IPForwardingRule#getId()
+       */
+      public T id(String id) {
          this.id = id;
-         return this;
+         return self();
       }
 
-      public Builder IPAddress(String IPAddress) {
+      /** 
+       * @see IPForwardingRule#getIPAddress()
+       */
+      public T IPAddress(String IPAddress) {
          this.IPAddress = IPAddress;
-         return this;
+         return self();
       }
 
-      public Builder IPAddressId(String IPAddressId) {
+      /** 
+       * @see IPForwardingRule#getIPAddressId()
+       */
+      public T IPAddressId(String IPAddressId) {
          this.IPAddressId = IPAddressId;
-         return this;
+         return self();
       }
 
-      public Builder startPort(int startPort) {
+      /** 
+       * @see IPForwardingRule#getStartPort()
+       */
+      public T startPort(int startPort) {
          this.startPort = startPort;
-         return this;
+         return self();
       }
 
-      public Builder protocol(String protocol) {
+      /** 
+       * @see IPForwardingRule#getProtocol()
+       */
+      public T protocol(String protocol) {
          this.protocol = protocol;
-         return this;
+         return self();
       }
 
-      public Builder endPort(int endPort) {
+      /** 
+       * @see IPForwardingRule#getEndPort()
+       */
+      public T endPort(int endPort) {
          this.endPort = endPort;
-         return this;
+         return self();
       }
 
-      public Builder state(String state) {
+      /** 
+       * @see IPForwardingRule#getState()
+       */
+      public T state(String state) {
          this.state = state;
-         return this;
+         return self();
       }
 
-      public Builder virtualMachineDisplayName(String virtualMachineDisplayName) {
+      /** 
+       * @see IPForwardingRule#getVirtualMachineDisplayName()
+       */
+      public T virtualMachineDisplayName(String virtualMachineDisplayName) {
          this.virtualMachineDisplayName = virtualMachineDisplayName;
-         return this;
+         return self();
       }
 
-      public Builder virtualMachineId(String virtualMachineId) {
+      /** 
+       * @see IPForwardingRule#getVirtualMachineId()
+       */
+      public T virtualMachineId(String virtualMachineId) {
          this.virtualMachineId = virtualMachineId;
-         return this;
+         return self();
       }
 
-      public Builder virtualMachineName(String virtualMachineName) {
+      /** 
+       * @see IPForwardingRule#getVirtualMachineName()
+       */
+      public T virtualMachineName(String virtualMachineName) {
          this.virtualMachineName = virtualMachineName;
-         return this;
+         return self();
       }
 
-      public Builder publicPort(int publicPort) {
+      /** 
+       * @see IPForwardingRule#getPublicPort()
+       */
+      public T publicPort(int publicPort) {
          this.publicPort = publicPort;
-         return this;
+         return self();
       }
 
-      public Builder CIDRs(Set<String> CIDRs) {
-         this.CIDRs = CIDRs;
-         return this;
+      /** 
+       * @see IPForwardingRule#getCIDRs()
+       */
+      public T CIDRs(Set<String> CIDRs) {
+         this.CIDRs = ImmutableSet.copyOf(checkNotNull(CIDRs, "CIDRs"));      
+         return self();
       }
 
-      public Builder privateEndPort(int privateEndPort) {
+      public T CIDRs(String... in) {
+         return CIDRs(ImmutableSet.copyOf(in));
+      }
+
+      /** 
+       * @see IPForwardingRule#getPrivateEndPort()
+       */
+      public T privateEndPort(int privateEndPort) {
          this.privateEndPort = privateEndPort;
-         return this;
+         return self();
       }
 
-      public Builder publicEndPort(int publicEndPort) {
+      /** 
+       * @see IPForwardingRule#getPublicEndPort()
+       */
+      public T publicEndPort(int publicEndPort) {
          this.publicEndPort = publicEndPort;
-         return this;
+         return self();
       }
 
       public IPForwardingRule build() {
-         return new IPForwardingRule(id, IPAddress, IPAddressId, startPort, protocol, endPort, state,
-               virtualMachineDisplayName, virtualMachineId, virtualMachineName, publicEndPort, publicPort, CIDRs, privateEndPort);
+         return new IPForwardingRule(id, IPAddress, IPAddressId, startPort, protocol, endPort, state, virtualMachineDisplayName,
+               virtualMachineId, virtualMachineName, publicPort, CIDRs, privateEndPort, publicEndPort);
+      }
+      
+      public T fromIPForwardingRule(IPForwardingRule in) {
+         return this
+                  .id(in.getId())
+                  .IPAddress(in.getIPAddress())
+                  .IPAddressId(in.getIPAddressId())
+                  .startPort(in.getStartPort())
+                  .protocol(in.getProtocol())
+                  .endPort(in.getEndPort())
+                  .state(in.getState())
+                  .virtualMachineDisplayName(in.getVirtualMachineDisplayName())
+                  .virtualMachineId(in.getVirtualMachineId())
+                  .virtualMachineName(in.getVirtualMachineName())
+                  .publicPort(in.getPublicPort())
+                  .CIDRs(in.getCIDRs())
+                  .privateEndPort(in.getPrivateEndPort())
+                  .publicEndPort(in.getPublicEndPort());
       }
    }
 
-   private String id;
-   @SerializedName("ipaddress")
-   private String IPAddress;
-   @SerializedName("ipaddressid")
-   private String IPAddressId;
-   @SerializedName("startport")
-   private int startPort;
-   private String protocol;
-   @SerializedName("endport")
-   public int endPort;
-   private String state;
-   @SerializedName("virtualmachinedisplayname")
-   private String virtualMachineDisplayName;
-   @SerializedName("virtualmachineid")
-   public String virtualMachineId;
-   @SerializedName("virtualmachinename")
-   private String virtualMachineName;
-   @SerializedName("publicport")
-   public int publicPort;
-   @SerializedName("cidrlist")
-   private Set<String> CIDRs;
-   @SerializedName("privateendport")
-   private int privateEndPort;
-   @SerializedName("publicendport")
-   private int publicEndPort;
-
-   // for deserializer
-   IPForwardingRule() {
-
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
    }
 
-   public IPForwardingRule(String id, String iPAddress, String iPAddressId, int startPort, String protocol, int endPort,
-                           String state, String virtualMachineDisplayName, String virtualMachineId, String virtualMachineName,
-                           int publicEndPort, int publicPort, Set<String> CIDRs, int privateEndPort) {
-      this.id = id;
-      this.IPAddress = iPAddress;
-      this.IPAddressId = iPAddressId;
+   private final String id;
+   @Named("ipaddress")
+   private final String IPAddress;
+   @Named("ipaddressid")
+   private final String IPAddressId;
+   @Named("startport")
+   private final int startPort;
+   private final String protocol;
+   @Named("endport")
+   private final int endPort;
+   private final String state;
+   @Named("virtualmachinedisplayname")
+   private final String virtualMachineDisplayName;
+   @Named("virtualmachineid")
+   private final String virtualMachineId;
+   @Named("virtualmachinename")
+   private final String virtualMachineName;
+   @Named("publicport")
+   private final int publicPort;
+   @Named("cidrlist")
+   private final Set<String> CIDRs;
+   @Named("privateendport")
+   private final int privateEndPort;
+   @Named("publicendport")
+   private final int publicEndPort;
+
+   @ConstructorProperties({
+      "id", "ipaddress", "ipaddressid", "startport", "protocol", "endport", "state", "virtualmachinedisplayname",
+         "virtualmachineid", "virtualmachinename", "publicport", "cidrlist", "privateendport", "publicendport"
+   })
+   protected IPForwardingRule(String id, String IPAddress, String IPAddressId, int startPort, @Nullable String protocol,
+                              int endPort, @Nullable String state, @Nullable String virtualMachineDisplayName,
+                              @Nullable String virtualMachineId, @Nullable String virtualMachineName, int publicPort,
+                              @Nullable Set<String> CIDRs, int privateEndPort, int publicEndPort) {
+      this.id = checkNotNull(id, "id");
+      this.IPAddress = IPAddress;
+      this.IPAddressId = IPAddressId;
       this.startPort = startPort;
       this.protocol = protocol;
       this.endPort = endPort;
@@ -170,153 +255,158 @@
       this.virtualMachineDisplayName = virtualMachineDisplayName;
       this.virtualMachineId = virtualMachineId;
       this.virtualMachineName = virtualMachineName;
-      this.CIDRs = Sets.newHashSet(CIDRs);
+      this.publicPort = publicPort;
+      this.CIDRs = CIDRs == null ? ImmutableSet.<String>of() : ImmutableSet.copyOf(CIDRs);      
       this.privateEndPort = privateEndPort;
       this.publicEndPort = publicEndPort;
-      this.publicPort = publicPort;
-
-   }
-
-   @Override
-   public int compareTo(IPForwardingRule arg0) {
-      return id.compareTo(arg0.getId());
    }
 
    /**
     * @return the ID of the ip forwarding rule
     */
    public String getId() {
-      return id;
+      return this.id;
    }
 
    /**
     * @return the public ip address for the ip forwarding rule
     */
+   @Nullable
    public String getIPAddress() {
-      return IPAddress;
+      return this.IPAddress;
    }
 
    /**
     * @return the public ip address id for the ip forwarding rule
     */
+   @Nullable
    public String getIPAddressId() {
-      return IPAddressId;
+      return this.IPAddressId;
    }
 
    /**
     * @return the private port for the ip forwarding rule
     */
    public int getStartPort() {
-      return startPort;
+      return this.startPort;
    }
 
    /**
     * @return the protocol of the ip forwarding rule
     */
+   @Nullable
    public String getProtocol() {
-      return protocol;
+      return this.protocol;
    }
 
    /**
     * @return the public port for the ip forwarding rule
     */
    public int getEndPort() {
-      return endPort;
+      return this.endPort;
    }
 
    /**
     * @return the state of the rule
     */
+   @Nullable
    public String getState() {
-      return state;
+      return this.state;
    }
 
    /**
     * @return the VM display name for the ip forwarding rule
     */
+   @Nullable
    public String getVirtualMachineDisplayName() {
-      return virtualMachineDisplayName;
+      return this.virtualMachineDisplayName;
    }
 
    /**
     * @return the VM ID for the ip forwarding rule
     */
+   @Nullable
    public String getVirtualMachineId() {
-      return virtualMachineId;
+      return this.virtualMachineId;
    }
 
    /**
     * @return the VM name for the ip forwarding rule
     */
+   @Nullable
    public String getVirtualMachineName() {
-      return virtualMachineName;
+      return this.virtualMachineName;
    }
 
    /**
     * @return the starting port of port forwarding rule's public port range
     */
    public int getPublicPort() {
-      return publicPort;
+      return this.publicPort;
    }
 
    /**
     * @return the cidr list to forward traffic from
     */
    public Set<String> getCIDRs() {
-      return Collections.unmodifiableSet(CIDRs);
+      return this.CIDRs;
    }
 
    /**
     * @return the ending port of port forwarding rule's private port range
     */
    public int getPrivateEndPort() {
-      return privateEndPort;
+      return this.privateEndPort;
    }
 
    /**
     * @return the ending port of port forwarding rule's private port range
     */
    public int getPublicEndPort() {
-      return publicEndPort;
-   }
-
-   @Override
-   public boolean equals(Object o) {
-      if (this == o) return true;
-      if (o == null || getClass() != o.getClass()) return false;
-
-      IPForwardingRule that = (IPForwardingRule) o;
-
-      if (!Objects.equal(IPAddress, that.IPAddress)) return false;
-      if (!Objects.equal(IPAddressId, that.IPAddressId)) return false;
-      if (!Objects.equal(endPort, that.endPort)) return false;
-      if (!Objects.equal(id, that.id)) return false;
-      if (!Objects.equal(protocol, that.protocol)) return false;
-      if (!Objects.equal(startPort, that.startPort)) return false;
-      if (!Objects.equal(publicEndPort, that.publicEndPort)) return false;
-      if (!Objects.equal(privateEndPort, that.privateEndPort)) return false;
-      if (!Objects.equal(publicPort, that.publicPort)) return false;
-      if (!Objects.equal(state, that.state)) return false;
-      if (!Objects.equal(virtualMachineDisplayName, that.virtualMachineDisplayName)) return false;
-      if (!Objects.equal(virtualMachineId, that.virtualMachineId)) return false;
-      if (!Objects.equal(virtualMachineName, that.virtualMachineName)) return false;
-
-      return true;
+      return this.publicEndPort;
    }
 
    @Override
    public int hashCode() {
-       return Objects.hashCode(IPAddress, IPAddressId, endPort, id, protocol, startPort, publicEndPort,
-                               privateEndPort, publicPort, state, virtualMachineDisplayName,
-                               virtualMachineId, virtualMachineName);
+      return Objects.hashCode(id, IPAddress, IPAddressId, startPort, protocol, endPort, state, virtualMachineDisplayName, virtualMachineId, virtualMachineName, publicPort, CIDRs, privateEndPort, publicEndPort);
    }
 
    @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      IPForwardingRule that = IPForwardingRule.class.cast(obj);
+      return Objects.equal(this.id, that.id)
+               && Objects.equal(this.IPAddress, that.IPAddress)
+               && Objects.equal(this.IPAddressId, that.IPAddressId)
+               && Objects.equal(this.startPort, that.startPort)
+               && Objects.equal(this.protocol, that.protocol)
+               && Objects.equal(this.endPort, that.endPort)
+               && Objects.equal(this.state, that.state)
+               && Objects.equal(this.virtualMachineDisplayName, that.virtualMachineDisplayName)
+               && Objects.equal(this.virtualMachineId, that.virtualMachineId)
+               && Objects.equal(this.virtualMachineName, that.virtualMachineName)
+               && Objects.equal(this.publicPort, that.publicPort)
+               && Objects.equal(this.CIDRs, that.CIDRs)
+               && Objects.equal(this.privateEndPort, that.privateEndPort)
+               && Objects.equal(this.publicEndPort, that.publicEndPort);
+   }
+   
+   protected ToStringHelper string() {
+      return Objects.toStringHelper(this)
+            .add("id", id).add("IPAddress", IPAddress).add("IPAddressId", IPAddressId).add("startPort", startPort)
+            .add("protocol", protocol).add("endPort", endPort).add("state", state).add("virtualMachineDisplayName", virtualMachineDisplayName)
+            .add("virtualMachineId", virtualMachineId).add("virtualMachineName", virtualMachineName).add("publicPort", publicPort)
+            .add("CIDRs", CIDRs).add("privateEndPort", privateEndPort).add("publicEndPort", publicEndPort);
+   }
+   
+   @Override
    public String toString() {
-      return "[IPAddress=" + IPAddress + ", IPAddressId=" + IPAddressId + ", id=" + id + ", startPort=" + startPort
-            + ", protocol=" + protocol + ", endPort=" + endPort + ", state=" + state + ", virtualMachineDisplayName="
-            + virtualMachineDisplayName + ", virtualMachineId=" + virtualMachineId + ", virtualMachineName="
-            + virtualMachineName + "]";
+      return string().toString();
    }
 
+   @Override
+   public int compareTo(IPForwardingRule o) {
+      return id.compareTo(o.getId());
+   }
 }
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/ISO.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/ISO.java
index 6ed2242..d5a942e 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/ISO.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/ISO.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -20,699 +20,772 @@
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import java.beans.ConstructorProperties;
 import java.util.Date;
 
+import javax.inject.Named;
+
+import org.jclouds.javax.annotation.Nullable;
+
 import com.google.common.base.Objects;
-import com.google.gson.annotations.SerializedName;
+import com.google.common.base.Objects.ToStringHelper;
 
 /**
+ * Class ISO
+ * 
  * @author Richard Downer
- */
-public class ISO implements Comparable<ISO> {
-
-   public static Builder builder() {
-      return new Builder();
-   }
-
-   public static class Builder {
-
-      private String id;
-      private String account;
-      private String accountId;
-      private boolean bootable;
-      private String checksum;
-      private Date created;
-      private boolean crossZones;
-      private String displayText;
-      private String domain;
-      private String domainid;
-      private String format;
-      private String hostId;
-      private String hostName;
-      private String hypervisor;
-      private boolean isExtractable;
-      private boolean isFeatured;
-      private boolean isPublic;
-      private boolean isReady;
-      private String jobId;
-      private String jobStatus;
-      private String name;
-      private String osTypeId;
-      private String osTypeName;
-      private boolean passwordEnabled;
-      private Date removed;
-      private long size;
-      private String sourceTemplateId;
-      private String status;
-      private String templateTag;
-      private String templateType;
-      private String zoneId;
-      private String zoneName;
-
-      /**
-       * @param id the template ID
-       */
-      public Builder id(String id) {
-         this.id = id;
-         return this;
-      }
-
-      /**
-       * @param account the account name to which the template belongs
-       */
-      public Builder account(String account) {
-         this.account = account;
-         return this;
-      }
-
-      /**
-       * @param accountId the account id to which the template belongs
-       */
-      public Builder accountId(String accountId) {
-         this.accountId = accountId;
-         return this;
-      }
-
-      /**
-       * @param bootable true if the ISO is bootable, false otherwise
-       */
-      public Builder bootable(boolean bootable) {
-         this.bootable = bootable;
-         return this;
-      }
-
-      /**
-       * @param checksum checksum of the template
-       */
-      public Builder checksum(String checksum) {
-         this.checksum = checksum;
-         return this;
-      }
-
-      /**
-       * @param created the date this template was created
-       */
-      public Builder created(Date created) {
-         this.created = created;
-         return this;
-      }
-
-      /**
-       * @param crossZones true if the template is managed across all Zones, false otherwise
-       */
-      public Builder crossZones(boolean crossZones) {
-         this.crossZones = crossZones;
-         return this;
-      }
-
-      /**
-       * @param displayText the template display text
-       */
-      public Builder displayText(String displayText) {
-         this.displayText = displayText;
-         return this;
-      }
-
-      /**
-       * @param domain the name of the domain to which the template belongs
-       */
-      public Builder domain(String domain) {
-         this.domain = domain;
-         return this;
-      }
-
-      /**
-       * @param domainid the ID of the domain to which the template belongs
-       */
-      public Builder domainid(String domainid) {
-         this.domainid = domainid;
-         return this;
-      }
-
-      /**
-       * @param format the format of the template.
-       */
-      public Builder format(String format) {
-         this.format = format;
-         return this;
-      }
-
-      /**
-       * @param hostId the ID of the secondary storage host for the template
-       */
-      public Builder hostId(String hostId) {
-         this.hostId = hostId;
-         return this;
-      }
-
-      /**
-       * @param hostName the name of the secondary storage host for the template
-       */
-      public Builder hostName(String hostName) {
-         this.hostName = hostName;
-         return this;
-      }
-
-      /**
-       * @param hypervisor the hypervisor on which the template runs
-       */
-      public Builder hypervisor(String hypervisor) {
-         this.hypervisor = hypervisor;
-         return this;
-      }
-
-      /**
-       * @param isExtractable true if the template is extractable, false otherwise
-       */
-      public Builder isExtractable(boolean isExtractable) {
-         this.isExtractable = isExtractable;
-         return this;
-      }
-
-      /**
-       * @param isFeatured true if this template is a featured template, false otherwise
-       */
-      public Builder isFeatured(boolean isFeatured) {
-         this.isFeatured = isFeatured;
-         return this;
-      }
-
-      /**
-       * @param isPublic true if this template is a public template, false otherwise
-       */
-      public Builder isPublic(boolean isPublic) {
-         this.isPublic = isPublic;
-         return this;
-      }
-
-      /**
-       * @param isReady true if the template is ready to be deployed from, false otherwise.
-       */
-      public Builder isReady(boolean isReady) {
-         this.isReady = isReady;
-         return this;
-      }
-
-      /**
-       * @param jobId shows the current pending asynchronous job ID. This tag is not returned if no current pending jobs are acting on the template
-       */
-      public Builder jobId(String jobId) {
-         this.jobId = jobId;
-         return this;
-      }
-
-      /**
-       * @param jobStatus shows the current pending asynchronous job status
-       */
-      public Builder jobStatus(String jobStatus) {
-         this.jobStatus = jobStatus;
-         return this;
-      }
-
-      /**
-       * @param name the template name
-       */
-      public Builder name(String name) {
-         this.name = name;
-         return this;
-      }
-
-      /**
-       * @param osTypeId the ID of the OS type for this template.
-       */
-      public Builder osTypeId(String osTypeId) {
-         this.osTypeId = osTypeId;
-         return this;
-      }
-
-      /**
-       * @param osTypeName the name of the OS type for this template.
-       */
-      public Builder osTypeName(String osTypeName) {
-         this.osTypeName = osTypeName;
-         return this;
-      }
-
-      /**
-       * @param passwordEnabled true if the reset password feature is enabled, false otherwise
-       */
-      public Builder passwordEnabled(boolean passwordEnabled) {
-         this.passwordEnabled = passwordEnabled;
-         return this;
-      }
-
-      /**
-       * @param removed the date this template was removed
-       */
-      public Builder removed(Date removed) {
-         this.removed = removed;
-         return this;
-      }
-
-      /**
-       * @param size the size of the template
-       */
-      public Builder size(long size) {
-         this.size = size;
-         return this;
-      }
-
-      /**
-       * @param sourceTemplateId the template ID of the parent template if present
-       */
-      public Builder sourceTemplateId(String sourceTemplateId) {
-         this.sourceTemplateId = sourceTemplateId;
-         return this;
-      }
-
-      /**
-       * @param status the status of the template
-       */
-      public Builder status(String status) {
-         this.status = status;
-         return this;
-      }
-
-      /**
-       * @param templateTag the tag of this template
-       */
-      public Builder templateTag(String templateTag) {
-         this.templateTag = templateTag;
-         return this;
-      }
-
-      /**
-       * @param templateType the type of the template
-       */
-      public Builder templateType(String templateType) {
-         this.templateType = templateType;
-         return this;
-      }
-
-      /**
-       * @param zoneId the ID of the zone for this template
-       */
-      public Builder zoneId(String zoneId) {
-         this.zoneId = zoneId;
-         return this;
-      }
-
-      /**
-       * @param zoneName the name of the zone for this template
-       */
-      public Builder zoneName(String zoneName) {
-         this.zoneName = zoneName;
-         return this;
-      }
-
-   }
-
-   private String id;
-   private String account;
-   @SerializedName("accountid")
-   private String accountId;
-   private boolean bootable;
-   private String checksum;
-   private Date created;
-   private boolean crossZones;
-   @SerializedName("displaytext")
-   private String displayText;
-   private String domain;
-   @SerializedName("domainId")
-   private String domainid;
-   private String format;
-   @SerializedName("hostid")
-   private String hostId;
-   @SerializedName("hostname")
-   private String hostName;
-   private String hypervisor;
-   @SerializedName("isextractable")
-   private boolean isExtractable;
-   @SerializedName("isfeatured")
-   private boolean isFeatured;
-   @SerializedName("ispublic")
-   private boolean isPublic;
-   @SerializedName("isready")
-   private boolean isReady;
-   @SerializedName("jobid")
-   private String jobId;
-   @SerializedName("jobstatus")
-   private String jobStatus;
-   private String name;
-   @SerializedName("ostypeid")
-   private String osTypeId;
-   @SerializedName("ostypename")
-   private String osTypeName;
-   @SerializedName("passwordenabled")
-   private boolean passwordEnabled;
-   private Date removed;
-   private long size;
-   @SerializedName("sourcetemplateid")
-   private String sourceTemplateId;
-   private String status;
-   @SerializedName("templatetag")
-   private String templateTag;
-   @SerializedName("templatetype")
-   private String templateType;
-   @SerializedName("zoneid")
-   private String zoneId;
-   @SerializedName("zonename")
-   private String zoneName;
+*/
+public class ISO {
 
    /**
-    * present only for serializer
     */
-   ISO() {
+   public static enum ISOFilter {
+      
+      featured, self, self_executable, executable, community, UNRECOGNIZED;
+      
+      public static ISOFilter fromValue(String format) {
+      try {
+      return valueOf(checkNotNull(format, "format"));
+      } catch (IllegalArgumentException e) {
+      return UNRECOGNIZED;
+      }
+      }
+   }
+
+   public static Builder<?> builder() { 
+      return new ConcreteBuilder();
+   }
+   
+   public Builder<?> toBuilder() { 
+      return new ConcreteBuilder().fromISO(this);
+   }
+
+   public static abstract class Builder<T extends Builder<T>>  {
+      protected abstract T self();
+
+      protected String id;
+      protected String account;
+      protected String accountId;
+      protected boolean bootable;
+      protected String checksum;
+      protected Date created;
+      protected boolean crossZones;
+      protected String displayText;
+      protected String domain;
+      protected String domainid;
+      protected String format;
+      protected String hostId;
+      protected String hostName;
+      protected String hypervisor;
+      protected boolean isExtractable;
+      protected boolean isFeatured;
+      protected boolean isPublic;
+      protected boolean isReady;
+      protected String jobId;
+      protected String jobStatus;
+      protected String name;
+      protected String osTypeId;
+      protected String osTypeName;
+      protected boolean passwordEnabled;
+      protected Date removed;
+      protected long size;
+      protected String sourceTemplateId;
+      protected String status;
+      protected String templateTag;
+      protected String templateType;
+      protected String zoneId;
+      protected String zoneName;
+   
+      /** 
+       * @see ISO#getId()
+       */
+      public T id(String id) {
+         this.id = id;
+         return self();
+      }
+
+      /** 
+       * @see ISO#getAccount()
+       */
+      public T account(String account) {
+         this.account = account;
+         return self();
+      }
+
+      /** 
+       * @see ISO#getAccountId()
+       */
+      public T accountId(String accountId) {
+         this.accountId = accountId;
+         return self();
+      }
+
+      /** 
+       * @see ISO#isBootable()
+       */
+      public T bootable(boolean bootable) {
+         this.bootable = bootable;
+         return self();
+      }
+
+      /** 
+       * @see ISO#getChecksum()
+       */
+      public T checksum(String checksum) {
+         this.checksum = checksum;
+         return self();
+      }
+
+      /** 
+       * @see ISO#getCreated()
+       */
+      public T created(Date created) {
+         this.created = created;
+         return self();
+      }
+
+      /** 
+       * @see ISO#isCrossZones()
+       */
+      public T crossZones(boolean crossZones) {
+         this.crossZones = crossZones;
+         return self();
+      }
+
+      /** 
+       * @see ISO#getDisplayText()
+       */
+      public T displayText(String displayText) {
+         this.displayText = displayText;
+         return self();
+      }
+
+      /** 
+       * @see ISO#getDomain()
+       */
+      public T domain(String domain) {
+         this.domain = domain;
+         return self();
+      }
+
+      /** 
+       * @see ISO#getDomainid()
+       */
+      public T domainid(String domainid) {
+         this.domainid = domainid;
+         return self();
+      }
+
+      /** 
+       * @see ISO#getFormat()
+       */
+      public T format(String format) {
+         this.format = format;
+         return self();
+      }
+
+      /** 
+       * @see ISO#getHostId()
+       */
+      public T hostId(String hostId) {
+         this.hostId = hostId;
+         return self();
+      }
+
+      /** 
+       * @see ISO#getHostName()
+       */
+      public T hostName(String hostName) {
+         this.hostName = hostName;
+         return self();
+      }
+
+      /** 
+       * @see ISO#getHypervisor()
+       */
+      public T hypervisor(String hypervisor) {
+         this.hypervisor = hypervisor;
+         return self();
+      }
+
+      /** 
+       * @see ISO#isExtractable()
+       */
+      public T isExtractable(boolean isExtractable) {
+         this.isExtractable = isExtractable;
+         return self();
+      }
+
+      /** 
+       * @see ISO#isFeatured()
+       */
+      public T isFeatured(boolean isFeatured) {
+         this.isFeatured = isFeatured;
+         return self();
+      }
+
+      /** 
+       * @see ISO#isPublic()
+       */
+      public T isPublic(boolean isPublic) {
+         this.isPublic = isPublic;
+         return self();
+      }
+
+      /** 
+       * @see ISO#isReady()
+       */
+      public T isReady(boolean isReady) {
+         this.isReady = isReady;
+         return self();
+      }
+
+      /** 
+       * @see ISO#getJobId()
+       */
+      public T jobId(String jobId) {
+         this.jobId = jobId;
+         return self();
+      }
+
+      /** 
+       * @see ISO#getJobStatus()
+       */
+      public T jobStatus(String jobStatus) {
+         this.jobStatus = jobStatus;
+         return self();
+      }
+
+      /** 
+       * @see ISO#getName()
+       */
+      public T name(String name) {
+         this.name = name;
+         return self();
+      }
+
+      /** 
+       * @see ISO#getOsTypeId()
+       */
+      public T osTypeId(String osTypeId) {
+         this.osTypeId = osTypeId;
+         return self();
+      }
+
+      /** 
+       * @see ISO#getOsTypeName()
+       */
+      public T osTypeName(String osTypeName) {
+         this.osTypeName = osTypeName;
+         return self();
+      }
+
+      /** 
+       * @see ISO#isPasswordEnabled()
+       */
+      public T passwordEnabled(boolean passwordEnabled) {
+         this.passwordEnabled = passwordEnabled;
+         return self();
+      }
+
+      /** 
+       * @see ISO#getRemoved()
+       */
+      public T removed(Date removed) {
+         this.removed = removed;
+         return self();
+      }
+
+      /** 
+       * @see ISO#getSize()
+       */
+      public T size(long size) {
+         this.size = size;
+         return self();
+      }
+
+      /** 
+       * @see ISO#getSourceTemplateId()
+       */
+      public T sourceTemplateId(String sourceTemplateId) {
+         this.sourceTemplateId = sourceTemplateId;
+         return self();
+      }
+
+      /** 
+       * @see ISO#getStatus()
+       */
+      public T status(String status) {
+         this.status = status;
+         return self();
+      }
+
+      /** 
+       * @see ISO#getTemplateTag()
+       */
+      public T templateTag(String templateTag) {
+         this.templateTag = templateTag;
+         return self();
+      }
+
+      /** 
+       * @see ISO#getTemplateType()
+       */
+      public T templateType(String templateType) {
+         this.templateType = templateType;
+         return self();
+      }
+
+      /** 
+       * @see ISO#getZoneId()
+       */
+      public T zoneId(String zoneId) {
+         this.zoneId = zoneId;
+         return self();
+      }
+
+      /** 
+       * @see ISO#getZoneName()
+       */
+      public T zoneName(String zoneName) {
+         this.zoneName = zoneName;
+         return self();
+      }
+
+      public ISO build() {
+         return new ISO(id, account, accountId, bootable, checksum, created, crossZones, displayText, domain, domainid,
+               format, hostId, hostName, hypervisor, isExtractable, isFeatured, isPublic, isReady, jobId, jobStatus, name,
+               osTypeId, osTypeName, passwordEnabled, removed, size, sourceTemplateId, status, templateTag, templateType,
+               zoneId, zoneName);
+      }
+      
+      public T fromISO(ISO in) {
+         return this
+                  .id(in.getId())
+                  .account(in.getAccount())
+                  .accountId(in.getAccountId())
+                  .bootable(in.isBootable())
+                  .checksum(in.getChecksum())
+                  .created(in.getCreated())
+                  .crossZones(in.isCrossZones())
+                  .displayText(in.getDisplayText())
+                  .domain(in.getDomain())
+                  .domainid(in.getDomainid())
+                  .format(in.getFormat())
+                  .hostId(in.getHostId())
+                  .hostName(in.getHostName())
+                  .hypervisor(in.getHypervisor())
+                  .isExtractable(in.isExtractable())
+                  .isFeatured(in.isFeatured())
+                  .isPublic(in.isPublic())
+                  .isReady(in.isReady())
+                  .jobId(in.getJobId())
+                  .jobStatus(in.getJobStatus())
+                  .name(in.getName())
+                  .osTypeId(in.getOsTypeId())
+                  .osTypeName(in.getOsTypeName())
+                  .passwordEnabled(in.isPasswordEnabled())
+                  .removed(in.getRemoved())
+                  .size(in.getSize())
+                  .sourceTemplateId(in.getSourceTemplateId())
+                  .status(in.getStatus())
+                  .templateTag(in.getTemplateTag())
+                  .templateType(in.getTemplateType())
+                  .zoneId(in.getZoneId())
+                  .zoneName(in.getZoneName());
+      }
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
+   }
+
+   private final String id;
+   private final String account;
+   @Named("accountid")
+   private final String accountId;
+   private final boolean bootable;
+   private final String checksum;
+   private final Date created;
+   private final boolean crossZones;
+   @Named("displaytext")
+   private final String displayText;
+   private final String domain;
+   @Named("domainId")
+   private final String domainid;
+   private final String format;
+   @Named("hostid")
+   private final String hostId;
+   @Named("hostname")
+   private final String hostName;
+   private final String hypervisor;
+   @Named("isextractable")
+   private final boolean isExtractable;
+   @Named("isfeatured")
+   private final boolean isFeatured;
+   @Named("ispublic")
+   private final boolean isPublic;
+   @Named("isready")
+   private final boolean isReady;
+   @Named("jobid")
+   private final String jobId;
+   @Named("jobstatus")
+   private final String jobStatus;
+   private final String name;
+   @Named("ostypeid")
+   private final String osTypeId;
+   @Named("ostypename")
+   private final String osTypeName;
+   @Named("passwordenabled")
+   private final boolean passwordEnabled;
+   private final Date removed;
+   private final long size;
+   @Named("sourcetemplateid")
+   private final String sourceTemplateId;
+   private final String status;
+   @Named("templatetag")
+   private final String templateTag;
+   @Named("templatetype")
+   private final String templateType;
+   @Named("zoneid")
+   private final String zoneId;
+   @Named("zonename")
+   private final String zoneName;
+
+   @ConstructorProperties({
+      "id", "account", "accountid", "bootable", "checksum", "created", "crossZones", "displaytext", "domain", "domainId", "format", "hostid", "hostname", "hypervisor", "isextractable", "isfeatured", "ispublic", "isready", "jobid", "jobstatus", "name", "ostypeid", "ostypename", "passwordenabled", "removed", "size", "sourcetemplateid", "status", "templatetag", "templatetype", "zoneid", "zonename"
+   })
+   protected ISO(String id, @Nullable String account, @Nullable String accountId, boolean bootable, @Nullable String checksum,
+                 @Nullable Date created, boolean crossZones, @Nullable String displayText, @Nullable String domain,
+                 @Nullable String domainid, @Nullable String format, @Nullable String hostId, @Nullable String hostName,
+                 @Nullable String hypervisor, boolean isExtractable, boolean isFeatured, boolean isPublic, boolean isReady,
+                 @Nullable String jobId, @Nullable String jobStatus, @Nullable String name, @Nullable String osTypeId,
+                 @Nullable String osTypeName, boolean passwordEnabled, @Nullable Date removed, long size, @Nullable String sourceTemplateId,
+                 @Nullable String status, @Nullable String templateTag, @Nullable String templateType, @Nullable String zoneId, @Nullable String zoneName) {
+      this.id = checkNotNull(id, "id");
+      this.account = account;
+      this.accountId = accountId;
+      this.bootable = bootable;
+      this.checksum = checksum;
+      this.created = created;
+      this.crossZones = crossZones;
+      this.displayText = displayText;
+      this.domain = domain;
+      this.domainid = domainid;
+      this.format = format;
+      this.hostId = hostId;
+      this.hostName = hostName;
+      this.hypervisor = hypervisor;
+      this.isExtractable = isExtractable;
+      this.isFeatured = isFeatured;
+      this.isPublic = isPublic;
+      this.isReady = isReady;
+      this.jobId = jobId;
+      this.jobStatus = jobStatus;
+      this.name = name;
+      this.osTypeId = osTypeId;
+      this.osTypeName = osTypeName;
+      this.passwordEnabled = passwordEnabled;
+      this.removed = removed;
+      this.size = size;
+      this.sourceTemplateId = sourceTemplateId;
+      this.status = status;
+      this.templateTag = templateTag;
+      this.templateType = templateType;
+      this.zoneId = zoneId;
+      this.zoneName = zoneName;
    }
 
    /**
     * @return the template ID
     */
    public String getId() {
-      return id;
+      return this.id;
    }
 
    /**
     * @return the account name to which the template belongs
     */
+   @Nullable
    public String getAccount() {
-      return account;
+      return this.account;
    }
 
    /**
     * @return the account id to which the template belongs
     */
+   @Nullable
    public String getAccountId() {
-      return accountId;
+      return this.accountId;
    }
 
-   /**
-    * @return true if the ISO is bootable, false otherwise
-    */
-   public boolean getBootable() {
-      return bootable;
+   public boolean isBootable() {
+      return this.bootable;
    }
 
    /**
     * @return checksum of the template
     */
+   @Nullable
    public String getChecksum() {
-      return checksum;
+      return this.checksum;
    }
 
    /**
     * @return the date this template was created
     */
+   @Nullable
    public Date getCreated() {
-      return created;
+      return this.created;
    }
 
-   /**
-    * @return true if the template is managed across all Zones, false otherwise
-    */
-   public boolean getCrossZones() {
-      return crossZones;
+   public boolean isCrossZones() {
+      return this.crossZones;
    }
 
    /**
     * @return the template display text
     */
+   @Nullable
    public String getDisplayText() {
-      return displayText;
+      return this.displayText;
    }
 
    /**
     * @return the name of the domain to which the template belongs
     */
+   @Nullable
    public String getDomain() {
-      return domain;
+      return this.domain;
    }
 
    /**
     * @return the ID of the domain to which the template belongs
     */
+   @Nullable
    public String getDomainid() {
-      return domainid;
+      return this.domainid;
    }
 
    /**
     * @return the format of the template.
     */
+   @Nullable
    public String getFormat() {
-      return format;
+      return this.format;
    }
 
    /**
     * @return the ID of the secondary storage host for the template
     */
+   @Nullable
    public String getHostId() {
-      return hostId;
+      return this.hostId;
    }
 
    /**
     * @return the name of the secondary storage host for the template
     */
+   @Nullable
    public String getHostName() {
-      return hostName;
+      return this.hostName;
    }
 
    /**
     * @return the hypervisor on which the template runs
     */
+   @Nullable
    public String getHypervisor() {
-      return hypervisor;
+      return this.hypervisor;
    }
 
-   /**
-    * @return true if the template is extractable, false otherwise
-    */
-   public boolean getIsExtractable() {
-      return isExtractable;
+   public boolean isExtractable() {
+      return this.isExtractable;
    }
 
-   /**
-    * @return true if this template is a featured template, false otherwise
-    */
-   public boolean getIsFeatured() {
-      return isFeatured;
+   public boolean isFeatured() {
+      return this.isFeatured;
    }
 
-   /**
-    * @return true if this template is a public template, false otherwise
-    */
-   public boolean getIsPublic() {
-      return isPublic;
+   public boolean isPublic() {
+      return this.isPublic;
    }
 
-   /**
-    * @return true if the template is ready to be deployed from, false otherwise.
-    */
-   public boolean getIsReady() {
-      return isReady;
+   public boolean isReady() {
+      return this.isReady;
    }
 
    /**
     * @return shows the current pending asynchronous job ID. This tag is not returned if no current pending jobs are acting on the template
     */
+   @Nullable
    public String getJobId() {
-      return jobId;
+      return this.jobId;
    }
 
    /**
     * @return shows the current pending asynchronous job status
     */
+   @Nullable
    public String getJobStatus() {
-      return jobStatus;
+      return this.jobStatus;
    }
 
    /**
     * @return the template name
     */
+   @Nullable
    public String getName() {
-      return name;
+      return this.name;
    }
 
    /**
     * @return the ID of the OS type for this template.
     */
+   @Nullable
    public String getOsTypeId() {
-      return osTypeId;
+      return this.osTypeId;
    }
 
    /**
     * @return the name of the OS type for this template.
     */
+   @Nullable
    public String getOsTypeName() {
-      return osTypeName;
+      return this.osTypeName;
    }
 
-   /**
-    * @return true if the reset password feature is enabled, false otherwise
-    */
-   public boolean getPasswordEnabled() {
-      return passwordEnabled;
+   public boolean isPasswordEnabled() {
+      return this.passwordEnabled;
    }
 
    /**
     * @return the date this template was removed
     */
+   @Nullable
    public Date getRemoved() {
-      return removed;
+      return this.removed;
    }
 
    /**
     * @return the size of the template
     */
    public long getSize() {
-      return size;
+      return this.size;
    }
 
    /**
     * @return the template ID of the parent template if present
     */
+   @Nullable
    public String getSourceTemplateId() {
-      return sourceTemplateId;
+      return this.sourceTemplateId;
    }
 
    /**
     * @return the status of the template
     */
+   @Nullable
    public String getStatus() {
-      return status;
+      return this.status;
    }
 
    /**
     * @return the tag of this template
     */
+   @Nullable
    public String getTemplateTag() {
-      return templateTag;
+      return this.templateTag;
    }
 
    /**
     * @return the type of the template
     */
+   @Nullable
    public String getTemplateType() {
-      return templateType;
+      return this.templateType;
    }
 
    /**
     * @return the ID of the zone for this template
     */
+   @Nullable
    public String getZoneId() {
-      return zoneId;
+      return this.zoneId;
    }
 
    /**
     * @return the name of the zone for this template
     */
+   @Nullable
    public String getZoneName() {
-      return zoneName;
-   }
-
-   @Override
-   public boolean equals(Object o) {
-      if (this == o) return true;
-      if (o == null || getClass() != o.getClass()) return false;
-
-      ISO that = (ISO) o;
-
-      if (!Objects.equal(accountId, that.accountId)) return false;
-      if (!Objects.equal(bootable, that.bootable)) return false;
-      if (!Objects.equal(crossZones, that.crossZones)) return false;
-      if (!Objects.equal(domainid, that.domainid)) return false;
-      if (!Objects.equal(hostId, that.hostId)) return false;
-      if (!Objects.equal(id, that.id)) return false;
-      if (!Objects.equal(isExtractable, that.isExtractable)) return false;
-      if (!Objects.equal(isPublic, that.isPublic)) return false;
-      if (!Objects.equal(isReady, that.isReady)) return false;
-      if (!Objects.equal(jobId, that.jobId)) return false;
-      if (!Objects.equal(osTypeId, that.osTypeId)) return false;
-      if (!Objects.equal(passwordEnabled, that.passwordEnabled)) return false;
-      if (!Objects.equal(size, that.size)) return false;
-      if (!Objects.equal(sourceTemplateId, that.sourceTemplateId)) return false;
-      if (!Objects.equal(zoneId, that.zoneId)) return false;
-      if (!Objects.equal(account, that.account)) return false;
-      if (!Objects.equal(checksum, that.checksum)) return false;
-      if (!Objects.equal(created, that.created)) return false;
-      if (!Objects.equal(displayText, that.displayText)) return false;
-      if (!Objects.equal(domain, that.domain)) return false;
-      if (!Objects.equal(format, that.format)) return false;
-      if (!Objects.equal(hostName, that.hostName)) return false;
-      if (!Objects.equal(hypervisor, that.hypervisor)) return false;
-      if (!Objects.equal(jobStatus, that.jobStatus)) return false;
-      if (!Objects.equal(name, that.name)) return false;
-      if (!Objects.equal(osTypeName, that.osTypeName)) return false;
-      if (!Objects.equal(removed, that.removed)) return false;
-      if (!Objects.equal(status, that.status)) return false;
-      if (!Objects.equal(templateTag, that.templateTag)) return false;
-      if (!Objects.equal(templateType, that.templateType)) return false;
-      if (!Objects.equal(zoneName, that.zoneName)) return false;
-
-      return true;
+      return this.zoneName;
    }
 
    @Override
    public int hashCode() {
-       return Objects.hashCode(accountId, bootable, crossZones, domainid, hostId, id, isExtractable,
-                               isPublic, isReady, jobId, osTypeId, passwordEnabled, size, sourceTemplateId,
-                               zoneId, account, checksum, created, displayText, domain, format, hostName,
-                               hypervisor, jobStatus, name, osTypeName, removed, status, templateTag,
-                               templateType, zoneName);
+      return Objects.hashCode(id, account, accountId, bootable, checksum, created, crossZones, displayText, domain,
+            domainid, format, hostId, hostName, hypervisor, isExtractable, isFeatured, isPublic, isReady, jobId, jobStatus,
+            name, osTypeId, osTypeName, passwordEnabled, removed, size, sourceTemplateId, status, templateTag, templateType, zoneId, zoneName);
    }
 
    @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      ISO that = ISO.class.cast(obj);
+      return Objects.equal(this.id, that.id)
+               && Objects.equal(this.account, that.account)
+               && Objects.equal(this.accountId, that.accountId)
+               && Objects.equal(this.bootable, that.bootable)
+               && Objects.equal(this.checksum, that.checksum)
+               && Objects.equal(this.created, that.created)
+               && Objects.equal(this.crossZones, that.crossZones)
+               && Objects.equal(this.displayText, that.displayText)
+               && Objects.equal(this.domain, that.domain)
+               && Objects.equal(this.domainid, that.domainid)
+               && Objects.equal(this.format, that.format)
+               && Objects.equal(this.hostId, that.hostId)
+               && Objects.equal(this.hostName, that.hostName)
+               && Objects.equal(this.hypervisor, that.hypervisor)
+               && Objects.equal(this.isExtractable, that.isExtractable)
+               && Objects.equal(this.isFeatured, that.isFeatured)
+               && Objects.equal(this.isPublic, that.isPublic)
+               && Objects.equal(this.isReady, that.isReady)
+               && Objects.equal(this.jobId, that.jobId)
+               && Objects.equal(this.jobStatus, that.jobStatus)
+               && Objects.equal(this.name, that.name)
+               && Objects.equal(this.osTypeId, that.osTypeId)
+               && Objects.equal(this.osTypeName, that.osTypeName)
+               && Objects.equal(this.passwordEnabled, that.passwordEnabled)
+               && Objects.equal(this.removed, that.removed)
+               && Objects.equal(this.size, that.size)
+               && Objects.equal(this.sourceTemplateId, that.sourceTemplateId)
+               && Objects.equal(this.status, that.status)
+               && Objects.equal(this.templateTag, that.templateTag)
+               && Objects.equal(this.templateType, that.templateType)
+               && Objects.equal(this.zoneId, that.zoneId)
+               && Objects.equal(this.zoneName, that.zoneName);
+   }
+   
+   protected ToStringHelper string() {
+      return Objects.toStringHelper(this)
+            .add("id", id).add("account", account).add("accountId", accountId).add("bootable", bootable)
+            .add("checksum", checksum).add("created", created).add("crossZones", crossZones).add("displayText", displayText)
+            .add("domain", domain).add("domainid", domainid).add("format", format).add("hostId", hostId).add("hostName", hostName)
+            .add("hypervisor", hypervisor).add("isExtractable", isExtractable).add("isFeatured", isFeatured).add("isPublic", isPublic)
+            .add("isReady", isReady).add("jobId", jobId).add("jobStatus", jobStatus).add("name", name).add("osTypeId", osTypeId)
+            .add("osTypeName", osTypeName).add("passwordEnabled", passwordEnabled).add("removed", removed).add("size", size)
+            .add("sourceTemplateId", sourceTemplateId).add("status", status).add("templateTag", templateTag).add("templateType", templateType)
+            .add("zoneId", zoneId).add("zoneName", zoneName);
+   }
+   
+   @Override
    public String toString() {
-      return "ISO{" +
-            "id=" + id +
-            ", account='" + account + '\'' +
-            ", accountId=" + accountId +
-            ", bootable=" + bootable +
-            ", checksum='" + checksum + '\'' +
-            ", created=" + created +
-            ", crossZones=" + crossZones +
-            ", displayText='" + displayText + '\'' +
-            ", domain='" + domain + '\'' +
-            ", domainid=" + domainid +
-            ", format='" + format + '\'' +
-            ", hostId=" + hostId +
-            ", hostName='" + hostName + '\'' +
-            ", hypervisor='" + hypervisor + '\'' +
-            ", isExtractable=" + isExtractable +
-            ", isFeatured=" + isFeatured +
-            ", isPublic=" + isPublic +
-            ", isReady=" + isReady +
-            ", jobId=" + jobId +
-            ", jobStatus='" + jobStatus + '\'' +
-            ", name='" + name + '\'' +
-            ", osTypeId=" + osTypeId +
-            ", osTypeName='" + osTypeName + '\'' +
-            ", passwordEnabled=" + passwordEnabled +
-            ", removed=" + removed +
-            ", size=" + size +
-            ", sourceTemplateId=" + sourceTemplateId +
-            ", status='" + status + '\'' +
-            ", templateTag='" + templateTag + '\'' +
-            ", templateType='" + templateType + '\'' +
-            ", zoneId=" + zoneId +
-            ", zoneName='" + zoneName + '\'' +
-            '}';
+      return string().toString();
    }
 
-   @Override
-   public int compareTo(ISO other) {
-      return id.compareTo(other.getId());
-   }
-
-   public enum ISOFilter {
-
-      featured, self, self_executable, executable, community, UNRECOGNIZED;
-
-      public static ISOFilter fromValue(String format) {
-         try {
-            return valueOf(checkNotNull(format, "format"));
-         } catch (IllegalArgumentException e) {
-            return UNRECOGNIZED;
-         }
-      }
-   }
 }
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/ISOExtraction.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/ISOExtraction.java
index 13e79e4..5b86042 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/ISOExtraction.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/ISOExtraction.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -18,309 +18,361 @@
  */
 package org.jclouds.cloudstack.domain;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.beans.ConstructorProperties;
 import java.util.Date;
 
+import javax.inject.Named;
+
+import org.jclouds.javax.annotation.Nullable;
+
 import com.google.common.base.Objects;
-import com.google.gson.annotations.SerializedName;
+import com.google.common.base.Objects.ToStringHelper;
 
 /**
+ * Class ISOExtraction
+ * 
  * @author Richard Downer
- */
-public class ISOExtraction implements Comparable<ISOExtraction> {
+*/
+public class ISOExtraction {
 
-   public static Builder builder() {
-      return new Builder();
+   public static Builder<?> builder() { 
+      return new ConcreteBuilder();
+   }
+   
+   public Builder<?> toBuilder() { 
+      return new ConcreteBuilder().fromISOExtraction(this);
    }
 
-   public static class Builder {
+   public static abstract class Builder<T extends Builder<T>>  {
+      protected abstract T self();
 
-      private String id;
-      private String accountId;
-      private Date created;
-      private String extractId;
-      private ExtractMode extractMode;
-      private String name;
-      private String state;
-      private String status;
-      private String storageType;
-      private int uploadPercentage;
-      private String url;
-      private String zoneId;
-      private String zoneName;
-
-      /**
-       * @param id the id of extracted object
+      protected String id;
+      protected String accountId;
+      protected Date created;
+      protected String extractId;
+      protected ExtractMode extractMode;
+      protected String name;
+      protected String state;
+      protected String status;
+      protected String storageType;
+      protected int uploadPercentage;
+      protected String url;
+      protected String zoneId;
+      protected String zoneName;
+   
+      /** 
+       * @see ISOExtraction#getId()
        */
-      public Builder id(String id) {
+      public T id(String id) {
          this.id = id;
-         return this;
+         return self();
       }
 
-      /**
-       * @param accountId the account id to which the extracted object belongs
+      /** 
+       * @see ISOExtraction#getAccountId()
        */
-      public Builder accountId(String accountId) {
+      public T accountId(String accountId) {
          this.accountId = accountId;
-         return this;
+         return self();
       }
 
-      /**
-       * @param created the time and date the object was created
+      /** 
+       * @see ISOExtraction#getCreated()
        */
-      public Builder created(Date created) {
+      public T created(Date created) {
          this.created = created;
-         return this;
+         return self();
       }
 
-      /**
-       * @param extractId the upload id of extracted object
+      /** 
+       * @see ISOExtraction#getExtractId()
        */
-      public Builder extractId(String extractId) {
+      public T extractId(String extractId) {
          this.extractId = extractId;
-         return this;
+         return self();
       }
 
-      /**
-       * @param extractMode the mode of extraction - upload or download
+      /** 
+       * @see ISOExtraction#getExtractMode()
        */
-      public Builder extractMode(ExtractMode extractMode) {
+      public T extractMode(ExtractMode extractMode) {
          this.extractMode = extractMode;
-         return this;
+         return self();
       }
 
-      /**
-       * @param name the name of the extracted object
+      /** 
+       * @see ISOExtraction#getName()
        */
-      public Builder name(String name) {
+      public T name(String name) {
          this.name = name;
-         return this;
+         return self();
       }
 
-      /**
-       * @param state the state of the extracted object
+      /** 
+       * @see ISOExtraction#getState()
        */
-      public Builder state(String state) {
+      public T state(String state) {
          this.state = state;
-         return this;
+         return self();
       }
 
-      /**
-       * @param status the status of the extraction
+      /** 
+       * @see ISOExtraction#getStatus()
        */
-      public Builder status(String status) {
+      public T status(String status) {
          this.status = status;
-         return this;
+         return self();
       }
 
-      /**
-       * @param storageType type of the storage
+      /** 
+       * @see ISOExtraction#getStorageType()
        */
-      public Builder storageType(String storageType) {
+      public T storageType(String storageType) {
          this.storageType = storageType;
-         return this;
+         return self();
       }
 
-      /**
-       * @param uploadPercentage the percentage of the entity uploaded to the specified location
+      /** 
+       * @see ISOExtraction#getUploadPercentage()
        */
-      public Builder uploadPercentage(int uploadPercentage) {
+      public T uploadPercentage(int uploadPercentage) {
          this.uploadPercentage = uploadPercentage;
-         return this;
+         return self();
       }
 
-      /**
-       * @param url if mode = upload then url of the uploaded entity. if mode = download the url from which the entity can be downloaded
+      /** 
+       * @see ISOExtraction#getUrl()
        */
-      public Builder url(String url) {
+      public T url(String url) {
          this.url = url;
-         return this;
+         return self();
       }
 
-      /**
-       * @param zoneId zone ID the object was extracted from
+      /** 
+       * @see ISOExtraction#getZoneId()
        */
-      public Builder zoneId(String zoneId) {
+      public T zoneId(String zoneId) {
          this.zoneId = zoneId;
-         return this;
+         return self();
       }
 
-      /**
-       * @param zoneName zone name the object was extracted from
+      /** 
+       * @see ISOExtraction#getZoneName()
        */
-      public Builder zoneName(String zoneName) {
+      public T zoneName(String zoneName) {
          this.zoneName = zoneName;
-         return this;
+         return self();
       }
 
+      public ISOExtraction build() {
+         return new ISOExtraction(id, accountId, created, extractId, extractMode, name, state, status, storageType, uploadPercentage, url, zoneId, zoneName);
+      }
+      
+      public T fromISOExtraction(ISOExtraction in) {
+         return this
+                  .id(in.getId())
+                  .accountId(in.getAccountId())
+                  .created(in.getCreated())
+                  .extractId(in.getExtractId())
+                  .extractMode(in.getExtractMode())
+                  .name(in.getName())
+                  .state(in.getState())
+                  .status(in.getStatus())
+                  .storageType(in.getStorageType())
+                  .uploadPercentage(in.getUploadPercentage())
+                  .url(in.getUrl())
+                  .zoneId(in.getZoneId())
+                  .zoneName(in.getZoneName());
+      }
    }
 
-   private String id;
-   @SerializedName("accountid")
-   private String accountId;
-   private Date created;
-   private String extractId;
-   private ExtractMode extractMode;
-   private String name;
-   private String state;
-   private String status;
-   @SerializedName("storagetype")
-   private String storageType;
-   @SerializedName("uploadpercentage")
-   private int uploadPercentage;
-   private String url;
-   @SerializedName("zoneid")
-   private String zoneId;
-   @SerializedName("zonename")
-   private String zoneName;
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
+   }
 
-   /**
-    * present only for serializer
-    */
-   ISOExtraction() {
+   private final String id;
+   @Named("accountid")
+   private final String accountId;
+   private final Date created;
+   private final String extractId;
+   private final ExtractMode extractMode;
+   private final String name;
+   private final String state;
+   private final String status;
+   @Named("storagetype")
+   private final String storageType;
+   @Named("uploadpercentage")
+   private final int uploadPercentage;
+   private final String url;
+   @Named("zoneid")
+   private final String zoneId;
+   @Named("zonename")
+   private final String zoneName;
+
+   @ConstructorProperties({
+      "id", "accountid", "created", "extractId", "extractMode", "name", "state", "status", "storagetype", "uploadpercentage", "url", "zoneid", "zonename"
+   })
+   protected ISOExtraction(String id, @Nullable String accountId, @Nullable Date created, @Nullable String extractId,
+                           @Nullable ExtractMode extractMode, @Nullable String name, @Nullable String state, @Nullable String status,
+                           @Nullable String storageType, int uploadPercentage, @Nullable String url, @Nullable String zoneId,
+                           @Nullable String zoneName) {
+      this.id = checkNotNull(id, "id");
+      this.accountId = accountId;
+      this.created = created;
+      this.extractId = extractId;
+      this.extractMode = extractMode;
+      this.name = name;
+      this.state = state;
+      this.status = status;
+      this.storageType = storageType;
+      this.uploadPercentage = uploadPercentage;
+      this.url = url;
+      this.zoneId = zoneId;
+      this.zoneName = zoneName;
    }
 
    /**
     * @return the id of extracted object
     */
    public String getId() {
-      return id;
+      return this.id;
    }
 
    /**
     * @return the account id to which the extracted object belongs
     */
+   @Nullable
    public String getAccountId() {
-      return accountId;
+      return this.accountId;
    }
 
    /**
     * @return the time and date the object was created
     */
+   @Nullable
    public Date getCreated() {
-      return created;
+      return this.created;
    }
 
    /**
     * @return the upload id of extracted object
     */
+   @Nullable
    public String getExtractId() {
-      return extractId;
+      return this.extractId;
    }
 
    /**
     * @return the mode of extraction - upload or download
     */
+   @Nullable
    public ExtractMode getExtractMode() {
-      return extractMode;
+      return this.extractMode;
    }
 
    /**
     * @return the name of the extracted object
     */
+   @Nullable
    public String getName() {
-      return name;
+      return this.name;
    }
 
    /**
     * @return the state of the extracted object
     */
+   @Nullable
    public String getState() {
-      return state;
+      return this.state;
    }
 
    /**
     * @return the status of the extraction
     */
+   @Nullable
    public String getStatus() {
-      return status;
+      return this.status;
    }
 
    /**
     * @return type of the storage
     */
+   @Nullable
    public String getStorageType() {
-      return storageType;
+      return this.storageType;
    }
 
    /**
     * @return the percentage of the entity uploaded to the specified location
     */
    public int getUploadPercentage() {
-      return uploadPercentage;
+      return this.uploadPercentage;
    }
 
    /**
     * @return if mode = upload then url of the uploaded entity. if mode = download the url from which the entity can be downloaded
     */
+   @Nullable
    public String getUrl() {
-      return url;
+      return this.url;
    }
 
    /**
     * @return zone ID the object was extracted from
     */
+   @Nullable
    public String getZoneId() {
-      return zoneId;
+      return this.zoneId;
    }
 
    /**
     * @return zone name the object was extracted from
     */
+   @Nullable
    public String getZoneName() {
-      return zoneName;
-   }
-
-   @Override
-   public boolean equals(Object o) {
-      if (this == o) return true;
-      if (o == null || getClass() != o.getClass()) return false;
-
-      ISOExtraction that = (ISOExtraction) o;
-
-      if (!Objects.equal(accountId, that.accountId)) return false;
-      if (!Objects.equal(extractId, that.extractId)) return false;
-      if (!Objects.equal(id, that.id)) return false;
-      if (!Objects.equal(uploadPercentage, that.uploadPercentage)) return false;
-      if (!Objects.equal(zoneId, that.zoneId)) return false;
-      if (!Objects.equal(created, that.created)) return false;
-      if (!Objects.equal(extractMode, that.extractMode)) return false;
-      if (!Objects.equal(name, that.name)) return false;
-      if (!Objects.equal(state, that.state)) return false;
-      if (!Objects.equal(status, that.status)) return false;
-      if (!Objects.equal(storageType, that.storageType)) return false;
-      if (!Objects.equal(url, that.url)) return false;
-      if (!Objects.equal(zoneName, that.zoneName)) return false;
-
-      return true;
+      return this.zoneName;
    }
 
    @Override
    public int hashCode() {
-       return Objects.hashCode(accountId, extractId, id, uploadPercentage, zoneId, created, extractMode, name, state, status, storageType, url, zoneName);
+      return Objects.hashCode(id, accountId, created, extractId, extractMode, name, state, status, storageType, uploadPercentage, url, zoneId, zoneName);
    }
 
    @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      ISOExtraction that = ISOExtraction.class.cast(obj);
+      return Objects.equal(this.id, that.id)
+               && Objects.equal(this.accountId, that.accountId)
+               && Objects.equal(this.created, that.created)
+               && Objects.equal(this.extractId, that.extractId)
+               && Objects.equal(this.extractMode, that.extractMode)
+               && Objects.equal(this.name, that.name)
+               && Objects.equal(this.state, that.state)
+               && Objects.equal(this.status, that.status)
+               && Objects.equal(this.storageType, that.storageType)
+               && Objects.equal(this.uploadPercentage, that.uploadPercentage)
+               && Objects.equal(this.url, that.url)
+               && Objects.equal(this.zoneId, that.zoneId)
+               && Objects.equal(this.zoneName, that.zoneName);
+   }
+   
+   protected ToStringHelper string() {
+      return Objects.toStringHelper(this)
+            .add("id", id).add("accountId", accountId).add("created", created).add("extractId", extractId).add("extractMode", extractMode)
+            .add("name", name).add("state", state).add("status", status).add("storageType", storageType).add("uploadPercentage", uploadPercentage)
+            .add("url", url).add("zoneId", zoneId).add("zoneName", zoneName);
+   }
+   
+   @Override
    public String toString() {
-      return "ISOExtraction{" +
-            "id=" + id +
-            ", accountId=" + accountId +
-            ", created=" + created +
-            ", extractId=" + extractId +
-            ", extractMode=" + extractMode +
-            ", name='" + name + '\'' +
-            ", state='" + state + '\'' +
-            ", status='" + status + '\'' +
-            ", storageType='" + storageType + '\'' +
-            ", uploadPercentage=" + uploadPercentage +
-            ", url='" + url + '\'' +
-            ", zoneId=" + zoneId +
-            ", zoneName='" + zoneName + '\'' +
-            '}';
-   }
-
-   @Override
-   public int compareTo(ISOExtraction other) {
-      return id.compareTo(other.getId());
+      return string().toString();
    }
 
 }
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/ISOPermissions.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/ISOPermissions.java
index 79d1efe..03dbda6 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/ISOPermissions.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/ISOPermissions.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -18,136 +18,169 @@
  */
 package org.jclouds.cloudstack.domain;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.beans.ConstructorProperties;
 import java.util.Set;
 
+import javax.inject.Named;
+
+import org.jclouds.javax.annotation.Nullable;
+
 import com.google.common.base.Objects;
-import com.google.gson.annotations.SerializedName;
+import com.google.common.base.Objects.ToStringHelper;
+import com.google.common.collect.ImmutableSet;
 
 /**
+ * Class ISOPermissions
+ * 
  * @author Richard Downer
- */
-public class ISOPermissions implements Comparable<ISOPermissions> {
+*/
+public class ISOPermissions {
 
-   public static Builder builder() {
-      return new Builder();
+   public static Builder<?> builder() { 
+      return new ConcreteBuilder();
+   }
+   
+   public Builder<?> toBuilder() { 
+      return new ConcreteBuilder().fromISOPermissions(this);
    }
 
-   public static class Builder {
+   public static abstract class Builder<T extends Builder<T>>  {
+      protected abstract T self();
 
-      private String id;
-      private String account;
-      private String domainId;
-      private boolean isPublic;
-
-      /**
-       * @param id the template ID
+      protected String id;
+      protected Set<String> accounts = ImmutableSet.of();
+      protected String domainId;
+      protected boolean isPublic;
+   
+      /** 
+       * @see ISOPermissions#getId()
        */
-      public Builder id(String id) {
+      public T id(String id) {
          this.id = id;
-         return this;
+         return self();
       }
 
-      /**
-       * @param account the list of accounts the template is available for
+      /** 
+       * @see ISOPermissions#getAccounts()
        */
-      public Builder account(String account) {
-         this.account = account;
-         return this;
+      public T accounts(Set<String> accounts) {
+         this.accounts = ImmutableSet.copyOf(checkNotNull(accounts, "accounts"));      
+         return self();
       }
 
-      /**
-       * @param domainId the ID of the domain to which the template belongs
+      public T accounts(String... in) {
+         return accounts(ImmutableSet.copyOf(in));
+      }
+
+      /** 
+       * @see ISOPermissions#getDomainId()
        */
-      public Builder domainId(String domainId) {
+      public T domainId(String domainId) {
          this.domainId = domainId;
-         return this;
+         return self();
       }
 
-      /**
-       * @param isPublic true if this template is a public template, false otherwise
+      /** 
+       * @see ISOPermissions#isPublic()
        */
-      public Builder isPublic(boolean isPublic) {
+      public T isPublic(boolean isPublic) {
          this.isPublic = isPublic;
-         return this;
+         return self();
       }
 
+      public ISOPermissions build() {
+         return new ISOPermissions(id, accounts, domainId, isPublic);
+      }
+      
+      public T fromISOPermissions(ISOPermissions in) {
+         return this
+                  .id(in.getId())
+                  .accounts(in.getAccounts())
+                  .domainId(in.getDomainId())
+                  .isPublic(in.isPublic());
+      }
    }
 
-   private String id;
-   @SerializedName("account")
-   private Set<String> accounts;
-   @SerializedName("domainid")
-   private String domainId;
-   @SerializedName("ispublic")
-   private boolean isPublic;
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
+   }
 
-   /**
-    * present only for serializer
-    */
-   ISOPermissions() {
+   private final String id;
+   @Named("account")
+   private final Set<String> accounts;
+   @Named("domainid")
+   private final String domainId;
+   @Named("ispublic")
+   private final boolean isPublic;
+
+   @ConstructorProperties({
+      "id", "account", "domainid", "ispublic"
+   })
+   protected ISOPermissions(String id, @Nullable Set<String> accounts,  @Nullable String domainId, boolean isPublic) {
+      this.id = checkNotNull(id, "id");
+      this.accounts = accounts == null ? ImmutableSet.<String>of() : ImmutableSet.copyOf(accounts);      
+      this.domainId = domainId;
+      this.isPublic = isPublic;
    }
 
    /**
     * @return the template ID
     */
    public String getId() {
-      return id;
+      return this.id;
    }
 
    /**
     * @return the list of accounts the template is available for
     */
    public Set<String> getAccounts() {
-      return accounts;
+      return this.accounts;
    }
 
    /**
     * @return the ID of the domain to which the template belongs
     */
+   @Nullable
    public String getDomainId() {
-      return domainId;
+      return this.domainId;
    }
 
    /**
     * @return true if this template is a public template, false otherwise
     */
-   public boolean getIsPublic() {
-      return isPublic;
-   }
-
-   @Override
-   public boolean equals(Object o) {
-      if (this == o) return true;
-      if (o == null || getClass() != o.getClass()) return false;
-
-      ISOPermissions that = (ISOPermissions) o;
-
-      if (!Objects.equal(accounts, that.accounts)) return false;
-      if (!Objects.equal(domainId, that.domainId)) return false;
-      if (!Objects.equal(id, that.id)) return false;
-      if (!Objects.equal(isPublic, that.isPublic)) return false;
-
-      return true;
+   public boolean isPublic() {
+      return this.isPublic;
    }
 
    @Override
    public int hashCode() {
-       return Objects.hashCode(accounts, domainId, id, isPublic);
+      return Objects.hashCode(id, accounts, domainId, isPublic);
    }
 
    @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      ISOPermissions that = ISOPermissions.class.cast(obj);
+      return Objects.equal(this.id, that.id)
+               && Objects.equal(this.accounts, that.accounts)
+               && Objects.equal(this.domainId, that.domainId)
+               && Objects.equal(this.isPublic, that.isPublic);
+   }
+   
+   protected ToStringHelper string() {
+      return Objects.toStringHelper(this)
+            .add("id", id).add("accounts", accounts).add("domainId", domainId).add("isPublic", isPublic);
+   }
+   
+   @Override
    public String toString() {
-      return "ISOPermissions{" +
-            "id=" + id +
-            ", accounts='" + accounts + '\'' +
-            ", domainId=" + domainId +
-            ", isPublic=" + isPublic +
-            '}';
-   }
-
-   @Override
-   public int compareTo(ISOPermissions other) {
-      return id.compareTo(other.getId());
+      return string().toString();
    }
 
 }
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/IngressRule.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/IngressRule.java
index ebff971..e400ca3 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/IngressRule.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/IngressRule.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -18,118 +18,170 @@
  */
 package org.jclouds.cloudstack.domain;
 
-import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.beans.ConstructorProperties;
+
+import javax.inject.Named;
+
+import org.jclouds.javax.annotation.Nullable;
 
 import com.google.common.base.Objects;
-import com.google.gson.annotations.SerializedName;
+import com.google.common.base.Objects.ToStringHelper;
 
 /**
  * 
  * @author Adrian Cole
- */
+*/
 public class IngressRule implements Comparable<IngressRule> {
-   public static Builder builder() {
-      return new Builder();
+
+   public static Builder<?> builder() { 
+      return new ConcreteBuilder();
+   }
+   
+   public Builder<?> toBuilder() { 
+      return new ConcreteBuilder().fromIngressRule(this);
    }
 
-   public static class Builder {
-      private String account;
-      private String CIDR;
-      private int endPort = -1;
-      private int ICMPCode = -1;
-      private int ICMPType = -1;
-      private String protocol;
-       private String id;
-      private String securityGroupName;
-      private int startPort = -1;
+   public static abstract class Builder<T extends Builder<T>>  {
+      protected abstract T self();
 
-      public Builder account(String account) {
+      protected String account;
+      protected String CIDR;
+      protected int endPort;
+      protected int ICMPCode;
+      protected int ICMPType;
+      protected String protocol;
+      protected String id;
+      protected String securityGroupName;
+      protected int startPort;
+   
+      /** 
+       * @see IngressRule#getAccount()
+       */
+      public T account(String account) {
          this.account = account;
-         return this;
+         return self();
       }
 
-      public Builder CIDR(String CIDR) {
+      /** 
+       * @see IngressRule#getCIDR()
+       */
+      public T CIDR(String CIDR) {
          this.CIDR = CIDR;
-         return this;
+         return self();
       }
 
-      public Builder endPort(int endPort) {
+      /** 
+       * @see IngressRule#getEndPort()
+       */
+      public T endPort(int endPort) {
          this.endPort = endPort;
-         return this;
+         return self();
       }
 
-      public Builder ICMPCode(int ICMPCode) {
+      /** 
+       * @see IngressRule#getICMPCode()
+       */
+      public T ICMPCode(int ICMPCode) {
          this.ICMPCode = ICMPCode;
-         return this;
+         return self();
       }
 
-      public Builder ICMPType(int ICMPType) {
+      /** 
+       * @see IngressRule#getICMPType()
+       */
+      public T ICMPType(int ICMPType) {
          this.ICMPType = ICMPType;
-         return this;
+         return self();
       }
 
-      public Builder protocol(String protocol) {
+      /** 
+       * @see IngressRule#getProtocol()
+       */
+      public T protocol(String protocol) {
          this.protocol = protocol;
-         return this;
+         return self();
       }
 
-      public Builder id(String id) {
+      /** 
+       * @see IngressRule#getId()
+       */
+      public T id(String id) {
          this.id = id;
-         return this;
+         return self();
       }
 
-      public Builder securityGroupName(String securityGroupName) {
+      /** 
+       * @see IngressRule#getSecurityGroupName()
+       */
+      public T securityGroupName(String securityGroupName) {
          this.securityGroupName = securityGroupName;
-         return this;
+         return self();
       }
 
-      public Builder startPort(int startPort) {
+      /** 
+       * @see IngressRule#getStartPort()
+       */
+      public T startPort(int startPort) {
          this.startPort = startPort;
-         return this;
+         return self();
       }
 
       public IngressRule build() {
          return new IngressRule(account, CIDR, endPort, ICMPCode, ICMPType, protocol, id, securityGroupName, startPort);
       }
+      
+      public T fromIngressRule(IngressRule in) {
+         return this
+                  .account(in.getAccount())
+                  .CIDR(in.getCIDR())
+                  .endPort(in.getEndPort())
+                  .ICMPCode(in.getICMPCode())
+                  .ICMPType(in.getICMPType())
+                  .protocol(in.getProtocol())
+                  .id(in.getId())
+                  .securityGroupName(in.getSecurityGroupName())
+                  .startPort(in.getStartPort());
+      }
    }
 
-   private String account;
-   @SerializedName("cidr")
-   private String CIDR;
-   @SerializedName("endport")
-   private int endPort = -1;
-   @SerializedName("icmpcode")
-   private int ICMPCode = -1;
-   @SerializedName("icmptype")
-   private int ICMPType = -1;
-   private String protocol;
-   @SerializedName("ruleid")
-   private String id;
-   @SerializedName("securitygroupname")
-   private String securityGroupName;
-   @SerializedName("startport")
-   private int startPort = -1;
-
-   // for serialization
-   IngressRule() {
-
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
    }
 
-   public IngressRule(String account, String CIDR, int endPort, int iCMPCode, int iCMPType, String protocol, String id,
-         String securityGroupName, int startPort) {
-      if (account == null)
-         checkArgument(securityGroupName == null && CIDR != null,
-               "if you do not specify an account and security group, you must specify a CIDR range");
-      if (CIDR == null)
-         checkArgument(account != null && securityGroupName != null,
-               "if you do not specify an account and security group, you must specify a CIDR range");
+   private final String account;
+   @Named("cidr")
+   private final String CIDR;
+   @Named("endport")
+   private final int endPort;
+   @Named("icmpcode")
+   private final int ICMPCode;
+   @Named("icmptype")
+   private final int ICMPType;
+   private final String protocol;
+   @Named("ruleid")
+   private final String id;
+   @Named("securitygroupname")
+   private final String securityGroupName;
+   @Named("startport")
+   private final int startPort;
+
+   @ConstructorProperties({
+      "account", "cidr", "endport", "icmpcode", "icmptype", "protocol", "ruleid", "securitygroupname", "startport"
+   })
+   protected IngressRule(@Nullable String account, @Nullable String CIDR, int endPort, int ICMPCode, int ICMPType,
+                         @Nullable String protocol, String id, @Nullable String securityGroupName, int startPort) {
       this.account = account;
       this.CIDR = CIDR;
       this.endPort = endPort;
-      this.ICMPCode = iCMPCode;
-      this.ICMPType = iCMPType;
+      this.ICMPCode = ICMPCode;
+      this.ICMPType = ICMPType;
       this.protocol = protocol;
-      this.id = id;
+      this.id = checkNotNull(id, "id");
       this.securityGroupName = securityGroupName;
       this.startPort = startPort;
    }
@@ -137,108 +189,104 @@
    /**
     * @return account owning the ingress rule
     */
+   @Nullable
    public String getAccount() {
-      return account;
+      return this.account;
    }
 
    /**
     * @return the CIDR notation for the base IP address of the ingress rule
     */
+   @Nullable
    public String getCIDR() {
-      return CIDR;
+      return this.CIDR;
    }
 
    /**
     * @return the ending IP of the ingress rule
     */
    public int getEndPort() {
-      return endPort;
+      return this.endPort;
    }
 
    /**
     * @return the code for the ICMP message response
     */
    public int getICMPCode() {
-      return ICMPCode;
+      return this.ICMPCode;
    }
 
    /**
     * @return the type of the ICMP message response
     */
    public int getICMPType() {
-      return ICMPType;
+      return this.ICMPType;
    }
 
    /**
     * @return the protocol of the ingress rule
     */
+   @Nullable
    public String getProtocol() {
-      return protocol;
+      return this.protocol;
    }
 
    /**
     * @return the id of the ingress rule
     */
    public String getId() {
-      return id;
+      return this.id;
    }
 
    /**
     * @return security group name
     */
+   @Nullable
    public String getSecurityGroupName() {
-      return securityGroupName;
+      return this.securityGroupName;
    }
 
    /**
     * @return the starting IP of the ingress rule
     */
    public int getStartPort() {
-      return startPort;
-   }
-
-   @Override
-   public boolean equals(Object o) {
-      if (this == o) return true;
-      if (o == null || getClass() != o.getClass()) return false;
-
-      IngressRule that = (IngressRule) o;
-
-      if (!Objects.equal(CIDR, that.CIDR)) return false;
-      if (!Objects.equal(ICMPCode, that.ICMPCode)) return false;
-      if (!Objects.equal(ICMPType, that.ICMPType)) return false;
-      if (!Objects.equal(account, that.account)) return false;
-      if (!Objects.equal(endPort, that.endPort)) return false;
-      if (!Objects.equal(id, that.id)) return false;
-      if (!Objects.equal(protocol, that.protocol)) return false;
-      if (!Objects.equal(securityGroupName, that.securityGroupName)) return false;
-      if (!Objects.equal(startPort, that.startPort)) return false;
-
-      return true;
+      return this.startPort;
    }
 
    @Override
    public int hashCode() {
-       return Objects.hashCode(CIDR, ICMPCode, ICMPType, account, endPort, id, protocol, securityGroupName, startPort);
+      return Objects.hashCode(account, CIDR, endPort, ICMPCode, ICMPType, protocol, id, securityGroupName, startPort);
    }
 
    @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      IngressRule that = IngressRule.class.cast(obj);
+      return Objects.equal(this.account, that.account)
+               && Objects.equal(this.CIDR, that.CIDR)
+               && Objects.equal(this.endPort, that.endPort)
+               && Objects.equal(this.ICMPCode, that.ICMPCode)
+               && Objects.equal(this.ICMPType, that.ICMPType)
+               && Objects.equal(this.protocol, that.protocol)
+               && Objects.equal(this.id, that.id)
+               && Objects.equal(this.securityGroupName, that.securityGroupName)
+               && Objects.equal(this.startPort, that.startPort);
+   }
+   
+   protected ToStringHelper string() {
+      return Objects.toStringHelper(this)
+            .add("account", account).add("CIDR", CIDR).add("endPort", endPort).add("ICMPCode", ICMPCode)
+            .add("ICMPType", ICMPType).add("protocol", protocol).add("id", id).add("securityGroupName", securityGroupName).add("startPort", startPort);
+   }
+   
+   @Override
    public String toString() {
-      return "IngressRule{" +
-            "account='" + account + '\'' +
-            ", CIDR='" + CIDR + '\'' +
-            ", endPort=" + endPort +
-            ", ICMPCode=" + ICMPCode +
-            ", ICMPType=" + ICMPType +
-            ", protocol='" + protocol + '\'' +
-            ", id=" + id +
-            ", securityGroupName='" + securityGroupName + '\'' +
-            ", startPort=" + startPort +
-            '}';
+      return string().toString();
    }
 
    @Override
-   public int compareTo(IngressRule arg0) {
-      return id.compareTo(arg0.getId());
+   public int compareTo(IngressRule o) {
+      return id.compareTo(o.getId());
    }
 }
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/JobResult.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/JobResult.java
index f6b3bc4..732e5e6 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/JobResult.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/JobResult.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -18,73 +18,115 @@
  */
 package org.jclouds.cloudstack.domain;
 
+import java.beans.ConstructorProperties;
+
+import javax.inject.Named;
+
+import org.jclouds.javax.annotation.Nullable;
+
 import com.google.common.base.Objects;
-import com.google.gson.annotations.SerializedName;
+import com.google.common.base.Objects.ToStringHelper;
 
 /**
  * The result of an operation.
- *
+ * 
  * A handful of Cloudstack API calls return this structure when there is no domain model data to return - for example,
  * when deleting an object.
- *
+ * 
  * @author Richard Downer
- */
-public class JobResult implements Comparable<JobResult> {
+*/
+public class JobResult {
 
-   private boolean success;
-   @SerializedName("displaytext")
-   private String displayText;
-
-   /**
-    * present only for the serializer
-    */
-   JobResult() {
+   public static Builder<?> builder() { 
+      return new ConcreteBuilder();
+   }
+   
+   public Builder<?> toBuilder() { 
+      return new ConcreteBuilder().fromJobResult(this);
    }
 
-   public JobResult(boolean success, String displayText) {
+   public static abstract class Builder<T extends Builder<T>>  {
+      protected abstract T self();
+
+      protected boolean success;
+      protected String displayText;
+   
+      /** 
+       * @see JobResult#isSuccess()
+       */
+      public T success(boolean success) {
+         this.success = success;
+         return self();
+      }
+
+      /** 
+       * @see JobResult#getDisplayText()
+       */
+      public T displayText(String displayText) {
+         this.displayText = displayText;
+         return self();
+      }
+
+      public JobResult build() {
+         return new JobResult(success, displayText);
+      }
+      
+      public T fromJobResult(JobResult in) {
+         return this
+                  .success(in.isSuccess())
+                  .displayText(in.getDisplayText());
+      }
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
+   }
+
+   private final boolean success;
+   @Named("displaytext")
+   private final String displayText;
+
+   @ConstructorProperties({
+      "success", "displaytext"
+   })
+   protected JobResult(boolean success, @Nullable String displayText) {
       this.success = success;
       this.displayText = displayText;
    }
 
-   public boolean getSuccess() {
-      return success;
+   public boolean isSuccess() {
+      return this.success;
    }
 
+   @Nullable
    public String getDisplayText() {
-      return displayText;
-   }
-
-   @Override
-   public boolean equals(Object o) {
-      if (this == o) return true;
-      if (o == null || getClass() != o.getClass()) return false;
-
-      JobResult that = (JobResult) o;
-
-      if (!Objects.equal(success, that.success)) return false;
-      if (!Objects.equal(displayText, that.displayText)) return false;
-
-      return true;
+      return this.displayText;
    }
 
    @Override
    public int hashCode() {
-       return Objects.hashCode(success, displayText);
+      return Objects.hashCode(success, displayText);
    }
 
    @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      JobResult that = JobResult.class.cast(obj);
+      return Objects.equal(this.success, that.success)
+               && Objects.equal(this.displayText, that.displayText);
+   }
+   
+   protected ToStringHelper string() {
+      return Objects.toStringHelper(this).add("success", success).add("displayText", displayText);
+   }
+   
+   @Override
    public String toString() {
-      return "JobResult{" +
-            "success=" + success +
-            ", displayText='" + displayText + '\'' +
-            '}';
+      return string().toString();
    }
 
-   @Override
-   public int compareTo(JobResult other) {
-      int comparison = Boolean.valueOf(success).compareTo(other.success);
-      if (comparison == 0)
-         comparison = displayText.compareTo(other.displayText);
-      return comparison;
-   }
 }
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/LoadBalancerRule.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/LoadBalancerRule.java
index 28165de..3b1654d 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/LoadBalancerRule.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/LoadBalancerRule.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -20,180 +20,264 @@
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import java.beans.ConstructorProperties;
 import java.util.Set;
 
+import javax.inject.Named;
+
+import org.jclouds.javax.annotation.Nullable;
+
 import com.google.common.base.CaseFormat;
 import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
 import com.google.common.collect.ImmutableSet;
-import com.google.gson.annotations.SerializedName;
 
 /**
+ * Class LoadBalancerRule
+ * 
  * @author Adrian Cole
- */
-public class LoadBalancerRule implements Comparable<LoadBalancerRule> {
+*/
+public class LoadBalancerRule {
+
+   /**
+    */
    public static enum State {
       ADD, ACTIVE, UNRECOGNIZED;
-
+      
       @Override
       public String toString() {
-         return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, name());
+      return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, name());
       }
-
+      
       public static State fromValue(String state) {
-         try {
-            return valueOf(CaseFormat.UPPER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, checkNotNull(state, "state")));
-         } catch (IllegalArgumentException e) {
-            return UNRECOGNIZED;
-         }
+      try {
+      return valueOf(CaseFormat.UPPER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, checkNotNull(state, "state")));
+      } catch (IllegalArgumentException e) {
+      return UNRECOGNIZED;
       }
-
+      }
+      
    }
 
    public static enum Algorithm {
       SOURCE, ROUNDROBIN, LEASTCONN, UNRECOGNIZED;
-
+      
       @Override
       public String toString() {
-         return name().toLowerCase();
+      return name().toLowerCase();
       }
-
+      
       public static Algorithm fromValue(String algorithm) {
-         try {
-            return Algorithm.valueOf(checkNotNull(algorithm, "algorithm").toUpperCase());
-         } catch (IllegalArgumentException e) {
-            return UNRECOGNIZED;
-         }
+      try {
+      return Algorithm.valueOf(checkNotNull(algorithm, "algorithm").toUpperCase());
+      } catch (IllegalArgumentException e) {
+      return UNRECOGNIZED;
       }
-
+      }
+      
    }
 
-   public static Builder builder() {
-      return new Builder();
+   public static Builder<?> builder() { 
+      return new ConcreteBuilder();
+   }
+   
+   public Builder<?> toBuilder() { 
+      return new ConcreteBuilder().fromLoadBalancerRule(this);
    }
 
-   public static class Builder {
-      private String id;
-      private String account;
-      private Algorithm algorithm;
-      private String description;
-      private String domain;
-      private String domainId;
-      private String name;
-      private int privatePort;
-      private String publicIP;
-      private String publicIPId;
-      private int publicPort;
-      private State state;
-      private Set<String> CIDRs = ImmutableSet.of();
-      private String zoneId;
+   public static abstract class Builder<T extends Builder<T>>  {
+      protected abstract T self();
 
-      public Builder id(String id) {
+      protected String id;
+      protected String account;
+      protected LoadBalancerRule.Algorithm algorithm;
+      protected String description;
+      protected String domain;
+      protected String domainId;
+      protected String name;
+      protected int privatePort;
+      protected String publicIP;
+      protected String publicIPId;
+      protected int publicPort;
+      protected LoadBalancerRule.State state;
+      protected Set<String> CIDRs = ImmutableSet.of();
+      protected String zoneId;
+   
+      /** 
+       * @see LoadBalancerRule#getId()
+       */
+      public T id(String id) {
          this.id = id;
-         return this;
+         return self();
       }
 
-      public Builder account(String account) {
+      /** 
+       * @see LoadBalancerRule#getAccount()
+       */
+      public T account(String account) {
          this.account = account;
-         return this;
+         return self();
       }
 
-      public Builder algorithm(Algorithm algorithm) {
+      /** 
+       * @see LoadBalancerRule#getAlgorithm()
+       */
+      public T algorithm(LoadBalancerRule.Algorithm algorithm) {
          this.algorithm = algorithm;
-         return this;
+         return self();
       }
 
-      public Builder description(String description) {
+      /** 
+       * @see LoadBalancerRule#getDescription()
+       */
+      public T description(String description) {
          this.description = description;
-         return this;
+         return self();
       }
 
-      public Builder domain(String domain) {
+      /** 
+       * @see LoadBalancerRule#getDomain()
+       */
+      public T domain(String domain) {
          this.domain = domain;
-         return this;
+         return self();
       }
 
-      public Builder domainId(String domainId) {
+      /** 
+       * @see LoadBalancerRule#getDomainId()
+       */
+      public T domainId(String domainId) {
          this.domainId = domainId;
-         return this;
+         return self();
       }
 
-      public Builder name(String name) {
+      /** 
+       * @see LoadBalancerRule#getName()
+       */
+      public T name(String name) {
          this.name = name;
-         return this;
+         return self();
       }
 
-      public Builder privatePort(int privatePort) {
+      /** 
+       * @see LoadBalancerRule#getPrivatePort()
+       */
+      public T privatePort(int privatePort) {
          this.privatePort = privatePort;
-         return this;
+         return self();
       }
 
-      public Builder publicIP(String publicIP) {
+      /** 
+       * @see LoadBalancerRule#getPublicIP()
+       */
+      public T publicIP(String publicIP) {
          this.publicIP = publicIP;
-         return this;
+         return self();
       }
 
-      public Builder publicIPId(String publicIPId) {
+      /** 
+       * @see LoadBalancerRule#getPublicIPId()
+       */
+      public T publicIPId(String publicIPId) {
          this.publicIPId = publicIPId;
-         return this;
+         return self();
       }
 
-      public Builder publicPort(int publicPort) {
+      /** 
+       * @see LoadBalancerRule#getPublicPort()
+       */
+      public T publicPort(int publicPort) {
          this.publicPort = publicPort;
-         return this;
+         return self();
       }
 
-      public Builder state(State state) {
+      /** 
+       * @see LoadBalancerRule#getState()
+       */
+      public T state(LoadBalancerRule.State state) {
          this.state = state;
-         return this;
+         return self();
       }
 
-      public Builder CIDRs(Set<String> CIDRs) {
-         this.CIDRs = CIDRs;
-         return this;
+      /** 
+       * @see LoadBalancerRule#getCIDRs()
+       */
+      public T CIDRs(Set<String> CIDRs) {
+         this.CIDRs = ImmutableSet.copyOf(checkNotNull(CIDRs, "CIDRs"));      
+         return self();
       }
 
-      public Builder zoneId(String zoneId) {
+      public T CIDRs(String... in) {
+         return CIDRs(ImmutableSet.copyOf(in));
+      }
+
+      /** 
+       * @see LoadBalancerRule#getZoneId()
+       */
+      public T zoneId(String zoneId) {
          this.zoneId = zoneId;
-         return this;
+         return self();
       }
 
       public LoadBalancerRule build() {
-         return new LoadBalancerRule(id, account, algorithm, description, domain, domainId, name, privatePort,
-               publicIP, publicIPId, publicPort, state, zoneId, CIDRs);
+         return new LoadBalancerRule(id, account, algorithm, description, domain, domainId, name, privatePort, publicIP, publicIPId, publicPort, state, CIDRs, zoneId);
+      }
+      
+      public T fromLoadBalancerRule(LoadBalancerRule in) {
+         return this
+                  .id(in.getId())
+                  .account(in.getAccount())
+                  .algorithm(in.getAlgorithm())
+                  .description(in.getDescription())
+                  .domain(in.getDomain())
+                  .domainId(in.getDomainId())
+                  .name(in.getName())
+                  .privatePort(in.getPrivatePort())
+                  .publicIP(in.getPublicIP())
+                  .publicIPId(in.getPublicIPId())
+                  .publicPort(in.getPublicPort())
+                  .state(in.getState())
+                  .CIDRs(in.getCIDRs())
+                  .zoneId(in.getZoneId());
       }
    }
 
-   private String id;
-   private String account;
-   private Algorithm algorithm;
-   private String description;
-   private String domain;
-   @SerializedName("domainid")
-   private String domainId;
-   private String name;
-   @SerializedName("privateport")
-   private int privatePort;
-   @SerializedName("publicip")
-   private String publicIP;
-   @SerializedName("publicipid")
-   private String publicIPId;
-   @SerializedName("publicport")
-   private int publicPort;
-   private State state;
-   @SerializedName("cidrlist")
-   private Set<String> CIDRs;
-   @SerializedName("zoneId")
-   private String zoneId;
-
-   // for deserializer
-   LoadBalancerRule() {
-
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
    }
 
-   public LoadBalancerRule(String id, String account, Algorithm algorithm, String description, String domain,
-                           String domainId, String name, int privatePort, String publicIP, String publicIPId, int publicPort, State state,
-                           String zoneId, Set<String> CIDRs) {
-      this.id = id;
+   private final String id;
+   private final String account;
+   private final LoadBalancerRule.Algorithm algorithm;
+   private final String description;
+   private final String domain;
+   @Named("domainid")
+   private final String domainId;
+   private final String name;
+   @Named("privateport")
+   private final int privatePort;
+   @Named("publicip")
+   private final String publicIP;
+   @Named("publicipid")
+   private final String publicIPId;
+   @Named("publicport")
+   private final int publicPort;
+   private final LoadBalancerRule.State state;
+   @Named("cidrlist")
+   private final Set<String> CIDRs;
+   private final String zoneId;
+
+   @ConstructorProperties({
+      "id", "account", "algorithm", "description", "domain", "domainid", "name", "privateport", "publicip", "publicipid", "publicport", "state", "cidrlist", "zoneId"
+   })
+   protected LoadBalancerRule(String id, @Nullable String account, @Nullable LoadBalancerRule.Algorithm algorithm,
+                              @Nullable String description, @Nullable String domain, @Nullable String domainId, @Nullable String name,
+                              int privatePort, @Nullable String publicIP, @Nullable String publicIPId, int publicPort,
+                              @Nullable LoadBalancerRule.State state, @Nullable Set<String> CIDRs, @Nullable String zoneId) {
+      this.id = checkNotNull(id, "id");
       this.account = account;
       this.algorithm = algorithm;
       this.description = description;
@@ -205,162 +289,152 @@
       this.publicIPId = publicIPId;
       this.publicPort = publicPort;
       this.state = state;
+      this.CIDRs = CIDRs == null ? ImmutableSet.<String>of() : ImmutableSet.copyOf(CIDRs);      
       this.zoneId = zoneId;
-      this.CIDRs = ImmutableSet.copyOf(CIDRs);
-
    }
 
    /**
     * @return the load balancer rule ID
     */
    public String getId() {
-      return id;
+      return this.id;
    }
 
    /**
     * @return the account of the load balancer rule
     */
+   @Nullable
    public String getAccount() {
-      return account;
+      return this.account;
    }
 
    /**
     * @return the load balancer algorithm (source, roundrobin, leastconn)
     */
-   public Algorithm getAlgorithm() {
-      return algorithm;
+   @Nullable
+   public LoadBalancerRule.Algorithm getAlgorithm() {
+      return this.algorithm;
    }
 
    /**
     * @return the description of the load balancer
     */
+   @Nullable
    public String getDescription() {
-      return description;
+      return this.description;
    }
 
    /**
     * @return the domain of the load balancer rule
     */
+   @Nullable
    public String getDomain() {
-      return domain;
+      return this.domain;
    }
 
    /**
     * @return the domain ID of the load balancer rule
     */
+   @Nullable
    public String getDomainId() {
-      return domainId;
+      return this.domainId;
    }
 
    /**
     * @return the name of the load balancer
     */
+   @Nullable
    public String getName() {
-      return name;
+      return this.name;
    }
 
    /**
     * @return the private port
     */
    public int getPrivatePort() {
-      return privatePort;
+      return this.privatePort;
    }
 
    /**
     * @return the public ip address
     */
+   @Nullable
    public String getPublicIP() {
-      return publicIP;
+      return this.publicIP;
    }
 
    /**
     * @return the public ip address id
     */
+   @Nullable
    public String getPublicIPId() {
-      return publicIPId;
+      return this.publicIPId;
    }
 
    /**
     * @return the public port
     */
    public int getPublicPort() {
-      return publicPort;
+      return this.publicPort;
    }
 
    /**
     * @return the state of the rule
     */
-   public State getState() {
-      return state;
+   @Nullable
+   public LoadBalancerRule.State getState() {
+      return this.state;
    }
 
    /**
     * @return the cidr list to forward traffic from
     */
    public Set<String> getCIDRs() {
-      return CIDRs;
+      return this.CIDRs;
    }
 
    /**
     * @return the id of the zone the rule beStrings to
     */
+   @Nullable
    public String getZoneId() {
-      return zoneId;
-   }
-
-   @Override
-   public int compareTo(LoadBalancerRule arg0) {
-      return id.compareTo(arg0.getId());
-   }
-
-
-   @Override
-   public boolean equals(Object o) {
-      if (this == o) return true;
-      if (o == null || getClass() != o.getClass()) return false;
-
-      LoadBalancerRule that = (LoadBalancerRule) o;
-
-      if (!Objects.equal(account, that.account)) return false;
-      if (!Objects.equal(algorithm, that.algorithm)) return false;
-      if (!Objects.equal(description, that.description)) return false;
-      if (!Objects.equal(domain, that.domain)) return false;
-      if (!Objects.equal(domainId, that.domainId)) return false;
-      if (!Objects.equal(id, that.id)) return false;
-      if (!Objects.equal(name, that.name)) return false;
-      if (!Objects.equal(privatePort, that.privatePort)) return false;
-      if (!Objects.equal(publicIP, that.publicIP)) return false;
-      if (!Objects.equal(publicIPId, that.publicIPId)) return false;
-      if (!Objects.equal(publicPort, that.publicPort)) return false;
-      if (!Objects.equal(zoneId, that.zoneId)) return false;
-      if (!Objects.equal(state, that.state)) return false;
-
-      return true;
+      return this.zoneId;
    }
 
    @Override
    public int hashCode() {
-       return Objects.hashCode(account, algorithm, description, domain, domainId, id, name, privatePort, publicIP, publicIPId, publicPort, zoneId, state);
+      return Objects.hashCode(id, account, algorithm, description, domain, domainId, name, privatePort, publicIP, publicIPId, publicPort, state, CIDRs, zoneId);
    }
 
    @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      LoadBalancerRule that = LoadBalancerRule.class.cast(obj);
+      return Objects.equal(this.id, that.id)
+               && Objects.equal(this.account, that.account)
+               && Objects.equal(this.algorithm, that.algorithm)
+               && Objects.equal(this.description, that.description)
+               && Objects.equal(this.domain, that.domain)
+               && Objects.equal(this.domainId, that.domainId)
+               && Objects.equal(this.name, that.name)
+               && Objects.equal(this.privatePort, that.privatePort)
+               && Objects.equal(this.publicIP, that.publicIP)
+               && Objects.equal(this.publicIPId, that.publicIPId)
+               && Objects.equal(this.publicPort, that.publicPort)
+               && Objects.equal(this.state, that.state)
+               && Objects.equal(this.CIDRs, that.CIDRs)
+               && Objects.equal(this.zoneId, that.zoneId);
+   }
+   
+   protected ToStringHelper string() {
+      return Objects.toStringHelper(this)
+            .add("id", id).add("account", account).add("algorithm", algorithm).add("description", description).add("domain", domain).add("domainId", domainId).add("name", name).add("privatePort", privatePort).add("publicIP", publicIP).add("publicIPId", publicIPId).add("publicPort", publicPort).add("state", state).add("CIDRs", CIDRs).add("zoneId", zoneId);
+   }
+   
+   @Override
    public String toString() {
-      return "LoadBalancerRule{" +
-            "id=" + id +
-            ", account='" + account + '\'' +
-            ", algorithm=" + algorithm +
-            ", description='" + description + '\'' +
-            ", domain='" + domain + '\'' +
-            ", domainId=" + domainId +
-            ", name='" + name + '\'' +
-            ", privatePort=" + privatePort +
-            ", publicIP='" + publicIP + '\'' +
-            ", publicIPId=" + publicIPId +
-            ", publicPort=" + publicPort +
-            ", state=" + state +
-            ", CIDRs=" + CIDRs +
-            ", zoneId=" + zoneId +
-            '}';
+      return string().toString();
    }
 
 }
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/LoginResponse.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/LoginResponse.java
index 8fb36e4..aac15b6 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/LoginResponse.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/LoginResponse.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -16,160 +16,219 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
 package org.jclouds.cloudstack.domain;
 
+import java.beans.ConstructorProperties;
+
+import javax.inject.Named;
+
+import org.jclouds.javax.annotation.Nullable;
+
 import com.google.common.base.Objects;
-import com.google.gson.annotations.SerializedName;
+import com.google.common.base.Objects.ToStringHelper;
 
 /**
  * Representation of the login API call response
  * 
  * @author Andrei Savu
- */
-public class LoginResponse implements Comparable<LoginResponse> {
+*/
+public class LoginResponse {
 
-   public static Builder builder() {
-      return new Builder();
+   public static Builder<?> builder() { 
+      return new ConcreteBuilder();
+   }
+   
+   public Builder<?> toBuilder() { 
+      return new ConcreteBuilder().fromLoginResponse(this);
    }
 
-   public static class Builder {
+   public static abstract class Builder<T extends Builder<T>>  {
+      protected abstract T self();
 
-      private String username;
-      private String userId;
-      private String password;
-      private String domainId;
-      private long timeout;
-      private boolean registered;
-      private String accountName;
-      private String firstName;
-      private String lastName;
-      private Account.Type accountType;
-      private String timezone;
-      private String timezoneOffset;
-      private String sessionKey;
-      private String jSessionId;
-
-      public Builder copyOf(LoginResponse r) {
-         this.username = r.username;
-         this.userId = r.userId;
-         this.password = r.password;
-         this.domainId = r.domainId;
-         this.timeout = r.timeout;
-         this.registered = r.registered;
-         this.accountName = r.accountName;
-         this.firstName = r.firstName;
-         this.lastName = r.lastName;
-         this.accountType = r.accountType;
-         this.timezone = r.timezone;
-         this.timezoneOffset = r.timezoneOffset;
-         this.sessionKey = r.sessionKey;
-         this.jSessionId = r.jSessionId;
-         return this;
-      }
-
-      public Builder username(String username) {
+      protected String username;
+      protected String userId;
+      protected String password;
+      protected String domainId;
+      protected long timeout;
+      protected boolean registered;
+      protected String accountName;
+      protected String firstName;
+      protected String lastName;
+      protected Account.Type accountType;
+      protected String timezone;
+      protected String timezoneOffset;
+      protected String sessionKey;
+      protected String jSessionId;
+   
+      /** 
+       * @see LoginResponse#getUsername()
+       */
+      public T username(String username) {
          this.username = username;
-         return this;
+         return self();
       }
 
-      public Builder userId(String userId) {
+      /** 
+       * @see LoginResponse#getUserId()
+       */
+      public T userId(String userId) {
          this.userId = userId;
-         return this;
+         return self();
       }
 
-      public Builder password(String password) {
+      /** 
+       * @see LoginResponse#getPassword()
+       */
+      public T password(String password) {
          this.password = password;
-         return this;
+         return self();
       }
 
-      public Builder domainId(String domainId) {
+      /** 
+       * @see LoginResponse#getDomainId()
+       */
+      public T domainId(String domainId) {
          this.domainId = domainId;
-         return this;
+         return self();
       }
 
-      public Builder timeout(long timeout) {
+      /** 
+       * @see LoginResponse#getTimeout()
+       */
+      public T timeout(long timeout) {
          this.timeout = timeout;
-         return this;
+         return self();
       }
 
-      public Builder registered(boolean registered) {
+      /** 
+       * @see LoginResponse#isRegistered()
+       */
+      public T registered(boolean registered) {
          this.registered = registered;
-         return this;
+         return self();
       }
 
-      public Builder accountName(String accountName) {
+      /** 
+       * @see LoginResponse#getAccountName()
+       */
+      public T accountName(String accountName) {
          this.accountName = accountName;
-         return this;
+         return self();
       }
 
-      public Builder firstName(String firstName) {
+      /** 
+       * @see LoginResponse#getFirstName()
+       */
+      public T firstName(String firstName) {
          this.firstName = firstName;
-         return this;
+         return self();
       }
 
-      public Builder lastName(String lastName) {
+      /** 
+       * @see LoginResponse#getLastName()
+       */
+      public T lastName(String lastName) {
          this.lastName = lastName;
-         return this;
+         return self();
       }
 
-      public Builder accountType(Account.Type accountType) {
+      /** 
+       * @see LoginResponse#getAccountType()
+       */
+      public T accountType(Account.Type accountType) {
          this.accountType = accountType;
-         return this;
+         return self();
       }
 
-      public Builder timezone(String timezone) {
+      /** 
+       * @see LoginResponse#getTimezone()
+       */
+      public T timezone(String timezone) {
          this.timezone = timezone;
-         return this;
+         return self();
       }
 
-      public Builder timezoneOffset(String timezoneOffset) {
+      /** 
+       * @see LoginResponse#getTimezoneOffset()
+       */
+      public T timezoneOffset(String timezoneOffset) {
          this.timezoneOffset = timezoneOffset;
-         return this;
+         return self();
       }
 
-      public Builder sessionKey(String sessionKey) {
+      /** 
+       * @see LoginResponse#getSessionKey()
+       */
+      public T sessionKey(String sessionKey) {
          this.sessionKey = sessionKey;
-         return this;
+         return self();
       }
 
-      public Builder jSessionId(String jSessionId) {
+      /** 
+       * @see LoginResponse#getJSessionId()
+       */
+      public T jSessionId(String jSessionId) {
          this.jSessionId = jSessionId;
-         return this;
+         return self();
       }
 
       public LoginResponse build() {
-         return new LoginResponse(username, userId, password, domainId, timeout, registered, accountName, firstName,
-                  lastName, accountType, timezone, timezoneOffset, sessionKey, jSessionId);
+         return new LoginResponse(username, userId, password, domainId, timeout, registered, accountName, firstName, lastName, accountType, timezone, timezoneOffset, sessionKey, jSessionId);
+      }
+      
+      public T fromLoginResponse(LoginResponse in) {
+         return this
+                  .username(in.getUsername())
+                  .userId(in.getUserId())
+                  .password(in.getPassword())
+                  .domainId(in.getDomainId())
+                  .timeout(in.getTimeout())
+                  .registered(in.isRegistered())
+                  .accountName(in.getAccountName())
+                  .firstName(in.getFirstName())
+                  .lastName(in.getLastName())
+                  .accountType(in.getAccountType())
+                  .timezone(in.getTimezone())
+                  .timezoneOffset(in.getTimezoneOffset())
+                  .sessionKey(in.getSessionKey())
+                  .jSessionId(in.getJSessionId());
+      }
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
       }
    }
 
    private final String username;
-   @SerializedName("userid")
+   @Named("userid")
    private final String userId;
    private final String password;
-   @SerializedName("domainid")
+   @Named("domainid")
    private final String domainId;
    private final long timeout;
    private final boolean registered;
-   @SerializedName("account")
+   @Named("account")
    private final String accountName;
-   @SerializedName("firstname")
+   @Named("firstname")
    private final String firstName;
-   @SerializedName("lastname")
+   @Named("lastname")
    private final String lastName;
-   @SerializedName("type")
+   @Named("type")
    private final Account.Type accountType;
    private final String timezone;
-   @SerializedName("timezoneoffset")
+   @Named("timezoneoffset")
    private final String timezoneOffset;
-   @SerializedName("sessionkey")
+   @Named("sessionkey")
    private final String sessionKey;
    private final String jSessionId;
 
-   public LoginResponse(String username, String userId, String password, String domainId, long timeout, boolean registered,
-            String accountName, String firstName, String lastName, Account.Type accountType, String timezone,
-            String timezoneOffset, String sessionKey, String jSessionId) {
+   @ConstructorProperties({
+      "username", "userid", "password", "domainid", "timeout", "registered", "account", "firstname", "lastname", "type", "timezone", "timezoneoffset", "sessionkey", "jSessionId"
+   })
+   protected LoginResponse(@Nullable String username, @Nullable String userId, @Nullable String password, @Nullable String domainId, long timeout, boolean registered, @Nullable String accountName, @Nullable String firstName, @Nullable String lastName, @Nullable Account.Type accountType, @Nullable String timezone, @Nullable String timezoneOffset, @Nullable String sessionKey, @Nullable String jSessionId) {
       this.username = username;
       this.userId = userId;
       this.password = password;
@@ -186,107 +245,108 @@
       this.jSessionId = jSessionId;
    }
 
+   @Nullable
    public String getUsername() {
-      return username;
+      return this.username;
    }
 
+   @Nullable
    public String getUserId() {
-      return userId;
+      return this.userId;
    }
 
+   @Nullable
    public String getPassword() {
-      return password;
+      return this.password;
    }
 
+   @Nullable
    public String getDomainId() {
-      return domainId;
+      return this.domainId;
    }
 
    public long getTimeout() {
-      return timeout;
+      return this.timeout;
    }
 
    public boolean isRegistered() {
-      return registered;
+      return this.registered;
    }
 
+   @Nullable
    public String getAccountName() {
-      return accountName;
+      return this.accountName;
    }
 
+   @Nullable
    public String getFirstName() {
-      return firstName;
+      return this.firstName;
    }
 
+   @Nullable
    public String getLastName() {
-      return lastName;
+      return this.lastName;
    }
 
+   @Nullable
    public Account.Type getAccountType() {
-      return accountType;
+      return this.accountType;
    }
 
+   @Nullable
    public String getTimezone() {
-      return timezone;
+      return this.timezone;
    }
 
+   @Nullable
    public String getTimezoneOffset() {
-      return timezoneOffset;
+      return this.timezoneOffset;
    }
 
+   @Nullable
    public String getSessionKey() {
-      return sessionKey;
+      return this.sessionKey;
    }
 
+   @Nullable
    public String getJSessionId() {
-      return jSessionId;
-   }
-
-   @Override
-   public boolean equals(Object o) {
-      if (this == o) return true;
-      if (o == null || getClass() != o.getClass()) return false;
-
-      LoginResponse that = (LoginResponse) o;
-
-      if (!Objects.equal(accountName, that.accountName)) return false;
-      if (!Objects.equal(accountType, that.accountType)) return false;
-      if (!Objects.equal(domainId, that.domainId)) return false;
-      if (!Objects.equal(firstName, that.firstName)) return false;
-      if (!Objects.equal(jSessionId, that.jSessionId)) return false;
-      if (!Objects.equal(lastName, that.lastName)) return false;
-      if (!Objects.equal(password, that.password)) return false;
-      if (!Objects.equal(registered, that.registered)) return false;
-      if (!Objects.equal(sessionKey, that.sessionKey)) return false;
-      if (!Objects.equal(timeout, that.timeout)) return false;
-      if (!Objects.equal(timezone, that.timezone)) return false;
-      if (!Objects.equal(timezoneOffset, that.timezoneOffset)) return false;
-      if (!Objects.equal(userId, that.userId)) return false;
-      if (!Objects.equal(username, that.username)) return false;
-
-      return true;
+      return this.jSessionId;
    }
 
    @Override
    public int hashCode() {
-       return Objects.hashCode(accountName, accountType, domainId, firstName, jSessionId, lastName,
-                               password, registered, sessionKey, timeout, timezone, timezoneOffset,
-                               userId, username);
+      return Objects.hashCode(username, userId, password, domainId, timeout, registered, accountName, firstName, lastName, accountType, timezone, timezoneOffset, sessionKey, jSessionId);
    }
 
    @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      LoginResponse that = LoginResponse.class.cast(obj);
+      return Objects.equal(this.username, that.username)
+               && Objects.equal(this.userId, that.userId)
+               && Objects.equal(this.password, that.password)
+               && Objects.equal(this.domainId, that.domainId)
+               && Objects.equal(this.timeout, that.timeout)
+               && Objects.equal(this.registered, that.registered)
+               && Objects.equal(this.accountName, that.accountName)
+               && Objects.equal(this.firstName, that.firstName)
+               && Objects.equal(this.lastName, that.lastName)
+               && Objects.equal(this.accountType, that.accountType)
+               && Objects.equal(this.timezone, that.timezone)
+               && Objects.equal(this.timezoneOffset, that.timezoneOffset)
+               && Objects.equal(this.sessionKey, that.sessionKey)
+               && Objects.equal(this.jSessionId, that.jSessionId);
+   }
+   
+   protected ToStringHelper string() {
+      return Objects.toStringHelper(this)
+            .add("username", username).add("userId", userId).add("password", password).add("domainId", domainId).add("timeout", timeout).add("registered", registered).add("accountName", accountName).add("firstName", firstName).add("lastName", lastName).add("accountType", accountType).add("timezone", timezone).add("timezoneOffset", timezoneOffset).add("sessionKey", sessionKey).add("jSessionId", jSessionId);
+   }
+   
+   @Override
    public String toString() {
-      return "LoginResponse{" + "username='" + username + '\'' + ", userId=" + userId + ", password='" + password
-               + '\'' + ", domainId=" + domainId + ", timeout=" + timeout + ", registered=" + registered
-               + ", accountName='" + accountName + '\'' + ", firstName='" + firstName + '\'' + ", lastName='"
-               + lastName + '\'' + ", accountType=" + accountType + ", timezone='" + timezone + '\''
-               + ", timezoneOffset='" + timezoneOffset + '\'' + ", sessionKey='" + sessionKey + '\'' + ", jSessionId='"
-               + jSessionId + '\'' + '}';
-   }
-
-   @Override
-   public int compareTo(LoginResponse arg0) {
-      return sessionKey.compareTo(arg0.getSessionKey());
+      return string().toString();
    }
 
 }
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/NIC.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/NIC.java
index 6ecf585..e9c9b8d 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/NIC.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/NIC.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -18,128 +18,193 @@
  */
 package org.jclouds.cloudstack.domain;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.beans.ConstructorProperties;
 import java.net.URI;
 
+import javax.inject.Named;
+
+import org.jclouds.javax.annotation.Nullable;
+
 import com.google.common.base.Objects;
-import com.google.gson.annotations.SerializedName;
+import com.google.common.base.Objects.ToStringHelper;
 
 /**
- *
+ * Class NIC
+ * 
  * @author Adrian Cole
- */
+*/
 public class NIC {
-   public static Builder builder() {
-      return new Builder();
+
+   public static Builder<?> builder() { 
+      return new ConcreteBuilder();
+   }
+   
+   public Builder<?> toBuilder() { 
+      return new ConcreteBuilder().fromNIC(this);
    }
 
-   public static class Builder {
-      private String id;
-      private URI broadcastURI;
-      private String gateway;
-      private String IPAddress;
-      private boolean isDefault;
-      private URI isolationURI;
-      private String netmask;
-      private String macAddress;
-      private String networkId;
-      private TrafficType trafficType;
-      private GuestIPType guestIPType;
+   public static abstract class Builder<T extends Builder<T>>  {
+      protected abstract T self();
 
-      public Builder id(String id) {
+      protected String id;
+      protected URI broadcastURI;
+      protected String gateway;
+      protected String IPAddress;
+      protected boolean isDefault;
+      protected URI isolationURI;
+      protected String netmask;
+      protected String macAddress;
+      protected String networkId;
+      protected TrafficType trafficType;
+      protected GuestIPType guestIPType;
+   
+      /** 
+       * @see NIC#getId()
+       */
+      public T id(String id) {
          this.id = id;
-         return this;
+         return self();
       }
 
-      public Builder broadcastURI(URI broadcastURI) {
+      /** 
+       * @see NIC#getBroadcastURI()
+       */
+      public T broadcastURI(URI broadcastURI) {
          this.broadcastURI = broadcastURI;
-         return this;
+         return self();
       }
 
-      public Builder gateway(String gateway) {
+      /** 
+       * @see NIC#getGateway()
+       */
+      public T gateway(String gateway) {
          this.gateway = gateway;
-         return this;
+         return self();
       }
 
-      public Builder IPAddress(String IPAddress) {
+      /** 
+       * @see NIC#getIPAddress()
+       */
+      public T IPAddress(String IPAddress) {
          this.IPAddress = IPAddress;
-         return this;
+         return self();
       }
 
-      public Builder isDefault(boolean isDefault) {
+      /** 
+       * @see NIC#isDefault()
+       */
+      public T isDefault(boolean isDefault) {
          this.isDefault = isDefault;
-         return this;
+         return self();
       }
 
-      public Builder isolationURI(URI isolationURI) {
+      /** 
+       * @see NIC#getIsolationURI()
+       */
+      public T isolationURI(URI isolationURI) {
          this.isolationURI = isolationURI;
-         return this;
+         return self();
       }
 
-      public Builder netmask(String netmask) {
+      /** 
+       * @see NIC#getNetmask()
+       */
+      public T netmask(String netmask) {
          this.netmask = netmask;
-         return this;
+         return self();
       }
 
-      public Builder macAddress(String macAddress) {
+      /** 
+       * @see NIC#getMacAddress()
+       */
+      public T macAddress(String macAddress) {
          this.macAddress = macAddress;
-         return this;
+         return self();
       }
 
-      public Builder networkId(String networkId) {
+      /** 
+       * @see NIC#getNetworkId()
+       */
+      public T networkId(String networkId) {
          this.networkId = networkId;
-         return this;
+         return self();
       }
 
-      public Builder trafficType(TrafficType trafficType) {
+      /** 
+       * @see NIC#getTrafficType()
+       */
+      public T trafficType(TrafficType trafficType) {
          this.trafficType = trafficType;
-         return this;
+         return self();
       }
 
-      public Builder guestIPType(GuestIPType guestIPType) {
+      /** 
+       * @see NIC#getGuestIPType()
+       */
+      public T guestIPType(GuestIPType guestIPType) {
          this.guestIPType = guestIPType;
-         return this;
+         return self();
       }
 
       public NIC build() {
-         return new NIC(id, broadcastURI, gateway, IPAddress, isDefault, isolationURI, netmask, macAddress, networkId,
-               trafficType, guestIPType);
-
+         return new NIC(id, broadcastURI, gateway, IPAddress, isDefault, isolationURI, netmask, macAddress, networkId, trafficType, guestIPType);
+      }
+      
+      public T fromNIC(NIC in) {
+         return this
+                  .id(in.getId())
+                  .broadcastURI(in.getBroadcastURI())
+                  .gateway(in.getGateway())
+                  .IPAddress(in.getIPAddress())
+                  .isDefault(in.isDefault())
+                  .isolationURI(in.getIsolationURI())
+                  .netmask(in.getNetmask())
+                  .macAddress(in.getMacAddress())
+                  .networkId(in.getNetworkId())
+                  .trafficType(in.getTrafficType())
+                  .guestIPType(in.getGuestIPType());
       }
    }
 
-   private String id;
-   @SerializedName("broadcasturi")
-   private URI broadcastURI;
-   private String gateway;
-   @SerializedName("ipaddress")
-   private String IPAddress;
-   @SerializedName("isdefault")
-   private boolean isDefault;
-   @SerializedName("isolationuri")
-   private URI isolationURI;
-   private String netmask;
-   @SerializedName("macaddress")
-   private String macAddress;
-   @SerializedName("networkid")
-   private String networkId;
-   @SerializedName("traffictype")
-   private TrafficType trafficType;
-   @SerializedName("type")
-   private GuestIPType guestIPType;
-
-   /**
-    * present only for serializer
-    */
-   NIC() {
-
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
    }
 
-   public NIC(String id, URI broadcastURI, String gateway, String iPAddress, boolean isDefault, URI isolationURI,
-              String netmask, String macAddress, String networkId, TrafficType trafficType, GuestIPType guestIPType) {
-      this.id = id;
+   private final String id;
+   @Named("broadcasturi")
+   private final URI broadcastURI;
+   private final String gateway;
+   @Named("ipaddress")
+   private final String IPAddress;
+   @Named("isdefault")
+   private final boolean isDefault;
+   @Named("isolationuri")
+   private final URI isolationURI;
+   private final String netmask;
+   @Named("macaddress")
+   private final String macAddress;
+   @Named("networkid")
+   private final String networkId;
+   @Named("traffictype")
+   private final TrafficType trafficType;
+   @Named("type")
+   private final GuestIPType guestIPType;
+
+   @ConstructorProperties({
+      "id", "broadcasturi", "gateway", "ipaddress", "isdefault", "isolationuri", "netmask", "macaddress", "networkid", "traffictype", "type"
+   })
+   protected NIC(String id, @Nullable URI broadcastURI, @Nullable String gateway, @Nullable String IPAddress, boolean isDefault,
+                 @Nullable URI isolationURI, @Nullable String netmask, @Nullable String macAddress, @Nullable String networkId,
+                 @Nullable TrafficType trafficType, @Nullable GuestIPType guestIPType) {
+      this.id = checkNotNull(id, "id");
       this.broadcastURI = broadcastURI;
       this.gateway = gateway;
-      this.IPAddress = iPAddress;
+      this.IPAddress = IPAddress;
       this.isDefault = isDefault;
       this.isolationURI = isolationURI;
       this.netmask = netmask;
@@ -153,121 +218,121 @@
     * the ID of the nic
     */
    public String getId() {
-      return id;
+      return this.id;
    }
 
    /**
     * the broadcast uri of the nic
     */
+   @Nullable
    public URI getBroadcastURI() {
-      return broadcastURI;
+      return this.broadcastURI;
    }
 
    /**
     * the gateway of the nic
     */
+   @Nullable
    public String getGateway() {
-      return gateway;
+      return this.gateway;
    }
 
    /**
     * the ip address of the nic
     */
+   @Nullable
    public String getIPAddress() {
-      return IPAddress;
+      return this.IPAddress;
    }
 
    /**
     * true if nic is default, false otherwise
     */
    public boolean isDefault() {
-      return isDefault;
+      return this.isDefault;
    }
 
    /**
     * the isolation uri of the nic
     */
+   @Nullable
    public URI getIsolationURI() {
-      return isolationURI;
+      return this.isolationURI;
    }
 
    /**
     * the netmask of the nic
     */
+   @Nullable
    public String getNetmask() {
-      return netmask;
+      return this.netmask;
    }
 
    /**
     * the MAC Address of the NIC
     */
+   @Nullable
    public String getMacAddress() {
-      return macAddress;
+      return this.macAddress;
    }
 
    /**
     * the ID of the corresponding network
     */
+   @Nullable
    public String getNetworkId() {
-      return networkId;
+      return this.networkId;
    }
 
    /**
     * the traffic type of the nic
     */
+   @Nullable
    public TrafficType getTrafficType() {
-      return trafficType;
+      return this.trafficType;
    }
 
    /**
     * the type of the nic
     */
+   @Nullable
    public GuestIPType getGuestIPType() {
-      return guestIPType;
-   }
-
-   @Override
-   public boolean equals(Object o) {
-      if (this == o) return true;
-      if (o == null || getClass() != o.getClass()) return false;
-
-      NIC that = (NIC) o;
-
-      if (!Objects.equal(IPAddress, that.IPAddress)) return false;
-      if (!Objects.equal(broadcastURI, that.broadcastURI)) return false;
-      if (!Objects.equal(gateway, that.gateway)) return false;
-      if (!Objects.equal(guestIPType, that.guestIPType)) return false;
-      if (!Objects.equal(id, that.id)) return false;
-      if (!Objects.equal(isDefault, that.isDefault)) return false;
-      if (!Objects.equal(isolationURI, that.isolationURI)) return false;
-      if (!Objects.equal(netmask, that.netmask)) return false;
-      if (!Objects.equal(macAddress, that.macAddress)) return false;
-      if (!Objects.equal(networkId, that.networkId)) return false;
-      if (!Objects.equal(trafficType, that.trafficType)) return false;
-
-      return true;
+      return this.guestIPType;
    }
 
    @Override
    public int hashCode() {
-       return Objects.hashCode(IPAddress, broadcastURI, gateway, guestIPType, id, isDefault, isolationURI, netmask, macAddress, networkId, trafficType);
+      return Objects.hashCode(id, broadcastURI, gateway, IPAddress, isDefault, isolationURI, netmask, macAddress, networkId, trafficType, guestIPType);
    }
 
    @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      NIC that = NIC.class.cast(obj);
+      return Objects.equal(this.id, that.id)
+               && Objects.equal(this.broadcastURI, that.broadcastURI)
+               && Objects.equal(this.gateway, that.gateway)
+               && Objects.equal(this.IPAddress, that.IPAddress)
+               && Objects.equal(this.isDefault, that.isDefault)
+               && Objects.equal(this.isolationURI, that.isolationURI)
+               && Objects.equal(this.netmask, that.netmask)
+               && Objects.equal(this.macAddress, that.macAddress)
+               && Objects.equal(this.networkId, that.networkId)
+               && Objects.equal(this.trafficType, that.trafficType)
+               && Objects.equal(this.guestIPType, that.guestIPType);
+   }
+   
+   protected ToStringHelper string() {
+      return Objects.toStringHelper(this)
+            .add("id", id).add("broadcastURI", broadcastURI).add("gateway", gateway).add("IPAddress", IPAddress)
+            .add("isDefault", isDefault).add("isolationURI", isolationURI).add("netmask", netmask).add("macAddress", macAddress)
+            .add("networkId", networkId).add("trafficType", trafficType).add("guestIPType", guestIPType);
+   }
+   
+   @Override
    public String toString() {
-      return "NIC{" +
-            "id=" + id +
-            ", broadcastURI=" + broadcastURI +
-            ", gateway='" + gateway + '\'' +
-            ", IPAddress='" + IPAddress + '\'' +
-            ", isDefault=" + isDefault +
-            ", isolationURI=" + isolationURI +
-            ", netmask='" + netmask + '\'' +
-            ", macAddress='" + macAddress + '\'' +
-            ", networkId=" + networkId +
-            ", trafficType=" + trafficType +
-            ", guestIPType=" + guestIPType +
-            '}';
+      return string().toString();
    }
 
 }
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Network.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Network.java
index f2b3631..5cc209f 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Network.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Network.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -20,296 +20,428 @@
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import java.beans.ConstructorProperties;
 import java.net.URI;
 import java.util.List;
 import java.util.Set;
-import java.util.SortedSet;
 
-import javax.annotation.Nullable;
+import javax.inject.Named;
 
-import com.google.common.base.Joiner;
+import org.jclouds.javax.annotation.Nullable;
+
 import com.google.common.base.Objects;
-import com.google.common.base.Splitter;
+import com.google.common.base.Objects.ToStringHelper;
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.ImmutableSortedSet;
-import com.google.gson.annotations.SerializedName;
 
 /**
+ * Class Network
+ * 
  * @author Adrian Cole
- */
-public class Network implements Comparable<Network> {
-   public static Builder builder() {
-      return new Builder();
+*/
+public class Network {
+
+   public static Builder<?> builder() { 
+      return new ConcreteBuilder();
+   }
+   
+   public Builder<?> toBuilder() { 
+      return new ConcreteBuilder().fromNetwork(this);
    }
 
-   public static class Builder {
-      private String id;
-      private String broadcastDomainType;
-      private URI broadcastURI;
-      private String displayText;
-      private List<String> DNS = ImmutableList.of();
-      private String domain;
-      private String endIP;
-      private String gateway;
-      private boolean isDefault;
-      private boolean isShared;
-      private boolean isSystem;
-      private String netmask;
-      private String networkDomain;
-      private String networkOfferingAvailability;
-      private String networkOfferingDisplayText;
-      private String networkOfferingId;
-      private String networkOfferingName;
-      private String related;
-      private String startIP;
-      private String name;
-      private String state;
-      private GuestIPType guestIPType;
-      private String VLAN;
-      private TrafficType trafficType;
-      private String zoneId;
-      private Set<? extends NetworkService> services = ImmutableSet.<NetworkService>of();
-      private String account;
-      private String domainId;
-      private boolean securityGroupEnabled;
-      private Set<String> tags = ImmutableSet.of();
+   public static abstract class Builder<T extends Builder<T>>  {
+      protected abstract T self();
 
-
-      public Builder id(String id) {
+      protected String id;
+      protected String account;
+      protected String broadcastDomainType;
+      protected URI broadcastURI;
+      protected String displayText;
+      protected String DNS1;
+      protected String DNS2;
+      protected String domain;
+      protected String domainId;
+      protected String endIP;
+      protected String gateway;
+      protected boolean isDefault;
+      protected boolean isShared;
+      protected boolean isSystem;
+      protected String netmask;
+      protected String networkDomain;
+      protected String networkOfferingAvailability;
+      protected String networkOfferingDisplayText;
+      protected String networkOfferingId;
+      protected String networkOfferingName;
+      protected String related;
+      protected String startIP;
+      protected String name;
+      protected String state;
+      protected GuestIPType guestIPType;
+      protected String VLAN;
+      protected TrafficType trafficType;
+      protected String zoneId;
+      protected String tags;
+      protected boolean securityGroupEnabled;
+      protected Set<? extends NetworkService> services = ImmutableSortedSet.of();
+   
+      /** 
+       * @see Network#getId()
+       */
+      public T id(String id) {
          this.id = id;
-         return this;
+         return self();
       }
 
-      public Builder account(String account) {
+      /** 
+       * @see Network#getAccount()
+       */
+      public T account(String account) {
          this.account = account;
-         return this;
+         return self();
       }
 
-      public Builder domainId(String domainId) {
-         this.domainId = domainId;
-         return this;
-      }
-
-      public Builder broadcastDomainType(String broadcastDomainType) {
+      /** 
+       * @see Network#getBroadcastDomainType()
+       */
+      public T broadcastDomainType(String broadcastDomainType) {
          this.broadcastDomainType = broadcastDomainType;
-         return this;
+         return self();
       }
 
-      public Builder broadcastURI(URI broadcastURI) {
+      /** 
+       * @see Network#getBroadcastURI()
+       */
+      public T broadcastURI(URI broadcastURI) {
          this.broadcastURI = broadcastURI;
-         return this;
+         return self();
       }
 
-      public Builder displayText(String displayText) {
+      /** 
+       * @see Network#getDisplayText()
+       */
+      public T displayText(String displayText) {
          this.displayText = displayText;
-         return this;
+         return self();
       }
 
-      public Builder DNS(List<String> DNS) {
-         this.DNS = ImmutableList.copyOf(checkNotNull(DNS, "DNS"));
-         return this;
+      /**
+       * @return the DNS for the Network
+       */
+      public T DNS(List<String> DNS) {
+         if (!DNS.isEmpty()) this.DNS1 = DNS.get(0);
+         if (DNS.size() > 1) this.DNS2 = DNS.get(1);
+         return self();
       }
-
-      public Builder domain(String domain) {
+      /** 
+       * @see Network#getDomain()
+       */
+      public T domain(String domain) {
          this.domain = domain;
-         return this;
+         return self();
       }
 
-      public Builder endIP(String endIP) {
+      /** 
+       * @see Network#getDomainId()
+       */
+      public T domainId(String domainId) {
+         this.domainId = domainId;
+         return self();
+      }
+
+      /** 
+       * @see Network#getEndIP()
+       */
+      public T endIP(String endIP) {
          this.endIP = endIP;
-         return this;
+         return self();
       }
 
-      public Builder gateway(String gateway) {
+      /** 
+       * @see Network#getGateway()
+       */
+      public T gateway(String gateway) {
          this.gateway = gateway;
-         return this;
+         return self();
       }
 
-      public Builder isDefault(boolean isDefault) {
+      /** 
+       * @see Network#isDefault()
+       */
+      public T isDefault(boolean isDefault) {
          this.isDefault = isDefault;
-         return this;
+         return self();
       }
 
-      public Builder isShared(boolean isShared) {
+      /** 
+       * @see Network#isShared()
+       */
+      public T isShared(boolean isShared) {
          this.isShared = isShared;
-         return this;
+         return self();
       }
 
-      public Builder isSystem(boolean isSystem) {
+      /** 
+       * @see Network#isSystem()
+       */
+      public T isSystem(boolean isSystem) {
          this.isSystem = isSystem;
-         return this;
+         return self();
       }
 
-      public Builder netmask(String netmask) {
+      /** 
+       * @see Network#getNetmask()
+       */
+      public T netmask(String netmask) {
          this.netmask = netmask;
-         return this;
+         return self();
       }
 
-      public Builder networkDomain(String networkDomain) {
+      /** 
+       * @see Network#getNetworkDomain()
+       */
+      public T networkDomain(String networkDomain) {
          this.networkDomain = networkDomain;
-         return this;
+         return self();
       }
 
-      public Builder networkOfferingAvailability(String networkOfferingAvailability) {
+      /** 
+       * @see Network#getNetworkOfferingAvailability()
+       */
+      public T networkOfferingAvailability(String networkOfferingAvailability) {
          this.networkOfferingAvailability = networkOfferingAvailability;
-         return this;
+         return self();
       }
 
-      public Builder networkOfferingDisplayText(String networkOfferingDisplayText) {
+      /** 
+       * @see Network#getNetworkOfferingDisplayText()
+       */
+      public T networkOfferingDisplayText(String networkOfferingDisplayText) {
          this.networkOfferingDisplayText = networkOfferingDisplayText;
-         return this;
+         return self();
       }
 
-      public Builder networkOfferingId(String networkOfferingId) {
+      /** 
+       * @see Network#getNetworkOfferingId()
+       */
+      public T networkOfferingId(String networkOfferingId) {
          this.networkOfferingId = networkOfferingId;
-         return this;
+         return self();
       }
 
-      public Builder networkOfferingName(String networkOfferingName) {
+      /** 
+       * @see Network#getNetworkOfferingName()
+       */
+      public T networkOfferingName(String networkOfferingName) {
          this.networkOfferingName = networkOfferingName;
-         return this;
+         return self();
       }
 
-      public Builder related(String related) {
+      /** 
+       * @see Network#getRelated()
+       */
+      public T related(String related) {
          this.related = related;
-         return this;
+         return self();
       }
 
-      public Builder startIP(String startIP) {
+      /** 
+       * @see Network#getStartIP()
+       */
+      public T startIP(String startIP) {
          this.startIP = startIP;
-         return this;
+         return self();
       }
 
-      public Builder name(String name) {
+      /** 
+       * @see Network#getName()
+       */
+      public T name(String name) {
          this.name = name;
-         return this;
+         return self();
       }
 
-      public Builder state(String state) {
+      /** 
+       * @see Network#getState()
+       */
+      public T state(String state) {
          this.state = state;
-         return this;
+         return self();
       }
 
-      public Builder guestIPType(GuestIPType guestIPType) {
+      /** 
+       * @see Network#getGuestIPType()
+       */
+      public T guestIPType(GuestIPType guestIPType) {
          this.guestIPType = guestIPType;
-         return this;
+         return self();
       }
 
-      public Builder VLAN(String VLAN) {
+      /** 
+       * @see Network#getVLAN()
+       */
+      public T VLAN(String VLAN) {
          this.VLAN = VLAN;
-         return this;
+         return self();
       }
 
-      public Builder trafficType(TrafficType trafficType) {
+      /** 
+       * @see Network#getTrafficType()
+       */
+      public T trafficType(TrafficType trafficType) {
          this.trafficType = trafficType;
-         return this;
+         return self();
       }
 
-      public Builder zoneId(String zoneId) {
+      /** 
+       * @see Network#getZoneId()
+       */
+      public T zoneId(String zoneId) {
          this.zoneId = zoneId;
-         return this;
+         return self();
       }
 
-      public Builder services(Set<? extends NetworkService> services) {
-         this.services = ImmutableSet.<NetworkService>copyOf(checkNotNull(services, "services"));
-         return this;
+      /** 
+       * @see Network#getTags()
+       */
+      public T tags(String tags) {
+         this.tags = tags;
+         return self();
       }
 
-      public Builder tags(Set<String> tags) {
-         this.tags = ImmutableSet.copyOf(checkNotNull(tags, "tags"));
-         return this;
-      }
-
-      public Builder securityGroupEnabled(boolean securityGroupEnabled) {
+      /** 
+       * @see Network#isSecurityGroupEnabled()
+       */
+      public T securityGroupEnabled(boolean securityGroupEnabled) {
          this.securityGroupEnabled = securityGroupEnabled;
-         return this;
+         return self();
+      }
+
+      /** 
+       * @see Network#getServices()
+       */
+      public T services(Set<? extends NetworkService> services) {
+         this.services = services;
+         return self();
       }
 
       public Network build() {
-         return new Network(id, broadcastDomainType, broadcastURI, displayText, DNS, domain, domainId, endIP,
-               gateway, isDefault, isShared, isSystem, netmask, networkDomain, networkOfferingAvailability,
-               networkOfferingDisplayText, networkOfferingId, networkOfferingName, related, startIP, name, state,
-               guestIPType, VLAN, trafficType, zoneId, services, tags, securityGroupEnabled, account);
+         return new Network(id, account, broadcastDomainType, broadcastURI, displayText, DNS1, DNS2, domain, domainId, endIP, gateway, isDefault, isShared, isSystem, netmask, networkDomain, networkOfferingAvailability, networkOfferingDisplayText, networkOfferingId, networkOfferingName, related, startIP, name, state, guestIPType, VLAN, trafficType, zoneId, tags, securityGroupEnabled, services);
+      }
+      
+      public T fromNetwork(Network in) {
+         return this
+                  .id(in.getId())
+                  .account(in.getAccount())
+                  .broadcastDomainType(in.getBroadcastDomainType())
+                  .broadcastURI(in.getBroadcastURI())
+                  .displayText(in.getDisplayText())
+                  .DNS(in.getDNS())
+                  .domain(in.getDomain())
+                  .domainId(in.getDomainId())
+                  .endIP(in.getEndIP())
+                  .gateway(in.getGateway())
+                  .isDefault(in.isDefault())
+                  .isShared(in.isShared())
+                  .isSystem(in.isSystem())
+                  .netmask(in.getNetmask())
+                  .networkDomain(in.getNetworkDomain())
+                  .networkOfferingAvailability(in.getNetworkOfferingAvailability())
+                  .networkOfferingDisplayText(in.getNetworkOfferingDisplayText())
+                  .networkOfferingId(in.getNetworkOfferingId())
+                  .networkOfferingName(in.getNetworkOfferingName())
+                  .related(in.getRelated())
+                  .startIP(in.getStartIP())
+                  .name(in.getName())
+                  .state(in.getState())
+                  .guestIPType(in.getGuestIPType())
+                  .VLAN(in.getVLAN())
+                  .trafficType(in.getTrafficType())
+                  .zoneId(in.getZoneId())
+                  .tags(in.getTags())
+                  .securityGroupEnabled(in.isSecurityGroupEnabled())
+                  .services(in.getServices());
       }
    }
 
-   private String id;
-   private String account;
-   @SerializedName("broadcastdomaintype")
-   private String broadcastDomainType;
-   @SerializedName("broadcasturi")
-   private URI broadcastURI;
-   @SerializedName("displaytext")
-   private String displayText;
-   @SerializedName("dns1")
-   private String DNS1;
-   @SerializedName("dns2")
-   private String DNS2;
-   private String domain;
-   @SerializedName("domainid")
-   private String domainId;
-   @SerializedName("endip")
-   private String endIP;
-   private String gateway;
-   @SerializedName("isdefault")
-   private boolean isDefault;
-   @SerializedName("isshared")
-   private boolean isShared;
-   @SerializedName("issystem")
-   private boolean isSystem;
-   private String netmask;
-   @Nullable
-   @SerializedName("networkdomain")
-   private String networkDomain;
-   @SerializedName("networkofferingavailability")
-   private String networkOfferingAvailability;
-   @SerializedName("networkofferingdisplaytext")
-   private String networkOfferingDisplayText;
-   @SerializedName("networkofferingid")
-   private String networkOfferingId;
-   @SerializedName("networkofferingname")
-   private String networkOfferingName;
-   private String related;
-   @SerializedName("startip")
-   private String startIP;
-   private String name;
-   private String state;
-   @SerializedName("type")
-   private GuestIPType guestIPType;
-   @SerializedName("vlan")
-   private String VLAN;
-   @SerializedName("traffictype")
-   private TrafficType trafficType;
-   @SerializedName("zoneid")
-   private String zoneId;
-   private String tags;
-   @SerializedName("securitygroupenabled")
-   private boolean securityGroupEnabled;
-   // so tests and serialization comes out expected
-   @SerializedName("service")
-   private SortedSet<? extends NetworkService> services = ImmutableSortedSet.<NetworkService>of();
-
-   /**
-    * present only for serializer
-    */
-   Network() {
-
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
    }
 
-   public Network(String id, String broadcastDomainType, URI broadcastURI, String displayText,
-                  List<String> DNS, String domain, String domainId, String endIP, String gateway, boolean isDefault,
-                  boolean isShared, boolean isSystem, String netmask, String networkDomain, String networkOfferingAvailability,
-                  String networkOfferingDisplayText, String networkOfferingId, String networkOfferingName, String related,
-                  String startIP, String name, String state, GuestIPType type, String vLAN, TrafficType trafficType,
-                  String zoneId, Set<? extends NetworkService> services, Set<String> tags, boolean securityGroupEnabled,
-                  String account) {
-      this.id = id;
+   private final String id;
+   private final String account;
+   @Named("broadcastdomaintype")
+   private final String broadcastDomainType;
+   @Named("broadcasturi")
+   private final URI broadcastURI;
+   @Named("displaytext")
+   private final String displayText;
+   @Named("dns1")
+   private final String DNS1;
+   @Named("dns2")
+   private final String DNS2;
+   private final String domain;
+   @Named("domainid")
+   private final String domainId;
+   @Named("endip")
+   private final String endIP;
+   private final String gateway;
+   @Named("isdefault")
+   private final boolean isDefault;
+   @Named("isshared")
+   private final boolean isShared;
+   @Named("issystem")
+   private final boolean isSystem;
+   private final String netmask;
+   @Named("networkdomain")
+   private final String networkDomain;
+   @Named("networkofferingavailability")
+   private final String networkOfferingAvailability;
+   @Named("networkofferingdisplaytext")
+   private final String networkOfferingDisplayText;
+   @Named("networkofferingid")
+   private final String networkOfferingId;
+   @Named("networkofferingname")
+   private final String networkOfferingName;
+   private final String related;
+   @Named("startip")
+   private final String startIP;
+   private final String name;
+   private final String state;
+   @Named("type")
+   private final GuestIPType guestIPType;
+   @Named("vlan")
+   private final String VLAN;
+   @Named("traffictype")
+   private final TrafficType trafficType;
+   @Named("zoneid")
+   private final String zoneId;
+   private final String tags;
+   @Named("securitygroupenabled")
+   private final boolean securityGroupEnabled;
+   @Named("service")
+   private final Set<? extends NetworkService> services;
+
+   @ConstructorProperties({
+      "id", "account", "broadcastdomaintype", "broadcasturi", "displaytext", "dns1", "dns2", "domain", "domainid", "endip", "gateway", "isdefault", "isshared", "issystem", "netmask", "networkdomain", "networkofferingavailability", "networkofferingdisplaytext", "networkofferingid", "networkofferingname", "related", "startip", "name", "state", "type", "vlan", "traffictype", "zoneid", "tags", "securitygroupenabled", "service"
+   })
+   protected Network(String id, @Nullable String account, @Nullable String broadcastDomainType, @Nullable URI broadcastURI,
+                     @Nullable String displayText, @Nullable String DNS1, @Nullable String DNS2, @Nullable String domain, @Nullable String domainId,
+                     @Nullable String endIP, @Nullable String gateway, boolean isDefault, boolean isShared, boolean isSystem,
+                     @Nullable String netmask, @Nullable String networkDomain, @Nullable String networkOfferingAvailability,
+                     @Nullable String networkOfferingDisplayText, @Nullable String networkOfferingId, @Nullable String networkOfferingName,
+                     @Nullable String related, @Nullable String startIP, @Nullable String name, @Nullable String state,
+                     @Nullable GuestIPType guestIPType, @Nullable String VLAN, @Nullable TrafficType trafficType,
+                     @Nullable String zoneId, @Nullable String tags, boolean securityGroupEnabled, Set<? extends NetworkService> services) {
+      this.id = checkNotNull(id, "id");
+      this.account = account;
       this.broadcastDomainType = broadcastDomainType;
       this.broadcastURI = broadcastURI;
       this.displayText = displayText;
-      this.DNS1 = checkNotNull(DNS, "DNS").size() > 0 ? DNS.get(0) : null;
-      this.DNS2 = DNS.size() > 1 ? DNS.get(1) : null;
+      this.DNS1 = DNS1;
+      this.DNS2 = DNS2;
       this.domain = domain;
       this.domainId = domainId;
       this.endIP = endIP;
@@ -327,47 +459,54 @@
       this.startIP = startIP;
       this.name = name;
       this.state = state;
-      this.guestIPType = type;
-      this.VLAN = vLAN;
+      this.guestIPType = guestIPType;
+      this.VLAN = VLAN;
       this.trafficType = trafficType;
       this.zoneId = zoneId;
-      this.services = ImmutableSortedSet.copyOf(checkNotNull(services, "services"));
-      this.tags = tags.size() == 0 ? null : Joiner.on(',').join(tags);
+      this.tags = tags;
       this.securityGroupEnabled = securityGroupEnabled;
-      this.account = account;
+      this.services = ImmutableSortedSet.copyOf(services);
    }
 
    /**
     * @return network id
     */
    public String getId() {
-      return id;
+      return this.id;
+   }
+
+   /**
+    * @return the account associated with the network
+    */
+   @Nullable
+   public String getAccount() {
+      return this.account;
    }
 
    /**
     * @return Broadcast domain type of the network
     */
+   @Nullable
    public String getBroadcastDomainType() {
-      return broadcastDomainType;
+      return this.broadcastDomainType;
    }
 
    /**
     * @return broadcast uri of the network
     */
+   @Nullable
    public URI getBroadcastURI() {
-      return broadcastURI;
+      return this.broadcastURI;
    }
 
    /**
     * @return the display text of the zone
     */
+   @Nullable
    public String getDisplayText() {
-      return displayText;
+      return this.displayText;
    }
 
-   /**
-    * @return the external DNS for the network
-    */
    public List<String> getDNS() {
       ImmutableList.Builder<String> builder = ImmutableList.builder();
       if (DNS1 != null && !"".equals(DNS1))
@@ -380,266 +519,247 @@
    /**
     * @return Domain name for the Network
     */
+   @Nullable
    public String getDomain() {
-      return domain;
+      return this.domain;
    }
 
    /**
     * @return the domain id of the Network
     */
+   @Nullable
    public String getDomainId() {
-      return domainId;
-   }
-
-   /**
-    * @return the account associated with the network
-    */
-   public String getAccount() {
-      return account;
+      return this.domainId;
    }
 
    /**
     * @return the end ip of the network
     */
+   @Nullable
    public String getEndIP() {
-      return endIP;
+      return this.endIP;
    }
 
    /**
     * @return the network's gateway
     */
+   @Nullable
    public String getGateway() {
-      return gateway;
+      return this.gateway;
    }
 
    /**
     * @return true if network offering is default, false otherwise
     */
    public boolean isDefault() {
-      return isDefault;
+      return this.isDefault;
    }
 
    /**
     * @return true if network offering is shared, false otherwise
     */
    public boolean isShared() {
-      return isShared;
+      return this.isShared;
    }
 
    /**
     * @return true if network offering is system, false otherwise
     */
    public boolean isSystem() {
-      return isSystem;
+      return this.isSystem;
+   }
+
+   /**
+    * @return the network's netmask
+    */
+   @Nullable
+   public String getNetmask() {
+      return this.netmask;
+   }
+
+   /**
+    * @return the network domain
+    */
+   @Nullable
+   public String getNetworkDomain() {
+      return this.networkDomain;
+   }
+
+   /**
+    * @return availability of the network offering the network is created from
+    */
+   @Nullable
+   public String getNetworkOfferingAvailability() {
+      return this.networkOfferingAvailability;
+   }
+
+   /**
+    * @return display text of the network offering the network is created from
+    */
+   @Nullable
+   public String getNetworkOfferingDisplayText() {
+      return this.networkOfferingDisplayText;
+   }
+
+   /**
+    * @return network offering id the network is created from
+    */
+   @Nullable
+   public String getNetworkOfferingId() {
+      return this.networkOfferingId;
+   }
+
+   /**
+    * @return name of the network offering the network is created from
+    */
+   @Nullable
+   public String getNetworkOfferingName() {
+      return this.networkOfferingName;
+   }
+
+   /**
+    * @return related to what other network configuration
+    */
+   @Nullable
+   public String getRelated() {
+      return this.related;
+   }
+
+   /**
+    * @return the start ip of the network
+    */
+   @Nullable
+   public String getStartIP() {
+      return this.startIP;
    }
 
    /**
     * @return network name
     */
+   @Nullable
    public String getName() {
-      return name;
+      return this.name;
+   }
+
+   /**
+    * @return state of the network
+    */
+   @Nullable
+   public String getState() {
+      return this.state;
    }
 
    /**
     * @return the GuestIPType of the network
     */
    public GuestIPType getGuestIPType() {
-      return guestIPType;
-   }
-
-   /**
-    * @return state of the network
-    */
-   public String getState() {
-      return state;
+      return this.guestIPType;
    }
 
    /**
     * @return the vlan range of the zone
     */
+   @Nullable
    public String getVLAN() {
-      return VLAN;
+      return this.VLAN;
    }
 
    /**
     * @return the traffic type for this network offering
     */
+   @Nullable
    public TrafficType getTrafficType() {
-      return trafficType;
-   }
-
-   /**
-    * @return the network's netmask
-    */
-   public String getNetmask() {
-      return netmask;
-   }
-
-   /**
-    * @return the network domain
-    */
-   public String getNetworkDomain() {
-      return networkDomain;
-   }
-
-   /**
-    * @return availability of the network offering the network is created from
-    */
-   public String getNetworkOfferingAvailability() {
-      return networkOfferingAvailability;
-   }
-
-   /**
-    * @return display text of the network offering the network is created from
-    */
-   public String getNetworkOfferingDisplayText() {
-      return networkOfferingDisplayText;
-   }
-
-   /**
-    * @return network offering id the network is created from
-    */
-   public String getNetworkOfferingId() {
-      return networkOfferingId;
-   }
-
-   /**
-    * @return name of the network offering the network is created from
-    */
-   public String getNetworkOfferingName() {
-      return networkOfferingName;
-   }
-
-   /**
-    * @return related to what other network configuration
-    */
-   public String getRelated() {
-      return related;
-   }
-
-   /**
-    * @return the start ip of the network
-    */
-   public String getStartIP() {
-      return startIP;
+      return this.trafficType;
    }
 
    /**
     * @return zone id of the network
     */
+   @Nullable
    public String getZoneId() {
-      return zoneId;
+      return this.zoneId;
    }
 
    /**
-    * @return the list of services
+    * @return the tags for the Network
     */
-   public Set<? extends NetworkService> getServices() {
-      return services;
+   @Nullable
+   public String getTags() {
+      return this.tags;
    }
 
    /**
     * @return true if security group is enabled, false otherwise
     */
    public boolean isSecurityGroupEnabled() {
-      return securityGroupEnabled;
+      return this.securityGroupEnabled;
    }
 
    /**
-    * @return the tags for the Network
+    * @return the list of services
     */
-   public Set<String> getTags() {
-      return tags != null ? ImmutableSet.copyOf(Splitter.on(',').split(tags)) : ImmutableSet.<String>of();
-   }
-
-   @Override
-   public boolean equals(Object o) {
-      if (this == o) return true;
-      if (o == null || getClass() != o.getClass()) return false;
-
-      Network that = (Network) o;
-
-      if (!Objects.equal(DNS1, that.DNS1)) return false;
-      if (!Objects.equal(DNS2, that.DNS2)) return false;
-      if (!Objects.equal(VLAN, that.VLAN)) return false;
-      if (!Objects.equal(broadcastDomainType, that.broadcastDomainType)) return false;
-      if (!Objects.equal(broadcastURI, that.broadcastURI)) return false;
-      if (!Objects.equal(displayText, that.displayText)) return false;
-      if (!Objects.equal(domain, that.domain)) return false;
-      if (!Objects.equal(endIP, that.endIP)) return false;
-      if (!Objects.equal(gateway, that.gateway)) return false;
-      if (!Objects.equal(guestIPType, that.guestIPType)) return false;
-      if (!Objects.equal(id, that.id)) return false;
-      if (!Objects.equal(isDefault, that.isDefault)) return false;
-      if (!Objects.equal(isShared, that.isShared)) return false;
-      if (!Objects.equal(isSystem, that.isSystem)) return false;
-      if (!Objects.equal(name, that.name)) return false;
-      if (!Objects.equal(netmask, that.netmask)) return false;
-      if (!Objects.equal(networkDomain, that.networkDomain)) return false;
-      if (!Objects.equal(networkOfferingAvailability, that.networkOfferingAvailability)) return false;
-      if (!Objects.equal(networkOfferingDisplayText, that.networkOfferingDisplayText)) return false;
-      if (!Objects.equal(networkOfferingId, that.networkOfferingId)) return false;
-      if (!Objects.equal(networkOfferingName, that.networkOfferingName)) return false;
-      if (!Objects.equal(related, that.related)) return false;
-      if (!Objects.equal(services, that.services)) return false;
-      if (!Objects.equal(startIP, that.startIP)) return false;
-      if (!Objects.equal(state, that.state)) return false;
-      if (!Objects.equal(trafficType, that.trafficType)) return false;
-      if (!Objects.equal(zoneId, that.zoneId)) return false;
-      if (!Objects.equal(tags, that.tags)) return false;
-      if (!Objects.equal(domainId, that.domainId)) return false;
-
-      return true;
+   public Set<? extends NetworkService> getServices() {
+      return this.services;
    }
 
    @Override
    public int hashCode() {
-       return Objects.hashCode(DNS1, DNS2, VLAN, broadcastDomainType, broadcastURI, displayText, domain,
-                               endIP, gateway, guestIPType, id, isDefault, isShared, isSystem, name,
-                               netmask, networkDomain, networkOfferingAvailability, networkOfferingDisplayText,
-                               networkOfferingId, networkOfferingName, related, services, startIP, state,
-                               trafficType, zoneId, tags, domainId);
+      return Objects.hashCode(id, account, broadcastDomainType, broadcastURI, displayText, DNS1, DNS2, domain, domainId, endIP, gateway, isDefault, isShared, isSystem, netmask, networkDomain, networkOfferingAvailability, networkOfferingDisplayText, networkOfferingId, networkOfferingName, related, startIP, name, state, guestIPType, VLAN, trafficType, zoneId, tags, securityGroupEnabled, services);
    }
 
    @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      Network that = Network.class.cast(obj);
+      return Objects.equal(this.id, that.id)
+               && Objects.equal(this.account, that.account)
+               && Objects.equal(this.broadcastDomainType, that.broadcastDomainType)
+               && Objects.equal(this.broadcastURI, that.broadcastURI)
+               && Objects.equal(this.displayText, that.displayText)
+               && Objects.equal(this.DNS1, that.DNS1)
+               && Objects.equal(this.DNS2, that.DNS2)
+               && Objects.equal(this.domain, that.domain)
+               && Objects.equal(this.domainId, that.domainId)
+               && Objects.equal(this.endIP, that.endIP)
+               && Objects.equal(this.gateway, that.gateway)
+               && Objects.equal(this.isDefault, that.isDefault)
+               && Objects.equal(this.isShared, that.isShared)
+               && Objects.equal(this.isSystem, that.isSystem)
+               && Objects.equal(this.netmask, that.netmask)
+               && Objects.equal(this.networkDomain, that.networkDomain)
+               && Objects.equal(this.networkOfferingAvailability, that.networkOfferingAvailability)
+               && Objects.equal(this.networkOfferingDisplayText, that.networkOfferingDisplayText)
+               && Objects.equal(this.networkOfferingId, that.networkOfferingId)
+               && Objects.equal(this.networkOfferingName, that.networkOfferingName)
+               && Objects.equal(this.related, that.related)
+               && Objects.equal(this.startIP, that.startIP)
+               && Objects.equal(this.name, that.name)
+               && Objects.equal(this.state, that.state)
+               && Objects.equal(this.guestIPType, that.guestIPType)
+               && Objects.equal(this.VLAN, that.VLAN)
+               && Objects.equal(this.trafficType, that.trafficType)
+               && Objects.equal(this.zoneId, that.zoneId)
+               && Objects.equal(this.tags, that.tags)
+               && Objects.equal(this.securityGroupEnabled, that.securityGroupEnabled)
+               && Objects.equal(this.services, that.services);
+   }
+   
+   protected ToStringHelper string() {
+      return Objects.toStringHelper(this)
+            .add("id", id).add("account", account).add("broadcastDomainType", broadcastDomainType).add("broadcastURI", broadcastURI)
+            .add("displayText", displayText).add("DNS1", DNS1).add("DNS2", DNS2).add("domain", domain).add("domainId", domainId)
+            .add("endIP", endIP).add("gateway", gateway).add("isDefault", isDefault).add("isShared", isShared).add("isSystem", isSystem)
+            .add("netmask", netmask).add("networkDomain", networkDomain).add("networkOfferingAvailability", networkOfferingAvailability)
+            .add("networkOfferingDisplayText", networkOfferingDisplayText).add("networkOfferingId", networkOfferingId)
+            .add("networkOfferingName", networkOfferingName).add("related", related).add("startIP", startIP).add("name", name)
+            .add("state", state).add("guestIPType", guestIPType).add("VLAN", VLAN).add("trafficType", trafficType)
+            .add("zoneId", zoneId).add("tags", tags).add("securityGroupEnabled", securityGroupEnabled).add("services", services);
+   }
+   
+   @Override
    public String toString() {
-      return "Network{" +
-            "id=" + id +
-            ", account='" + account + '\'' +
-            ", broadcastDomainType='" + broadcastDomainType + '\'' +
-            ", broadcastURI=" + broadcastURI +
-            ", displayText='" + displayText + '\'' +
-            ", DNS1='" + DNS1 + '\'' +
-            ", DNS2='" + DNS2 + '\'' +
-            ", domain='" + domain + '\'' +
-            ", domainId=" + domainId +
-            ", endIP='" + endIP + '\'' +
-            ", gateway='" + gateway + '\'' +
-            ", isDefault=" + isDefault +
-            ", isShared=" + isShared +
-            ", isSystem=" + isSystem +
-            ", netmask='" + netmask + '\'' +
-            ", networkDomain='" + networkDomain + '\'' +
-            ", networkOfferingAvailability='" + networkOfferingAvailability + '\'' +
-            ", networkOfferingDisplayText='" + networkOfferingDisplayText + '\'' +
-            ", networkOfferingId=" + networkOfferingId +
-            ", networkOfferingName='" + networkOfferingName + '\'' +
-            ", related=" + related +
-            ", startIP='" + startIP + '\'' +
-            ", name='" + name + '\'' +
-            ", state='" + state + '\'' +
-            ", guestIPType=" + guestIPType +
-            ", VLAN='" + VLAN + '\'' +
-            ", trafficType=" + trafficType +
-            ", zoneId=" + zoneId +
-            ", tags='" + tags + '\'' +
-            ", securityGroupEnabled=" + securityGroupEnabled +
-            ", services=" + services +
-            '}';
+      return string().toString();
    }
 
-   @Override
-   public int compareTo(Network arg0) {
-      return id.compareTo(arg0.getId());
-   }
 }
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/NetworkOffering.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/NetworkOffering.java
index c8d475b..f71a4a5 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/NetworkOffering.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/NetworkOffering.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -20,299 +20,339 @@
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import java.beans.ConstructorProperties;
 import java.util.Date;
-import java.util.Set;
 
-import javax.annotation.Nullable;
+import javax.inject.Named;
 
-import com.google.common.base.Joiner;
+import org.jclouds.javax.annotation.Nullable;
+
 import com.google.common.base.Objects;
-import com.google.common.base.Splitter;
-import com.google.common.collect.ImmutableSet;
-import com.google.gson.annotations.SerializedName;
+import com.google.common.base.Objects.ToStringHelper;
 
 /**
+ * Class NetworkOffering
  * 
  * @author Adrian Cole
- */
+*/
 public class NetworkOffering implements Comparable<NetworkOffering> {
-   public static Builder builder() {
-      return new Builder();
+
+   public static Builder<?> builder() { 
+      return new ConcreteBuilder();
+   }
+   
+   public Builder<?> toBuilder() { 
+      return new ConcreteBuilder().fromNetworkOffering(this);
    }
 
-   public static class Builder {
-      private String id;
-      private String name;
-      private String displayText;
-      private Date created;
-      private NetworkOfferingAvailabilityType availability;
-      private Integer maxConnections;
-      private int networkRate;
-      private boolean isDefault;
-      private boolean supportsVLAN;
-      private TrafficType trafficType;
-      private GuestIPType guestIPType;
-      private Set<String> tags = ImmutableSet.of();
+   public static abstract class Builder<T extends Builder<T>>  {
+      protected abstract T self();
 
-      public Builder id(String id) {
+      protected String id;
+      protected String name;
+      protected String displayText;
+      protected Date created;
+      protected NetworkOfferingAvailabilityType availability;
+      protected Integer maxConnections;
+      protected boolean isDefault;
+      protected boolean supportsVLAN;
+      protected TrafficType trafficType;
+      protected GuestIPType guestIPType;
+      protected int networkRate;
+      protected String tags;
+   
+      /** 
+       * @see NetworkOffering#getId()
+       */
+      public T id(String id) {
          this.id = id;
-         return this;
+         return self();
       }
 
-      public Builder name(String name) {
+      /** 
+       * @see NetworkOffering#getName()
+       */
+      public T name(String name) {
          this.name = name;
-         return this;
+         return self();
       }
 
-      public Builder displayText(String displayText) {
+      /** 
+       * @see NetworkOffering#getDisplayText()
+       */
+      public T displayText(String displayText) {
          this.displayText = displayText;
-         return this;
+         return self();
       }
 
-      public Builder created(Date created) {
+      /** 
+       * @see NetworkOffering#getCreated()
+       */
+      public T created(Date created) {
          this.created = created;
-         return this;
+         return self();
       }
 
-      public Builder availability(NetworkOfferingAvailabilityType availability) {
+      /** 
+       * @see NetworkOffering#getAvailability()
+       */
+      public T availability(NetworkOfferingAvailabilityType availability) {
          this.availability = availability;
-         return this;
+         return self();
       }
 
-      public Builder maxConnections(Integer maxConnections) {
+      /** 
+       * @see NetworkOffering#getMaxConnections()
+       */
+      public T maxConnections(Integer maxConnections) {
          this.maxConnections = maxConnections;
-         return this;
+         return self();
       }
 
-      public Builder isDefault(boolean isDefault) {
+      /** 
+       * @see NetworkOffering#isDefault()
+       */
+      public T isDefault(boolean isDefault) {
          this.isDefault = isDefault;
-         return this;
+         return self();
       }
 
-      public Builder networkRate(int networkRate) {
-         this.networkRate = networkRate;
-         return this;
-      }
-
-      public Builder supportsVLAN(boolean supportsVLAN) {
+      /** 
+       * @see NetworkOffering#supportsVLAN()
+       */
+      public T supportsVLAN(boolean supportsVLAN) {
          this.supportsVLAN = supportsVLAN;
-         return this;
+         return self();
       }
 
-      public Builder trafficType(TrafficType trafficType) {
+      /** 
+       * @see NetworkOffering#getTrafficType()
+       */
+      public T trafficType(TrafficType trafficType) {
          this.trafficType = trafficType;
-         return this;
+         return self();
       }
 
-      public Builder guestIPType(GuestIPType guestIPType) {
+      /** 
+       * @see NetworkOffering#getGuestIPType()
+       */
+      public T guestIPType(GuestIPType guestIPType) {
          this.guestIPType = guestIPType;
-         return this;
+         return self();
       }
 
-      public Builder tags(Set<String> tags) {
-         this.tags = ImmutableSet.copyOf(checkNotNull(tags, "tags"));
-         return this;
+      /** 
+       * @see NetworkOffering#getNetworkRate()
+       */
+      public T networkRate(int networkRate) {
+         this.networkRate = networkRate;
+         return self();
+      }
+
+      /** 
+       * @see NetworkOffering#getTags()
+       */
+      public T tags(String tags) {
+         this.tags = tags;
+         return self();
       }
 
       public NetworkOffering build() {
-         return new NetworkOffering(id, name, displayText, created, availability, supportsVLAN, maxConnections,
-               isDefault, trafficType, guestIPType, networkRate, tags);
+         return new NetworkOffering(id, name, displayText, created, availability, maxConnections, isDefault, supportsVLAN, trafficType, guestIPType, networkRate, tags);
+      }
+      
+      public T fromNetworkOffering(NetworkOffering in) {
+         return this
+                  .id(in.getId())
+                  .name(in.getName())
+                  .displayText(in.getDisplayText())
+                  .created(in.getCreated())
+                  .availability(in.getAvailability())
+                  .maxConnections(in.getMaxConnections())
+                  .isDefault(in.isDefault())
+                  .supportsVLAN(in.supportsVLAN())
+                  .trafficType(in.getTrafficType())
+                  .guestIPType(in.getGuestIPType())
+                  .networkRate(in.getNetworkRate())
+                  .tags(in.getTags());
       }
    }
 
-   private String id;
-   private String name;
-   @SerializedName("displaytext")
-   private String displayText;
-   private Date created;
-   @SerializedName("availability")
-   private NetworkOfferingAvailabilityType availability;
-   @SerializedName("maxconnections")
-   private Integer maxConnections;
-   @SerializedName("isdefault")
-   private boolean isDefault;
-   @SerializedName("specifyvlan")
-   private boolean supportsVLAN;
-   @SerializedName("traffictype")
-   private TrafficType trafficType;
-   @SerializedName("guestiptype")
-   private GuestIPType guestIPType;
-   @SerializedName("networkrate")
-   private int networkRate = -1;
-   private String tags;
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
+   }
 
-   public NetworkOffering(String id, String name, String displayText, @Nullable Date created,
-         NetworkOfferingAvailabilityType availability, boolean supportsVLAN, @Nullable Integer maxConnections,
-         boolean isDefault, TrafficType trafficType, GuestIPType guestIPType, int networkRate, Set<String> tags) {
-      this.id = id;
+   private final String id;
+   private final String name;
+   @Named("displaytext")
+   private final String displayText;
+   private final Date created;
+   private final NetworkOfferingAvailabilityType availability;
+   @Named("maxconnections")
+   private final Integer maxConnections;
+   @Named("isdefault")
+   private final boolean isDefault;
+   @Named("specifyvlan")
+   private final boolean supportsVLAN;
+   @Named("traffictype")
+   private final TrafficType trafficType;
+   @Named("guestiptype")
+   private final GuestIPType guestIPType;
+   @Named("networkrate")
+   private final int networkRate;
+   private final String tags;
+
+   @ConstructorProperties({
+      "id", "name", "displaytext", "created", "availability", "maxconnections", "isdefault", "specifyvlan", "traffictype", "guestiptype", "networkrate", "tags"
+   })
+   protected NetworkOffering(String id, @Nullable String name, @Nullable String displayText, @Nullable Date created, @Nullable NetworkOfferingAvailabilityType availability, @Nullable Integer maxConnections, boolean isDefault, boolean supportsVLAN, @Nullable TrafficType trafficType, @Nullable GuestIPType guestIPType, int networkRate, @Nullable String tags) {
+      this.id = checkNotNull(id, "id");
       this.name = name;
       this.displayText = displayText;
       this.created = created;
       this.availability = availability;
-      this.supportsVLAN = supportsVLAN;
       this.maxConnections = maxConnections;
       this.isDefault = isDefault;
+      this.supportsVLAN = supportsVLAN;
       this.trafficType = trafficType;
       this.guestIPType = guestIPType;
       this.networkRate = networkRate;
-      this.tags = tags.size() == 0 ? null : Joiner.on(',').join(tags);
+      this.tags = tags;
    }
 
    /**
-    * present only for serializer
-    * 
-    */
-   NetworkOffering() {
-
-   }
-
-   /**
-    * 
     * @return the id of the network offering
     */
    public String getId() {
-      return id;
+      return this.id;
    }
 
    /**
-    * 
     * @return the name of the network offering
     */
-
+   @Nullable
    public String getName() {
-      return name;
+      return this.name;
    }
 
    /**
-    * 
     * @return an alternate display text of the network offering.
     */
+   @Nullable
    public String getDisplayText() {
-      return displayText;
+      return this.displayText;
    }
 
    /**
-    * 
     * @return the date this network offering was created
     */
    @Nullable
    public Date getCreated() {
-      return created;
+      return this.created;
    }
 
    /**
-    * 
     * @return Availability name for the offering
     */
+   @Nullable
    public NetworkOfferingAvailabilityType getAvailability() {
-      return availability;
+      return this.availability;
    }
 
    /**
-    * 
-    * @return true if network offering supports vlans, false otherwise
-    */
-   public boolean supportsVLAN() {
-      return supportsVLAN;
-   }
-
-   /**
-    * 
     * @return the max number of concurrent connection the network offering
-    *         supports
+         supports
     */
    @Nullable
    public Integer getMaxConnections() {
-      return maxConnections;
+      return this.maxConnections;
    }
 
    /**
-    * 
     * @return true if network offering is default, false otherwise
     */
    public boolean isDefault() {
-      return isDefault;
+      return this.isDefault;
    }
 
    /**
-    * 
+    * @return true if network offering supports vlans, false otherwise
+    */
+   public boolean supportsVLAN() {
+      return this.supportsVLAN;
+   }
+
+   /**
     * @return the traffic type for this network offering
     */
+   @Nullable
    public TrafficType getTrafficType() {
-      return trafficType;
+      return this.trafficType;
    }
 
    /**
-    * 
     * @return the guest ip type for this network offering
     */
+   @Nullable
    public GuestIPType getGuestIPType() {
-      return guestIPType;
+      return this.guestIPType;
    }
 
    /**
-    * 
     * @return data transfer rate in megabits per second allowed.
     */
    public int getNetworkRate() {
-      return networkRate;
+      return this.networkRate;
    }
 
    /**
-    * 
     * @return the tags for the network offering
     */
-   public Set<String> getTags() {
-      return tags != null ? ImmutableSet.copyOf(Splitter.on(',').split(tags)) : ImmutableSet.<String> of();
-   }
-
-   @Override
-   public boolean equals(Object o) {
-      if (this == o) return true;
-      if (o == null || getClass() != o.getClass()) return false;
-
-      NetworkOffering that = (NetworkOffering) o;
-
-      if (!Objects.equal(availability, that.availability)) return false;
-      if (!Objects.equal(created, that.created)) return false;
-      if (!Objects.equal(displayText, that.displayText)) return false;
-      if (!Objects.equal(id, that.id)) return false;
-      if (!Objects.equal(isDefault, that.isDefault)) return false;
-      if (!Objects.equal(maxConnections, that.maxConnections)) return false;
-      if (!Objects.equal(name, that.name)) return false;
-      if (!Objects.equal(supportsVLAN, that.supportsVLAN)) return false;
-      if (!Objects.equal(tags, that.tags)) return false;
-      if (!Objects.equal(trafficType, that.trafficType)) return false;
-
-      return true;
+   @Nullable
+   public String getTags() {
+      return this.tags;
    }
 
    @Override
    public int hashCode() {
-       return Objects.hashCode(availability, created, displayText, id, isDefault, maxConnections, name, supportsVLAN, tags, trafficType);
+      return Objects.hashCode(id, name, displayText, created, availability, maxConnections, isDefault, supportsVLAN, trafficType, guestIPType, networkRate, tags);
    }
 
    @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      NetworkOffering that = NetworkOffering.class.cast(obj);
+      return Objects.equal(this.id, that.id)
+               && Objects.equal(this.name, that.name)
+               && Objects.equal(this.displayText, that.displayText)
+               && Objects.equal(this.created, that.created)
+               && Objects.equal(this.availability, that.availability)
+               && Objects.equal(this.maxConnections, that.maxConnections)
+               && Objects.equal(this.isDefault, that.isDefault)
+               && Objects.equal(this.supportsVLAN, that.supportsVLAN)
+               && Objects.equal(this.trafficType, that.trafficType)
+               && Objects.equal(this.guestIPType, that.guestIPType)
+               && Objects.equal(this.networkRate, that.networkRate)
+               && Objects.equal(this.tags, that.tags);
+   }
+   
+   protected ToStringHelper string() {
+      return Objects.toStringHelper(this)
+            .add("id", id).add("name", name).add("displayText", displayText).add("created", created).add("availability", availability).add("maxConnections", maxConnections).add("isDefault", isDefault).add("supportsVLAN", supportsVLAN).add("trafficType", trafficType).add("guestIPType", guestIPType).add("networkRate", networkRate).add("tags", tags);
+   }
+   
+   @Override
    public String toString() {
-      return "NetworkOffering{" +
-            "id=" + id +
-            ", name='" + name + '\'' +
-            ", displayText='" + displayText + '\'' +
-            ", created=" + created +
-            ", availability=" + availability +
-            ", maxConnections=" + maxConnections +
-            ", isDefault=" + isDefault +
-            ", supportsVLAN=" + supportsVLAN +
-            ", trafficType=" + trafficType +
-            ", guestIPType=" + guestIPType +
-            ", networkRate=" + networkRate +
-            ", tags='" + tags + '\'' +
-            '}';
+      return string().toString();
    }
 
    @Override
-   public int compareTo(NetworkOffering arg0) {
-      return id.compareTo(arg0.getId());
+   public int compareTo(NetworkOffering o) {
+      return id.compareTo(o.getId());
    }
 
 }
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/NetworkService.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/NetworkService.java
index 71a1db5..158e0ef 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/NetworkService.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/NetworkService.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -20,129 +20,229 @@
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import java.beans.ConstructorProperties;
 import java.util.Map;
-import java.util.SortedSet;
-import java.util.Map.Entry;
+import java.util.Set;
+
+import javax.inject.Named;
+
+import org.jclouds.javax.annotation.Nullable;
 
 import com.google.common.base.Objects;
-import com.google.common.collect.ImmutableMap;
+import com.google.common.base.Objects.ToStringHelper;
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.ImmutableSortedMap;
 import com.google.common.collect.ImmutableSortedSet;
-import com.google.common.collect.ImmutableMap.Builder;
-import com.google.gson.annotations.SerializedName;
+import com.google.common.collect.Sets;
 
 /**
- * 
+ * Class NetworkService
+ *
  * @author Adrian Cole
  */
 public class NetworkService implements Comparable<NetworkService> {
-   // internal only to match json type
-   private static class Capability implements Comparable<Capability> {
 
-      private String name;
-      private String value;
+   public static class Capability implements Comparable<Capability> {
 
-      private Capability() {
-
+      public static Builder<?> builder() {
+         return new ConcreteBuilder();
       }
 
-      private Capability(String name, String value) {
-         this.name = name;
+      public Builder<?> toBuilder() {
+         return new ConcreteBuilder().fromCapability(this);
+      }
+
+      public static abstract class Builder<T extends Builder<T>>  {
+         protected abstract T self();
+
+         protected String name;
+         protected String value;
+
+         /**
+          * @see Capability#getName()
+          */
+         public T name(String name) {
+            this.name = name;
+            return self();
+         }
+
+         /**
+          * @see Capability#getValue()
+          */
+         public T value(String value) {
+            this.value = value;
+            return self();
+         }
+
+         public Capability build() {
+            return new Capability(name, value);
+         }
+
+         public T fromCapability(Capability in) {
+            return this
+                  .name(in.getName())
+                  .value(in.getValue());
+         }
+         
+      }
+
+      private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+         @Override
+         protected ConcreteBuilder self() {
+            return this;
+         }
+      }
+
+      private final String name;
+      private final String value;
+
+      @ConstructorProperties({
+            "name", "value"
+      })
+      protected Capability(String name, @Nullable String value) {
+         this.name = checkNotNull(name, "name");
          this.value = value;
       }
 
-      @Override
-      public boolean equals(Object o) {
-          if (this == o) return true;
-          if (o == null || getClass() != o.getClass()) return false;
-          
-          NetworkService.Capability that = (NetworkService.Capability) o;
-          
-          if (!Objects.equal(name, that.name)) return false;
-          if (!Objects.equal(value, that.value)) return false;
-          
-          return true;
+      public String getName() {
+         return this.name;
       }
-      
+
+      @Nullable
+      public String getValue() {
+         return this.value;
+      }
+
       @Override
       public int hashCode() {
-          return Objects.hashCode(name, value);
+         return Objects.hashCode(name, value);
+      }
+
+      @Override
+      public boolean equals(Object obj) {
+         if (this == obj) return true;
+         if (obj == null || getClass() != obj.getClass()) return false;
+         Capability that = Capability.class.cast(obj);
+         return Objects.equal(this.name, that.name)
+               && Objects.equal(this.value, that.value);
+      }
+
+      protected ToStringHelper string() {
+         return Objects.toStringHelper(this)
+               .add("name", name).add("value", value);
       }
 
       @Override
       public String toString() {
-         return "[name=" + name + ", value=" + value + "]";
+         return string().toString();
       }
 
       @Override
       public int compareTo(Capability o) {
-         return name.compareTo(o.name);
+         return name.compareTo(o.getName());
+      }
+   }
+   
+   public static Builder<?> builder() {
+      return new ConcreteBuilder();
+   }
+
+   public Builder<?> toBuilder() {
+      return new ConcreteBuilder().fromNetworkService(this);
+   }
+
+   public static abstract class Builder<T extends Builder<T>> {
+      protected abstract T self();
+
+      protected String name;
+      protected Set<Capability> capabilities = Sets.newHashSet();
+
+      /**
+       * @see NetworkService#getName()
+       */
+      public T name(String name) {
+         this.name = name;
+         return self();
       }
 
+      /**
+       * @see NetworkService#getCapabilities()
+       */
+      public T capabilities(Map<String, String> capabilities) {
+         for (Map.Entry<String, String> entry : capabilities.entrySet()) {
+            this.capabilities.add(Capability.builder().name(entry.getKey()).value(entry.getValue()).build());
+         }
+         return self();
+      }
+
+      public NetworkService build() {
+         return new NetworkService(name, capabilities);
+      }
+
+      public T fromNetworkService(NetworkService in) {
+         return this
+               .name(in.getName())
+               .capabilities(in.getCapabilities());
+      }
    }
 
-   private String name;
-   @SerializedName("capability")
-   // so tests and serialization comes out expected
-   private SortedSet<? extends NetworkService.Capability> capabilities = ImmutableSortedSet.of();
-
-   NetworkService() {
-
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
    }
 
-   public NetworkService(String name) {
-      this(name, ImmutableMap.<String, String> of());
-   }
+   private final String name;
+   @Named("capability")
+   private final Set<Capability> capabilities;
 
-   public NetworkService(String name, Map<String, String> capabilities) {
+   @ConstructorProperties({
+         "name", "capability"
+   })
+   protected NetworkService(String name, @Nullable Set<Capability> capabilities) {
       this.name = checkNotNull(name, "name");
-      ImmutableSortedSet.Builder<Capability> internal = ImmutableSortedSet.naturalOrder();
-      for (Entry<String, String> capabililty : checkNotNull(capabilities, "capabilities").entrySet())
-         internal.add(new Capability(capabililty.getKey(), capabililty.getValue()));
-      this.capabilities = internal.build();
+      this.capabilities = capabilities == null ? ImmutableSet.<Capability>of() : ImmutableSortedSet.copyOf(capabilities);
    }
 
    public String getName() {
-      return name;
+      return this.name;
    }
 
    public Map<String, String> getCapabilities() {
       // so tests and serialization comes out expected
-      Builder<String, String> returnVal = ImmutableSortedMap.naturalOrder();
+      ImmutableSortedMap.Builder<String, String> returnVal = ImmutableSortedMap.naturalOrder();
       for (Capability capability : capabilities) {
          returnVal.put(capability.name, capability.value);
       }
       return returnVal.build();
    }
-
+   
    @Override
-   public boolean equals(Object o) {
-      if (this == o) return true;
-      if (o == null || getClass() != o.getClass()) return false;
-
-      NetworkService that = (NetworkService) o;
-
-      if (!Objects.equal(capabilities, that.capabilities)) return false;
-      if (!Objects.equal(name, that.name)) return false;
-
-      return true;
+   public int hashCode() {
+      return Objects.hashCode(name, capabilities);
    }
 
    @Override
-   public int hashCode() {
-       return Objects.hashCode(capabilities, name);
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      NetworkService that = NetworkService.class.cast(obj);
+      return Objects.equal(this.name, that.name)
+            && Objects.equal(this.capabilities, that.capabilities);
+   }
+
+   protected ToStringHelper string() {
+      return Objects.toStringHelper(this).add("name", name).add("capabilities", capabilities);
    }
 
    @Override
    public String toString() {
-      return "NetworkService{" +
-            "name='" + name + '\'' +
-            ", capabilities=" + capabilities +
-            '}';
+      return string().toString();
    }
 
    @Override
    public int compareTo(NetworkService o) {
       return name.compareTo(o.getName());
    }
-}
\ No newline at end of file
+}
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/OSType.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/OSType.java
index 1a1305e..4ca638a 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/OSType.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/OSType.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -18,56 +18,92 @@
  */
 package org.jclouds.cloudstack.domain;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.beans.ConstructorProperties;
+
+import javax.inject.Named;
+
+import org.jclouds.javax.annotation.Nullable;
+
 import com.google.common.base.Objects;
-import com.google.gson.annotations.SerializedName;
+import com.google.common.base.Objects.ToStringHelper;
 
 /**
- * 
+ * Class OSType
+ *
  * @author Adrian Cole
  */
 public class OSType implements Comparable<OSType> {
 
-   public static Builder builder() {
-      return new Builder();
+   public static Builder<?> builder() {
+      return new ConcreteBuilder();
    }
 
-   public static class Builder {
-      private String id;
-      private String OSCategoryId;
-      private String description;
+   public Builder<?> toBuilder() {
+      return new ConcreteBuilder().fromOSType(this);
+   }
 
-      public Builder id(String id) {
+   public static abstract class Builder<T extends Builder<T>>  {
+      protected abstract T self();
+
+      protected String id;
+      protected String OSCategoryId;
+      protected String description;
+
+      /**
+       * @see OSType#getId()
+       */
+      public T id(String id) {
          this.id = id;
-         return this;
+         return self();
       }
 
-      public Builder OSCategoryId(String OSCategoryId) {
+      /**
+       * @see OSType#getOSCategoryId()
+       */
+      public T OSCategoryId(String OSCategoryId) {
          this.OSCategoryId = OSCategoryId;
-         return this;
+         return self();
       }
 
-      public Builder description(String description) {
+      /**
+       * @see OSType#getDescription()
+       */
+      public T description(String description) {
          this.description = description;
-         return this;
+         return self();
       }
 
       public OSType build() {
          return new OSType(id, OSCategoryId, description);
       }
+
+      public T fromOSType(OSType in) {
+         return this
+               .id(in.getId())
+               .OSCategoryId(in.getOSCategoryId())
+               .description(in.getDescription());
+      }
    }
 
-   // for deserialization
-   OSType() {
-
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
    }
 
-   private String id;
-   @SerializedName("oscategoryid")
-   private String OSCategoryId;
-   private String description;
+   private final String id;
+   @Named("oscategoryid")
+   private final String OSCategoryId;
+   private final String description;
 
-   public OSType(String id, String OSCategoryId, String description) {
-      this.id = id;
+   @ConstructorProperties({
+         "id", "oscategoryid", "description"
+   })
+   protected OSType(String id, @Nullable String OSCategoryId, @Nullable String description) {
+      this.id = checkNotNull(id, "id");
       this.OSCategoryId = OSCategoryId;
       this.description = description;
    }
@@ -76,54 +112,51 @@
     * @return the ID of the OS type
     */
    public String getId() {
-      return id;
+      return this.id;
    }
 
    /**
     * @return the ID of the OS category
     */
+   @Nullable
    public String getOSCategoryId() {
-      return OSCategoryId;
+      return this.OSCategoryId;
    }
 
    /**
     * @return the name/description of the OS type
     */
+   @Nullable
    public String getDescription() {
-      return description;
-   }
-
-   @Override
-   public boolean equals(Object o) {
-      if (this == o) return true;
-      if (o == null || getClass() != o.getClass()) return false;
-
-      OSType that = (OSType) o;
-
-      if (!Objects.equal(OSCategoryId, that.OSCategoryId)) return false;
-      if (!Objects.equal(description, that.description)) return false;
-      if (!Objects.equal(id, that.id)) return false;
-
-      return true;
+      return this.description;
    }
 
    @Override
    public int hashCode() {
-       return Objects.hashCode(OSCategoryId, description, id);
+      return Objects.hashCode(id, OSCategoryId, description);
+   }
+
+   @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      OSType that = OSType.class.cast(obj);
+      return Objects.equal(this.id, that.id)
+            && Objects.equal(this.OSCategoryId, that.OSCategoryId)
+            && Objects.equal(this.description, that.description);
+   }
+
+   protected ToStringHelper string() {
+      return Objects.toStringHelper(this).add("id", id).add("OSCategoryId", OSCategoryId).add("description", description);
    }
 
    @Override
    public String toString() {
-      return "OSType{" +
-            "id=" + id +
-            ", OSCategoryId=" + OSCategoryId +
-            ", description='" + description + '\'' +
-            '}';
+      return string().toString();
    }
 
    @Override
-   public int compareTo(OSType arg0) {
-      return id.compareTo(arg0.getId());
+   public int compareTo(OSType o) {
+      return id.compareTo(o.getId());
    }
-
 }
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Pod.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Pod.java
index db49ce6..6dc5366 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Pod.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Pod.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -18,130 +18,162 @@
  */
 package org.jclouds.cloudstack.domain;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.beans.ConstructorProperties;
+
+import javax.inject.Named;
+
+import org.jclouds.javax.annotation.Nullable;
+
 import com.google.common.base.Objects;
-import com.google.gson.annotations.SerializedName;
+import com.google.common.base.Objects.ToStringHelper;
 
 /**
  * Represents a Pod in CloudStack.
- *
+ * 
  * @author Richard Downer
- */
+*/
 public class Pod implements Comparable<Pod> {
 
-   public static Builder builder() {
-      return new Builder();
+   public static Builder<?> builder() { 
+      return new ConcreteBuilder();
+   }
+   
+   public Builder<?> toBuilder() { 
+      return new ConcreteBuilder().fromPod(this);
    }
 
-   public static class Builder {
+   public static abstract class Builder<T extends Builder<T>>  {
+      protected abstract T self();
 
-      private String id;
-      private String name;
-      private String zoneId;
-      private String zoneName;
-      private String gateway;
-      private String netmask;
-      private String startIp;
-      private String endIp;
-      private AllocationState allocationState;
-
-      private Builder() {}
-
-      /**
-       * @param id the ID of the Pod
+      protected String id;
+      protected String name;
+      protected String zoneId;
+      protected String zoneName;
+      protected String gateway;
+      protected String netmask;
+      protected String startIp;
+      protected String endIp;
+      protected AllocationState allocationState;
+   
+      /** 
+       * @see Pod#getId()
        */
-      public Builder id(String id) {
-         this.id  = id;
-         return this;
+      public T id(String id) {
+         this.id = id;
+         return self();
       }
 
-      /**
-       * @param name the name of the Pod
+      /** 
+       * @see Pod#getName()
        */
-      public Builder name(String name) {
+      public T name(String name) {
          this.name = name;
-         return this;
+         return self();
       }
 
-      /**
-       * @param zoneId the Zone ID of the Pod
+      /** 
+       * @see Pod#getZoneId()
        */
-      public Builder zoneId(String zoneId) {
+      public T zoneId(String zoneId) {
          this.zoneId = zoneId;
-         return this;
+         return self();
       }
 
-      /**
-       * @param zoneName the Zone name of the Pod
+      /** 
+       * @see Pod#getZoneName()
        */
-      public Builder zoneName(String zoneName) {
+      public T zoneName(String zoneName) {
          this.zoneName = zoneName;
-         return this;
+         return self();
       }
 
-      /**
-       * @param gateway the gateway of the Pod
+      /** 
+       * @see Pod#getGateway()
        */
-      public Builder gateway(String gateway) {
+      public T gateway(String gateway) {
          this.gateway = gateway;
-         return this;
+         return self();
       }
 
-      /**
-       * @param netmask the netmask of the Pod
+      /** 
+       * @see Pod#getNetmask()
        */
-      public Builder netmask(String netmask) {
+      public T netmask(String netmask) {
          this.netmask = netmask;
-         return this;
+         return self();
       }
 
-      /**
-       * @param startIp the starting IP for the Pod
+      /** 
+       * @see Pod#getStartIp()
        */
-      public Builder startIp(String startIp) {
+      public T startIp(String startIp) {
          this.startIp = startIp;
-         return this;
+         return self();
       }
 
-      /**
-       * @param endIp the ending IP for the Pod
+      /** 
+       * @see Pod#getEndIp()
        */
-      public Builder endIp(String endIp) {
+      public T endIp(String endIp) {
          this.endIp = endIp;
-         return this;
+         return self();
       }
 
-      /**
-       * @param allocationState the allocation state of the cluster
+      /** 
+       * @see Pod#getAllocationState()
        */
-      public Builder allocationState(AllocationState allocationState) {
+      public T allocationState(AllocationState allocationState) {
          this.allocationState = allocationState;
-         return this;
+         return self();
       }
 
-      /**
-       * Build the Pod object
-       * @return the Pod object
-       */
       public Pod build() {
          return new Pod(id, name, zoneId, zoneName, gateway, netmask, startIp, endIp, allocationState);
       }
+      
+      public T fromPod(Pod in) {
+         return this
+                  .id(in.getId())
+                  .name(in.getName())
+                  .zoneId(in.getZoneId())
+                  .zoneName(in.getZoneName())
+                  .gateway(in.getGateway())
+                  .netmask(in.getNetmask())
+                  .startIp(in.getStartIp())
+                  .endIp(in.getEndIp())
+                  .allocationState(in.getAllocationState());
+      }
    }
 
-   private String id;
-   private String name;
-   @SerializedName("zoneid") private String zoneId;
-   @SerializedName("zonename") private String zoneName;
-   private String gateway;
-   private String netmask;
-   @SerializedName("startip") private String startIp;
-   @SerializedName("endip") private String endIp;
-   @SerializedName("allocationstate") private AllocationState allocationState;
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
+   }
 
-   /* Just for the serializer */
-   Pod() {}
+   private final String id;
+   private final String name;
+   @Named("zoneid")
+   private final String zoneId;
+   @Named("zonename")
+   private final String zoneName;
+   private final String gateway;
+   private final String netmask;
+   @Named("startip")
+   private final String startIp;
+   @Named("endip")
+   private final String endIp;
+   @Named("allocationstate")
+   private final AllocationState allocationState;
 
-   public Pod(String id, String name, String zoneId, String zoneName, String gateway, String netmask, String startIp, String endIp, AllocationState allocationState) {
-      this.id = id;
+   @ConstructorProperties({
+      "id", "name", "zoneid", "zonename", "gateway", "netmask", "startip", "endip", "allocationstate"
+   })
+   protected Pod(String id, @Nullable String name, @Nullable String zoneId, @Nullable String zoneName, @Nullable String gateway, @Nullable String netmask, @Nullable String startIp, @Nullable String endIp, @Nullable AllocationState allocationState) {
+      this.id = checkNotNull(id, "id");
       this.name = name;
       this.zoneId = zoneId;
       this.zoneName = zoneName;
@@ -156,103 +188,102 @@
     * @return id the ID of the Pod
     */
    public String getId() {
-      return id;
+      return this.id;
    }
 
    /**
     * @return name the name of the Pod
     */
+   @Nullable
    public String getName() {
-      return name;
+      return this.name;
    }
 
    /**
     * @return zoneId the Zone ID of the Pod
     */
+   @Nullable
    public String getZoneId() {
-      return zoneId;
+      return this.zoneId;
    }
 
    /**
     * @return zoneName the Zone name of the Pod
     */
+   @Nullable
    public String getZoneName() {
-      return zoneName;
+      return this.zoneName;
    }
 
    /**
     * @return gateway the gateway of the Pod
     */
+   @Nullable
    public String getGateway() {
-      return gateway;
+      return this.gateway;
    }
 
    /**
     * @return netmask the netmask of the Pod
     */
+   @Nullable
    public String getNetmask() {
-      return netmask;
+      return this.netmask;
    }
 
    /**
     * @return startIp the starting IP for the Pod
     */
+   @Nullable
    public String getStartIp() {
-      return startIp;
+      return this.startIp;
    }
 
    /**
     * @return endIp the ending IP for the Pod
     */
+   @Nullable
    public String getEndIp() {
-      return endIp;
+      return this.endIp;
    }
 
    /**
-    * @param allocationState the allocation state of the cluster
+    * @return the allocation state of the cluster
     */
+   @Nullable
    public AllocationState getAllocationState() {
-      return allocationState;
-   }
-
-   @Override
-   public boolean equals(Object o) {
-      if (this == o) return true;
-      if (o == null || getClass() != o.getClass()) return false;
-
-      Pod that = (Pod) o;
-
-      if (!Objects.equal(id, that.id)) return false;
-      if (!Objects.equal(zoneId, that.zoneId)) return false;
-      if (!Objects.equal(allocationState, that.allocationState)) return false;
-      if (!Objects.equal(endIp, that.endIp)) return false;
-      if (!Objects.equal(gateway, that.gateway)) return false;
-      if (!Objects.equal(name, that.name)) return false;
-      if (!Objects.equal(netmask, that.netmask)) return false;
-      if (!Objects.equal(startIp, that.startIp)) return false;
-      if (!Objects.equal(zoneName, that.zoneName)) return false;
-
-      return true;
+      return this.allocationState;
    }
 
    @Override
    public int hashCode() {
-       return Objects.hashCode(id, zoneId, allocationState, endIp, gateway, name, netmask, startIp, zoneName);
+      return Objects.hashCode(id, name, zoneId, zoneName, gateway, netmask, startIp, endIp, allocationState);
    }
 
    @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      Pod that = Pod.class.cast(obj);
+      return Objects.equal(this.id, that.id)
+               && Objects.equal(this.name, that.name)
+               && Objects.equal(this.zoneId, that.zoneId)
+               && Objects.equal(this.zoneName, that.zoneName)
+               && Objects.equal(this.gateway, that.gateway)
+               && Objects.equal(this.netmask, that.netmask)
+               && Objects.equal(this.startIp, that.startIp)
+               && Objects.equal(this.endIp, that.endIp)
+               && Objects.equal(this.allocationState, that.allocationState);
+   }
+   
+   protected ToStringHelper string() {
+      return Objects.toStringHelper(this)
+            .add("id", id).add("name", name).add("zoneId", zoneId).add("zoneName", zoneName).add("gateway", gateway).add("netmask", netmask).add("startIp", startIp).add("endIp", endIp).add("allocationState", allocationState);
+   }
+   
+   @Override
    public String toString() {
-      return "Pod{" +
-         "id=" + id +
-         ", name='" + name + '\'' +
-         ", zoneId=" + zoneId +
-         ", zoneName='" + zoneName + '\'' +
-         ", gateway='" + gateway + '\'' +
-         ", netmask='" + netmask + '\'' +
-         ", startIp='" + startIp + '\'' +
-         ", endIp='" + endIp + '\'' +
-         ", allocationState=" + allocationState +
-         '}';
+      return string().toString();
    }
 
    @Override
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/PortForwardingRule.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/PortForwardingRule.java
index ee7f10c..73f317a 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/PortForwardingRule.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/PortForwardingRule.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -18,16 +18,25 @@
  */
 package org.jclouds.cloudstack.domain;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.beans.ConstructorProperties;
 import java.util.Set;
 
+import javax.inject.Named;
+
+import org.jclouds.javax.annotation.Nullable;
+
 import com.google.common.base.CaseFormat;
 import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
 import com.google.common.collect.ImmutableSet;
-import com.google.gson.annotations.SerializedName;
 
 /**
+ * Class PortForwardingRule
+ * 
  * @author Adrian Cole, Andrei Savu
- */
+*/
 public class PortForwardingRule implements Comparable<PortForwardingRule> {
 
    public static enum Protocol {
@@ -73,126 +82,204 @@
       }
    }
 
-   public static Builder builder() {
-      return new Builder();
+   public static Builder<?> builder() { 
+      return new ConcreteBuilder();
+   }
+   
+   public Builder<?> toBuilder() { 
+      return new ConcreteBuilder().fromPortForwardingRule(this);
    }
 
-   public static class Builder {
-      private String id;
-      private String IPAddress;
-      private String IPAddressId;
-      private int privatePort;
-      private Protocol protocol;
-      public int publicPort;
-      private State state;
-      private String virtualMachineDisplayName;
-      public String virtualMachineId;
-      private String virtualMachineName;
-      private Set<String> CIDRs = ImmutableSet.of();
-      private int privateEndPort;
-      private int publicEndPort;
+   public static abstract class Builder<T extends Builder<T>>  {
+      protected abstract T self();
 
-      public Builder id(String id) {
+      protected String id;
+      protected String IPAddress;
+      protected String IPAddressId;
+      protected int privatePort;
+      protected Protocol protocol;
+      protected int publicPort;
+      protected State state;
+      protected String virtualMachineDisplayName;
+      protected String virtualMachineId;
+      protected String virtualMachineName;
+      protected Set<String> CIDRs = ImmutableSet.of();
+      protected int privateEndPort;
+      protected int publicEndPort;
+   
+      /** 
+       * @see PortForwardingRule#getId()
+       */
+      public T id(String id) {
          this.id = id;
-         return this;
+         return self();
       }
 
-      public Builder IPAddress(String IPAddress) {
+      /** 
+       * @see PortForwardingRule#getIPAddress()
+       */
+      public T IPAddress(String IPAddress) {
          this.IPAddress = IPAddress;
-         return this;
+         return self();
       }
 
-      public Builder IPAddressId(String IPAddressId) {
+      /** 
+       * @see PortForwardingRule#getIPAddressId()
+       */
+      public T IPAddressId(String IPAddressId) {
          this.IPAddressId = IPAddressId;
-         return this;
+         return self();
       }
 
-      public Builder privatePort(int privatePort) {
+      /** 
+       * @see PortForwardingRule#getPrivatePort()
+       */
+      public T privatePort(int privatePort) {
          this.privatePort = privatePort;
-         return this;
+         return self();
       }
 
-      public Builder protocol(Protocol protocol) {
+      /** 
+       * @see PortForwardingRule#getProtocol()
+       */
+      public T protocol(Protocol protocol) {
          this.protocol = protocol;
-         return this;
+         return self();
       }
 
-      public Builder publicPort(int publicPort) {
+      /** 
+       * @see PortForwardingRule#getPublicPort()
+       */
+      public T publicPort(int publicPort) {
          this.publicPort = publicPort;
-         return this;
+         return self();
       }
 
-      public Builder state(State state) {
+      /** 
+       * @see PortForwardingRule#getState()
+       */
+      public T state(State state) {
          this.state = state;
-         return this;
+         return self();
       }
 
-      public Builder virtualMachineDisplayName(String virtualMachineDisplayName) {
+      /** 
+       * @see PortForwardingRule#getVirtualMachineDisplayName()
+       */
+      public T virtualMachineDisplayName(String virtualMachineDisplayName) {
          this.virtualMachineDisplayName = virtualMachineDisplayName;
-         return this;
+         return self();
       }
 
-      public Builder virtualMachineId(String virtualMachineId) {
+      /** 
+       * @see PortForwardingRule#getVirtualMachineId()
+       */
+      public T virtualMachineId(String virtualMachineId) {
          this.virtualMachineId = virtualMachineId;
-         return this;
+         return self();
       }
 
-      public Builder virtualMachineName(String virtualMachineName) {
+      /** 
+       * @see PortForwardingRule#getVirtualMachineName()
+       */
+      public T virtualMachineName(String virtualMachineName) {
          this.virtualMachineName = virtualMachineName;
-         return this;
+         return self();
       }
 
-      public Builder CIDRs(Set<String> CIDRs) {
-         this.CIDRs = CIDRs;
-         return this;
+      /** 
+       * @see PortForwardingRule#getCIDRs()
+       */
+      public T CIDRs(Set<String> CIDRs) {
+         this.CIDRs = ImmutableSet.copyOf(checkNotNull(CIDRs, "CIDRs"));      
+         return self();
       }
 
-      public Builder privateEndPort(int privateEndPort) {
+      public T CIDRs(String... in) {
+         return CIDRs(ImmutableSet.copyOf(in));
+      }
+
+      /** 
+       * @see PortForwardingRule#getPrivateEndPort()
+       */
+      public T privateEndPort(int privateEndPort) {
          this.privateEndPort = privateEndPort;
-         return this;
+         return self();
       }
 
-      public Builder publicEndPort(int publicEndPort) {
+      /** 
+       * @see PortForwardingRule#getPublicEndPort()
+       */
+      public T publicEndPort(int publicEndPort) {
          this.publicEndPort = publicEndPort;
-         return this;
+         return self();
       }
 
       public PortForwardingRule build() {
-         return new PortForwardingRule(id, IPAddress, IPAddressId, privatePort, protocol, publicPort, state,
-            virtualMachineDisplayName, virtualMachineId, virtualMachineName, CIDRs, privateEndPort, publicEndPort);
+         return new PortForwardingRule(id, IPAddress, IPAddressId, privatePort, protocol, publicPort, state, virtualMachineDisplayName,
+               virtualMachineId, virtualMachineName, CIDRs, privateEndPort, publicEndPort);
+      }
+      
+      public T fromPortForwardingRule(PortForwardingRule in) {
+         return this
+                  .id(in.getId())
+                  .IPAddress(in.getIPAddress())
+                  .IPAddressId(in.getIPAddressId())
+                  .privatePort(in.getPrivatePort())
+                  .protocol(in.getProtocol())
+                  .publicPort(in.getPublicPort())
+                  .state(in.getState())
+                  .virtualMachineDisplayName(in.getVirtualMachineDisplayName())
+                  .virtualMachineId(in.getVirtualMachineId())
+                  .virtualMachineName(in.getVirtualMachineName())
+                  .CIDRs(in.getCIDRs())
+                  .privateEndPort(in.getPrivateEndPort())
+                  .publicEndPort(in.getPublicEndPort());
       }
    }
 
-   private String id;
-   @SerializedName("ipaddress")
-   private String IPAddress;
-   @SerializedName("ipaddressid")
-   private String IPAddressId;
-   @SerializedName("privateport")
-   private int privatePort;
-   private Protocol protocol;
-   @SerializedName("publicport")
-   public int publicPort;
-   private State state;
-   @SerializedName("virtualmachinedisplayname")
-   private String virtualMachineDisplayName;
-   @SerializedName("virtualmachineid")
-   public String virtualMachineId;
-   @SerializedName("virtualmachinename")
-   private String virtualMachineName;
-   @SerializedName("cidrlist")
-   private Set<String> CIDRs;
-   @SerializedName("privateendport")
-   private int privateEndPort;
-   @SerializedName("publicendport")
-   private int publicEndPort;
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
+   }
 
-   public PortForwardingRule(String id, String iPAddress, String iPAddressId, int privatePort, Protocol protocol,
-                             int publicPort, State state, String virtualMachineDisplayName, String virtualMachineId,
-                             String virtualMachineName, Set<String> CIDRs, int privateEndPort, int publicEndPort) {
-      this.id = id;
-      this.IPAddress = iPAddress;
-      this.IPAddressId = iPAddressId;
+   private final String id;
+   @Named("ipaddress")
+   private final String IPAddress;
+   @Named("ipaddressid")
+   private final String IPAddressId;
+   @Named("privateport")
+   private final int privatePort;
+   private final PortForwardingRule.Protocol protocol;
+   @Named("publicport")
+   private final int publicPort;
+   private final PortForwardingRule.State state;
+   @Named("virtualmachinedisplayname")
+   private final String virtualMachineDisplayName;
+   @Named("virtualmachineid")
+   private final String virtualMachineId;
+   @Named("virtualmachinename")
+   private final String virtualMachineName;
+   @Named("cidrlist")
+   private final Set<String> CIDRs;
+   @Named("privateendport")
+   private final int privateEndPort;
+   @Named("publicendport")
+   private final int publicEndPort;
+
+   @ConstructorProperties({
+      "id", "ipaddress", "ipaddressid", "privateport", "protocol", "publicport", "state", "virtualmachinedisplayname",
+         "virtualmachineid", "virtualmachinename", "cidrlist", "privateendport", "publicendport"
+   })
+   protected PortForwardingRule(String id, @Nullable String IPAddress, @Nullable String IPAddressId, int privatePort,
+                                @Nullable Protocol protocol, int publicPort, @Nullable State state,
+                                @Nullable String virtualMachineDisplayName, @Nullable String virtualMachineId,
+                                @Nullable String virtualMachineName, @Nullable Set<String> CIDRs, int privateEndPort, int publicEndPort) {
+      this.id = checkNotNull(id, "id");
+      this.IPAddress = IPAddress;
+      this.IPAddressId = IPAddressId;
       this.privatePort = privatePort;
       this.protocol = protocol;
       this.publicPort = publicPort;
@@ -200,150 +287,149 @@
       this.virtualMachineDisplayName = virtualMachineDisplayName;
       this.virtualMachineId = virtualMachineId;
       this.virtualMachineName = virtualMachineName;
-      this.CIDRs = CIDRs;
+      this.CIDRs = CIDRs == null ? ImmutableSet.<String>of() : ImmutableSet.copyOf(CIDRs);      
       this.privateEndPort = privateEndPort;
       this.publicEndPort = publicEndPort;
    }
 
-   @Override
-   public int compareTo(PortForwardingRule arg0) {
-      return id.compareTo(arg0.getId());
-   }
-
    /**
     * @return the ID of the port forwarding rule
     */
    public String getId() {
-      return id;
+      return this.id;
    }
 
    /**
     * @return the public ip address for the port forwarding rule
     */
+   @Nullable
    public String getIPAddress() {
-      return IPAddress;
+      return this.IPAddress;
    }
 
    /**
     * @return the public ip address id for the port forwarding rule
     */
+   @Nullable
    public String getIPAddressId() {
-      return IPAddressId;
+      return this.IPAddressId;
    }
 
    /**
     * @return the private port for the port forwarding rule
     */
    public int getPrivatePort() {
-      return privatePort;
+      return this.privatePort;
    }
 
    /**
     * @return the protocol of the port forwarding rule
     */
+   @Nullable
    public Protocol getProtocol() {
-      return protocol;
+      return this.protocol;
    }
 
    /**
     * @return the public port for the port forwarding rule
     */
    public int getPublicPort() {
-      return publicPort;
+      return this.publicPort;
    }
 
    /**
     * @return the state of the rule
     */
+   @Nullable
    public State getState() {
-      return state;
+      return this.state;
    }
 
    /**
     * @return the VM display name for the port forwarding rule
     */
+   @Nullable
    public String getVirtualMachineDisplayName() {
-      return virtualMachineDisplayName;
+      return this.virtualMachineDisplayName;
    }
 
    /**
     * @return the VM ID for the port forwarding rule
     */
+   @Nullable
    public String getVirtualMachineId() {
-      return virtualMachineId;
+      return this.virtualMachineId;
    }
 
    /**
     * @return the VM name for the port forwarding rule
     */
+   @Nullable
    public String getVirtualMachineName() {
-      return virtualMachineName;
+      return this.virtualMachineName;
    }
 
    /**
     * @return the cidr list to forward traffic from
     */
    public Set<String> getCIDRs() {
-      return CIDRs;
+      return this.CIDRs;
    }
 
    /**
     * @return the starting port of port forwarding rule's private port range
     */
    public int getPrivateEndPort() {
-      return privateEndPort;
+      return this.privateEndPort;
    }
 
    /**
     * @return the starting port of port forwarding rule's public port range
     */
    public int getPublicEndPort() {
-      return publicEndPort;
-   }
-
-   @Override
-   public boolean equals(Object o) {
-      if (this == o) return true;
-      if (o == null || getClass() != o.getClass()) return false;
-
-      PortForwardingRule that = (PortForwardingRule) o;
-
-      if (!Objects.equal(IPAddress, that.IPAddress)) return false;
-      if (!Objects.equal(IPAddressId, that.IPAddressId)) return false;
-      if (!Objects.equal(id, that.id)) return false;
-      if (!Objects.equal(privatePort, that.privatePort)) return false;
-      if (!Objects.equal(protocol, that.protocol)) return false;
-      if (!Objects.equal(publicPort, that.publicPort)) return false;
-      if (!Objects.equal(state, that.state)) return false;
-      if (!Objects.equal(virtualMachineDisplayName, that.virtualMachineDisplayName)) return false;
-      if (!Objects.equal(virtualMachineId, that.virtualMachineId)) return false;
-      if (!Objects.equal(virtualMachineName, that.virtualMachineName)) return false;
-
-      return true;
+      return this.publicEndPort;
    }
 
    @Override
    public int hashCode() {
-       return Objects.hashCode(IPAddress, IPAddressId, id, privatePort, protocol, publicPort, state, virtualMachineDisplayName, virtualMachineId, virtualMachineName);
+      return Objects.hashCode(id, IPAddress, IPAddressId, privatePort, protocol, publicPort, state, virtualMachineDisplayName, virtualMachineId, virtualMachineName, CIDRs, privateEndPort, publicEndPort);
    }
 
    @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      PortForwardingRule that = PortForwardingRule.class.cast(obj);
+      return Objects.equal(this.id, that.id)
+               && Objects.equal(this.IPAddress, that.IPAddress)
+               && Objects.equal(this.IPAddressId, that.IPAddressId)
+               && Objects.equal(this.privatePort, that.privatePort)
+               && Objects.equal(this.protocol, that.protocol)
+               && Objects.equal(this.publicPort, that.publicPort)
+               && Objects.equal(this.state, that.state)
+               && Objects.equal(this.virtualMachineDisplayName, that.virtualMachineDisplayName)
+               && Objects.equal(this.virtualMachineId, that.virtualMachineId)
+               && Objects.equal(this.virtualMachineName, that.virtualMachineName)
+               && Objects.equal(this.CIDRs, that.CIDRs)
+               && Objects.equal(this.privateEndPort, that.privateEndPort)
+               && Objects.equal(this.publicEndPort, that.publicEndPort);
+   }
+   
+   protected ToStringHelper string() {
+      return Objects.toStringHelper(this)
+            .add("id", id).add("IPAddress", IPAddress).add("IPAddressId", IPAddressId).add("privatePort", privatePort)
+            .add("protocol", protocol).add("publicPort", publicPort).add("state", state).add("virtualMachineDisplayName", virtualMachineDisplayName)
+            .add("virtualMachineId", virtualMachineId).add("virtualMachineName", virtualMachineName).add("CIDRs", CIDRs)
+            .add("privateEndPort", privateEndPort).add("publicEndPort", publicEndPort);
+   }
+   
+   @Override
    public String toString() {
-      return "PortForwardingRule{" +
-         "id=" + id +
-         ", IPAddress='" + IPAddress + '\'' +
-         ", IPAddressId=" + IPAddressId +
-         ", privatePort=" + privatePort +
-         ", protocol='" + protocol + '\'' +
-         ", publicPort=" + publicPort +
-         ", state='" + state + '\'' +
-         ", virtualMachineDisplayName='" + virtualMachineDisplayName + '\'' +
-         ", virtualMachineId=" + virtualMachineId +
-         ", virtualMachineName='" + virtualMachineName + '\'' +
-         ", CIDRs=" + getCIDRs() +
-         ", privateEndPort=" + privateEndPort +
-         ", publicEndPort=" + publicEndPort +
-         '}';
+      return string().toString();
    }
 
+   @Override
+   public int compareTo(PortForwardingRule o) {
+      return id.compareTo(o.getId());
+   }
 }
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/PublicIPAddress.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/PublicIPAddress.java
index 596cfc0..12c759a 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/PublicIPAddress.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/PublicIPAddress.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -20,234 +20,339 @@
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import java.beans.ConstructorProperties;
 import java.util.Date;
 
-import javax.annotation.Nullable;
+import javax.inject.Named;
+
+import org.jclouds.javax.annotation.Nullable;
 
 import com.google.common.base.CaseFormat;
 import com.google.common.base.Objects;
-import com.google.gson.annotations.SerializedName;
+import com.google.common.base.Objects.ToStringHelper;
 
 /**
+ * Class PublicIPAddress
+ * 
  * @author Adrian Cole
- */
-public class PublicIPAddress implements Comparable<PublicIPAddress> {
+*/
+public class PublicIPAddress {
 
-   public static Builder builder() {
-      return new Builder();
+   /**
+    */
+   public static enum State {
+      ALLOCATING, ALLOCATED, RELEASING, UNRECOGNIZED;
+      
+      @Override
+      public String toString() {
+      return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, name());
+      }
+      
+      public static State fromValue(String state) {
+      try {
+      return valueOf(CaseFormat.UPPER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, checkNotNull(state, "state")));
+      } catch (IllegalArgumentException e) {
+      return UNRECOGNIZED;
+      }
+      }
+      
    }
 
-   public static class Builder {
-      private String id;
-      private String account;
-      private Date allocated;
-      private String associatedNetworkId;
-      private String domain;
-      private String domainId;
-      private boolean usesVirtualNetwork;
-      private String IPAddress;
-      private boolean isSourceNAT;
-      private boolean isStaticNAT;
-      private String networkId;
-      private State state;
-      private String virtualMachineDisplayName;
-      private String virtualMachineId;
-      private String virtualMachineName;
-      private String VLANId;
-      private String VLANName;
-      private String zoneId;
-      private String zoneName;
-      private String jobId;
-      private Integer jobStatus;
+   public static Builder<?> builder() { 
+      return new ConcreteBuilder();
+   }
+   
+   public Builder<?> toBuilder() { 
+      return new ConcreteBuilder().fromPublicIPAddress(this);
+   }
 
-      public Builder id(String id) {
+   public static abstract class Builder<T extends Builder<T>>  {
+      protected abstract T self();
+
+      protected String id;
+      protected String account;
+      protected Date allocated;
+      protected String associatedNetworkId;
+      protected String domain;
+      protected String domainId;
+      protected boolean usesVirtualNetwork;
+      protected String IPAddress;
+      protected boolean isSourceNAT;
+      protected boolean isStaticNAT;
+      protected String networkId;
+      protected PublicIPAddress.State state;
+      protected String virtualMachineDisplayName;
+      protected String virtualMachineId;
+      protected String virtualMachineName;
+      protected String VLANId;
+      protected String VLANName;
+      protected String zoneId;
+      protected String zoneName;
+      protected String jobId;
+      protected Integer jobStatus;
+   
+      /** 
+       * @see PublicIPAddress#getId()
+       */
+      public T id(String id) {
          this.id = id;
-         return this;
+         return self();
       }
 
-      public Builder account(String account) {
+      /** 
+       * @see PublicIPAddress#getAccount()
+       */
+      public T account(String account) {
          this.account = account;
-         return this;
+         return self();
       }
 
-      public Builder allocated(Date allocated) {
+      /** 
+       * @see PublicIPAddress#getAllocated()
+       */
+      public T allocated(Date allocated) {
          this.allocated = allocated;
-         return this;
+         return self();
       }
 
-      public Builder associatedNetworkId(String associatedNetworkId) {
+      /** 
+       * @see PublicIPAddress#getAssociatedNetworkId()
+       */
+      public T associatedNetworkId(String associatedNetworkId) {
          this.associatedNetworkId = associatedNetworkId;
-         return this;
+         return self();
       }
 
-      public Builder domain(String domain) {
+      /** 
+       * @see PublicIPAddress#getDomain()
+       */
+      public T domain(String domain) {
          this.domain = domain;
-         return this;
+         return self();
       }
 
-      public Builder domainId(String domainId) {
+      /** 
+       * @see PublicIPAddress#getDomainId()
+       */
+      public T domainId(String domainId) {
          this.domainId = domainId;
-         return this;
+         return self();
       }
 
-      public Builder usesVirtualNetwork(boolean usesVirtualNetwork) {
+      /** 
+       * @see PublicIPAddress#isUsesVirtualNetwork()
+       */
+      public T usesVirtualNetwork(boolean usesVirtualNetwork) {
          this.usesVirtualNetwork = usesVirtualNetwork;
-         return this;
+         return self();
       }
 
-      public Builder IPAddress(String IPAddress) {
+      /** 
+       * @see PublicIPAddress#getIPAddress()
+       */
+      public T IPAddress(String IPAddress) {
          this.IPAddress = IPAddress;
-         return this;
+         return self();
       }
 
-      public Builder isSourceNAT(boolean isSourceNAT) {
+      /** 
+       * @see PublicIPAddress#isSourceNAT()
+       */
+      public T isSourceNAT(boolean isSourceNAT) {
          this.isSourceNAT = isSourceNAT;
-         return this;
+         return self();
       }
 
-      public Builder isStaticNAT(boolean isStaticNAT) {
+      /** 
+       * @see PublicIPAddress#isStaticNAT()
+       */
+      public T isStaticNAT(boolean isStaticNAT) {
          this.isStaticNAT = isStaticNAT;
-         return this;
+         return self();
       }
 
-      public Builder networkId(String networkId) {
+      /** 
+       * @see PublicIPAddress#getNetworkId()
+       */
+      public T networkId(String networkId) {
          this.networkId = networkId;
-         return this;
+         return self();
       }
 
-      public Builder state(State state) {
+      /** 
+       * @see PublicIPAddress#getState()
+       */
+      public T state(PublicIPAddress.State state) {
          this.state = state;
-         return this;
+         return self();
       }
 
-      public Builder virtualMachineDisplayName(String virtualMachineDisplayName) {
+      /** 
+       * @see PublicIPAddress#getVirtualMachineDisplayName()
+       */
+      public T virtualMachineDisplayName(String virtualMachineDisplayName) {
          this.virtualMachineDisplayName = virtualMachineDisplayName;
-         return this;
+         return self();
       }
 
-      public Builder virtualMachineId(String virtualMachineId) {
+      /** 
+       * @see PublicIPAddress#getVirtualMachineId()
+       */
+      public T virtualMachineId(String virtualMachineId) {
          this.virtualMachineId = virtualMachineId;
-         return this;
+         return self();
       }
 
-      public Builder virtualMachineName(String virtualMachineName) {
+      /** 
+       * @see PublicIPAddress#getVirtualMachineName()
+       */
+      public T virtualMachineName(String virtualMachineName) {
          this.virtualMachineName = virtualMachineName;
-         return this;
+         return self();
       }
 
-      public Builder VLANId(String VLANId) {
+      /** 
+       * @see PublicIPAddress#getVLANId()
+       */
+      public T VLANId(String VLANId) {
          this.VLANId = VLANId;
-         return this;
+         return self();
       }
 
-      public Builder VLANName(String VLANName) {
+      /** 
+       * @see PublicIPAddress#getVLANName()
+       */
+      public T VLANName(String VLANName) {
          this.VLANName = VLANName;
-         return this;
+         return self();
       }
 
-      public Builder zoneId(String zoneId) {
+      /** 
+       * @see PublicIPAddress#getZoneId()
+       */
+      public T zoneId(String zoneId) {
          this.zoneId = zoneId;
-         return this;
+         return self();
       }
 
-      public Builder zoneName(String zoneName) {
+      /** 
+       * @see PublicIPAddress#getZoneName()
+       */
+      public T zoneName(String zoneName) {
          this.zoneName = zoneName;
-         return this;
+         return self();
       }
 
-      public Builder jobId(String jobId) {
+      /** 
+       * @see PublicIPAddress#getJobId()
+       */
+      public T jobId(String jobId) {
          this.jobId = jobId;
-         return this;
+         return self();
       }
 
-      public Builder jobStatus(int jobStatus) {
+      /** 
+       * @see PublicIPAddress#getJobStatus()
+       */
+      public T jobStatus(Integer jobStatus) {
          this.jobStatus = jobStatus;
-         return this;
+         return self();
       }
 
       public PublicIPAddress build() {
-         return new PublicIPAddress(id, account, allocated, associatedNetworkId, domain, domainId, usesVirtualNetwork,
-               IPAddress, isSourceNAT, isStaticNAT, networkId, state, virtualMachineDisplayName, virtualMachineId,
-               virtualMachineName, VLANId, VLANName, zoneId, zoneName, jobId, jobStatus);
+         return new PublicIPAddress(id, account, allocated, associatedNetworkId, domain, domainId, usesVirtualNetwork, IPAddress, isSourceNAT, isStaticNAT, networkId, state, virtualMachineDisplayName, virtualMachineId, virtualMachineName, VLANId, VLANName, zoneId, zoneName, jobId, jobStatus);
+      }
+      
+      public T fromPublicIPAddress(PublicIPAddress in) {
+         return this
+                  .id(in.getId())
+                  .account(in.getAccount())
+                  .allocated(in.getAllocated())
+                  .associatedNetworkId(in.getAssociatedNetworkId())
+                  .domain(in.getDomain())
+                  .domainId(in.getDomainId())
+                  .usesVirtualNetwork(in.isUsesVirtualNetwork())
+                  .IPAddress(in.getIPAddress())
+                  .isSourceNAT(in.isSourceNAT())
+                  .isStaticNAT(in.isStaticNAT())
+                  .networkId(in.getNetworkId())
+                  .state(in.getState())
+                  .virtualMachineDisplayName(in.getVirtualMachineDisplayName())
+                  .virtualMachineId(in.getVirtualMachineId())
+                  .virtualMachineName(in.getVirtualMachineName())
+                  .VLANId(in.getVLANId())
+                  .VLANName(in.getVLANName())
+                  .zoneId(in.getZoneId())
+                  .zoneName(in.getZoneName())
+                  .jobId(in.getJobId())
+                  .jobStatus(in.getJobStatus());
       }
    }
 
-   private String id;
-   private String account;
-   private Date allocated;
-   @SerializedName("associatednetworkid")
-   private String associatedNetworkId;
-   private String domain;
-   @SerializedName("domainid")
-   private String domainId;
-   @SerializedName("forvirtualnetwork")
-   private boolean usesVirtualNetwork;
-   @SerializedName("ipaddress")
-   private String IPAddress;
-   @SerializedName("issourcenat")
-   private boolean isSourceNAT;
-   @SerializedName("isstaticnat")
-   private boolean isStaticNAT;
-   @SerializedName("networkid")
-   private String networkId;
-   private State state;
-   @SerializedName("virtualmachinedisplayname")
-   private String virtualMachineDisplayName;
-   @SerializedName("virtualmachineid")
-   private String virtualMachineId;
-   @SerializedName("virtualmachinename")
-   private String virtualMachineName;
-   @SerializedName("VLANid")
-   private String VLANId;
-   @SerializedName("VLANname")
-   private String VLANName;
-   @SerializedName("zoneid")
-   private String zoneId;
-   @SerializedName("zonename")
-   private String zoneName;
-   @SerializedName("jobid")
-   @Nullable
-   private String jobId;
-   @SerializedName("jobstatus")
-   @Nullable
-   private Integer jobStatus;
-
-   public static enum State {
-      ALLOCATING, ALLOCATED, RELEASING, UNRECOGNIZED;
-
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
       @Override
-      public String toString() {
-         return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, name());
+      protected ConcreteBuilder self() {
+         return this;
       }
-
-      public static State fromValue(String state) {
-         try {
-            return valueOf(CaseFormat.UPPER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, checkNotNull(state, "state")));
-         } catch (IllegalArgumentException e) {
-            return UNRECOGNIZED;
-         }
-      }
-
    }
 
-   // for serialization
-   PublicIPAddress() {
+   private final String id;
+   private final String account;
+   private final Date allocated;
+   @Named("associatednetworkid")
+   private final String associatedNetworkId;
+   private final String domain;
+   @Named("domainid")
+   private final String domainId;
+   @Named("forvirtualnetwork")
+   private final boolean usesVirtualNetwork;
+   @Named("ipaddress")
+   private final String IPAddress;
+   @Named("issourcenat")
+   private final boolean isSourceNAT;
+   @Named("isstaticnat")
+   private final boolean isStaticNAT;
+   @Named("networkid")
+   private final String networkId;
+   private final PublicIPAddress.State state;
+   @Named("virtualmachinedisplayname")
+   private final String virtualMachineDisplayName;
+   @Named("virtualmachineid")
+   private final String virtualMachineId;
+   @Named("virtualmachinename")
+   private final String virtualMachineName;
+   @Named("VLANid")
+   private final String VLANId;
+   @Named("VLANname")
+   private final String VLANName;
+   @Named("zoneid")
+   private final String zoneId;
+   @Named("zonename")
+   private final String zoneName;
+   @Named("jobid")
+   private final String jobId;
+   @Named("jobstatus")
+   private final Integer jobStatus;
 
-   }
-
-   public PublicIPAddress(String id, String account, Date allocated, String associatedNetworkId, String domain,
-                          String domainId, boolean usesVirtualNetwork, String iPAddress, boolean isSourceNAT, boolean isStaticNAT,
-                          String networkId, State state, String virtualMachineDisplayName, String virtualMachineId,
-                          String virtualMachineName, String VLANId, String VLANName, String zoneId, String zoneName, String jobId, 
-                          Integer jobStatus) {
-      this.id = id;
+   @ConstructorProperties({
+      "id", "account", "allocated", "associatednetworkid", "domain", "domainid", "forvirtualnetwork", "ipaddress", "issourcenat",
+         "isstaticnat", "networkid", "state", "virtualmachinedisplayname", "virtualmachineid", "virtualmachinename", "VLANid",
+         "VLANname", "zoneid", "zonename", "jobid", "jobstatus"
+   })
+   protected PublicIPAddress(String id, @Nullable String account, @Nullable Date allocated, @Nullable String associatedNetworkId,
+                             @Nullable String domain, @Nullable String domainId, boolean usesVirtualNetwork, @Nullable String IPAddress,
+                             boolean isSourceNAT, boolean isStaticNAT, @Nullable String networkId, @Nullable PublicIPAddress.State state,
+                             @Nullable String virtualMachineDisplayName, @Nullable String virtualMachineId, @Nullable String virtualMachineName,
+                             @Nullable String VLANId, @Nullable String VLANName, @Nullable String zoneId, @Nullable String zoneName,
+                             @Nullable String jobId, @Nullable Integer jobStatus) {
+      this.id = checkNotNull(id, "id");
       this.account = account;
       this.allocated = allocated;
       this.associatedNetworkId = associatedNetworkId;
       this.domain = domain;
       this.domainId = domainId;
       this.usesVirtualNetwork = usesVirtualNetwork;
-      this.IPAddress = iPAddress;
+      this.IPAddress = IPAddress;
       this.isSourceNAT = isSourceNAT;
       this.isStaticNAT = isStaticNAT;
       this.networkId = networkId;
@@ -261,43 +366,168 @@
       this.zoneName = zoneName;
       this.jobId = jobId;
       this.jobStatus = jobStatus;
-
-   }
-
-   @Override
-   public int compareTo(PublicIPAddress arg0) {
-      return id.compareTo(arg0.getId());
    }
 
    /**
     * @return public IP address id
     */
    public String getId() {
-      return id;
+      return this.id;
    }
 
    /**
     * @return the account the public IP address is associated with
     */
+   @Nullable
    public String getAccount() {
-      return account;
+      return this.account;
    }
 
    /**
     * @return date the public IP address was acquired
     */
+   @Nullable
    public Date getAllocated() {
-      return allocated;
+      return this.allocated;
+   }
+
+   /**
+    * @return the ID of the Network associated with the IP address
+    */
+   @Nullable
+   public String getAssociatedNetworkId() {
+      return this.associatedNetworkId;
+   }
+
+   /**
+    * @return the domain the public IP address is associated with
+    */
+   @Nullable
+   public String getDomain() {
+      return this.domain;
+   }
+
+   /**
+    * @return the domain ID the public IP address is associated with
+    */
+   @Nullable
+   public String getDomainId() {
+      return this.domainId;
+   }
+
+   /**
+    * @return uses virtual network
+    */
+   public boolean isUsesVirtualNetwork() {
+      return this.usesVirtualNetwork;
+   }
+
+   /**
+    * @return public IP address
+    */
+   @Nullable
+   public String getIPAddress() {
+      return this.IPAddress;
+   }
+
+   /**
+    * @return true if the IP address is a source nat address, false otherwise
+    */
+   public boolean isSourceNAT() {
+      return this.isSourceNAT;
+   }
+
+   /**
+    * @return true if this ip is for static nat, false otherwise
+    */
+   public boolean isStaticNAT() {
+      return this.isStaticNAT;
+   }
+
+   /**
+    * @return the ID of the Network where ip belongs to
+    */
+   @Nullable
+   public String getNetworkId() {
+      return this.networkId;
+   }
+
+   /**
+    * @return State of the ip address. Can be: Allocating, Allocated and
+         Releasing
+    */
+   @Nullable
+   public PublicIPAddress.State getState() {
+      return this.state;
+   }
+
+   /**
+    * @return virtual machine display name the ip address is assigned to (not
+         null only for static nat Ip)
+    */
+   @Nullable
+   public String getVirtualMachineDisplayName() {
+      return this.virtualMachineDisplayName;
+   }
+
+   /**
+    * @return virtual machine id the ip address is assigned to (not null only
+         for static nat Ip)
+    */
+   @Nullable
+   public String getVirtualMachineId() {
+      return this.virtualMachineId;
+   }
+
+   /**
+    * @return virtual machine name the ip address is assigned to (not null only
+         for static nat Ip)
+    */
+   @Nullable
+   public String getVirtualMachineName() {
+      return this.virtualMachineName;
+   }
+
+   /**
+    * @return the ID of the VLAN associated with the IP address
+    */
+   @Nullable
+   public String getVLANId() {
+      return this.VLANId;
+   }
+
+   /**
+    * @return the VLAN associated with the IP address
+    */
+   @Nullable
+   public String getVLANName() {
+      return this.VLANName;
+   }
+
+   /**
+    * @return the ID of the zone the public IP address belongs to
+    */
+   @Nullable
+   public String getZoneId() {
+      return this.zoneId;
+   }
+
+   /**
+    * @return the name of the zone the public IP address belongs to
+    */
+   @Nullable
+   public String getZoneName() {
+      return this.zoneName;
    }
 
    /**
     * @return shows the current pending asynchronous job ID. This tag is not
-    *         returned if no current pending jobs are acting on the virtual
-    *         machine
+         returned if no current pending jobs are acting on the virtual
+         machine
     */
    @Nullable
    public String getJobId() {
-      return jobId;
+      return this.jobId;
    }
 
    /**
@@ -305,190 +535,55 @@
     */
    @Nullable
    public Integer getJobStatus() {
-      return jobStatus;
-   }
-
-
-   /**
-    * @return the ID of the Network associated with the IP address
-    */
-   public String getAssociatedNetworkId() {
-      return associatedNetworkId;
-   }
-
-   /**
-    * @return the domain the public IP address is associated with
-    */
-   public String getDomain() {
-      return domain;
-   }
-
-   /**
-    * @return the domain ID the public IP address is associated with
-    */
-   public String getDomainId() {
-      return domainId;
-   }
-
-   /**
-    * @return uses virtual network
-    */
-   public boolean usesVirtualNetwork() {
-      return usesVirtualNetwork;
-   }
-
-   /**
-    * @return public IP address
-    */
-   public String getIPAddress() {
-      return IPAddress;
-   }
-
-   /**
-    * @return true if the IP address is a source nat address, false otherwise
-    */
-   public boolean isSourceNAT() {
-      return isSourceNAT;
-   }
-
-   /**
-    * @return true if this ip is for static nat, false otherwise
-    */
-   public boolean isStaticNAT() {
-      return isStaticNAT;
-   }
-
-   /**
-    * @return the ID of the Network where ip belongs to
-    */
-   public String getNetworkId() {
-      return networkId;
-   }
-
-   /**
-    * @return State of the ip address. Can be: Allocating, Allocated and
-    *         Releasing
-    */
-   public State getState() {
-      return state;
-   }
-
-   /**
-    * @return virtual machine display name the ip address is assigned to (not
-    *         null only for static nat Ip)
-    */
-   public String getVirtualMachineDisplayName() {
-      return virtualMachineDisplayName;
-   }
-
-   /**
-    * @return virtual machine id the ip address is assigned to (not null only
-    *         for static nat Ip)
-    */
-   public String getVirtualMachineId() {
-      return virtualMachineId;
-   }
-
-   /**
-    * @return virtual machine name the ip address is assigned to (not null only
-    *         for static nat Ip)
-    */
-   public String getVirtualMachineName() {
-      return virtualMachineName;
-   }
-
-   /**
-    * @return the ID of the VLAN associated with the IP address
-    */
-   public String getVLANId() {
-      return VLANId;
-   }
-
-   /**
-    * @return the VLAN associated with the IP address
-    */
-   public String getVLANName() {
-      return VLANName;
-   }
-
-   /**
-    * @return the ID of the zone the public IP address belongs to
-    */
-   public String getZoneId() {
-      return zoneId;
-   }
-
-   /**
-    * @return the name of the zone the public IP address belongs to
-    */
-   public String getZoneName() {
-      return zoneName;
-   }
-
-   @Override
-   public boolean equals(Object o) {
-      if (this == o) return true;
-      if (o == null || getClass() != o.getClass()) return false;
-
-      PublicIPAddress that = (PublicIPAddress) o;
-
-      if (!Objects.equal(IPAddress, that.IPAddress)) return false;
-      if (!Objects.equal(VLANId, that.VLANId)) return false;
-      if (!Objects.equal(VLANName, that.VLANName)) return false;
-      if (!Objects.equal(account, that.account)) return false;
-      if (!Objects.equal(allocated, that.allocated)) return false;
-      if (!Objects.equal(associatedNetworkId, that.associatedNetworkId)) return false;
-      if (!Objects.equal(domain, that.domain)) return false;
-      if (!Objects.equal(domainId, that.domainId)) return false;
-      if (!Objects.equal(id, that.id)) return false;
-      if (!Objects.equal(isSourceNAT, that.isSourceNAT)) return false;
-      if (!Objects.equal(isStaticNAT, that.isStaticNAT)) return false;
-      if (!Objects.equal(networkId, that.networkId)) return false;
-      if (!Objects.equal(state, that.state)) return false;
-      if (!Objects.equal(usesVirtualNetwork, that.usesVirtualNetwork)) return false;
-      if (!Objects.equal(virtualMachineDisplayName, that.virtualMachineDisplayName)) return false;
-      if (!Objects.equal(virtualMachineId, that.virtualMachineId)) return false;
-      if (!Objects.equal(virtualMachineName, that.virtualMachineName)) return false;
-      if (!Objects.equal(zoneId, that.zoneId)) return false;
-      if (!Objects.equal(zoneName, that.zoneName)) return false;
-      if (!Objects.equal(jobStatus, that.jobStatus)) return false;
-
-      return true;
+      return this.jobStatus;
    }
 
    @Override
    public int hashCode() {
-       return Objects.hashCode(IPAddress, VLANId, VLANName, account, allocated, associatedNetworkId,
-                               domain, domainId, id, isSourceNAT, isStaticNAT, networkId, state,
-                               usesVirtualNetwork, virtualMachineDisplayName, virtualMachineId,
-                               virtualMachineName, zoneId, zoneName, jobStatus);
+      return Objects.hashCode(id, account, allocated, associatedNetworkId, domain, domainId, usesVirtualNetwork, IPAddress, isSourceNAT, isStaticNAT, networkId, state, virtualMachineDisplayName, virtualMachineId, virtualMachineName, VLANId, VLANName, zoneId, zoneName, jobId, jobStatus);
    }
 
    @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      PublicIPAddress that = PublicIPAddress.class.cast(obj);
+      return Objects.equal(this.id, that.id)
+               && Objects.equal(this.account, that.account)
+               && Objects.equal(this.allocated, that.allocated)
+               && Objects.equal(this.associatedNetworkId, that.associatedNetworkId)
+               && Objects.equal(this.domain, that.domain)
+               && Objects.equal(this.domainId, that.domainId)
+               && Objects.equal(this.usesVirtualNetwork, that.usesVirtualNetwork)
+               && Objects.equal(this.IPAddress, that.IPAddress)
+               && Objects.equal(this.isSourceNAT, that.isSourceNAT)
+               && Objects.equal(this.isStaticNAT, that.isStaticNAT)
+               && Objects.equal(this.networkId, that.networkId)
+               && Objects.equal(this.state, that.state)
+               && Objects.equal(this.virtualMachineDisplayName, that.virtualMachineDisplayName)
+               && Objects.equal(this.virtualMachineId, that.virtualMachineId)
+               && Objects.equal(this.virtualMachineName, that.virtualMachineName)
+               && Objects.equal(this.VLANId, that.VLANId)
+               && Objects.equal(this.VLANName, that.VLANName)
+               && Objects.equal(this.zoneId, that.zoneId)
+               && Objects.equal(this.zoneName, that.zoneName)
+               && Objects.equal(this.jobId, that.jobId)
+               && Objects.equal(this.jobStatus, that.jobStatus);
+   }
+   
+   protected ToStringHelper string() {
+      return Objects.toStringHelper(this)
+            .add("id", id).add("account", account).add("allocated", allocated).add("associatedNetworkId", associatedNetworkId)
+            .add("domain", domain).add("domainId", domainId).add("usesVirtualNetwork", usesVirtualNetwork).add("IPAddress", IPAddress)
+            .add("isSourceNAT", isSourceNAT).add("isStaticNAT", isStaticNAT).add("networkId", networkId).add("state", state)
+            .add("virtualMachineDisplayName", virtualMachineDisplayName).add("virtualMachineId", virtualMachineId)
+            .add("virtualMachineName", virtualMachineName).add("VLANId", VLANId).add("VLANName", VLANName).add("zoneId", zoneId)
+            .add("zoneName", zoneName).add("jobId", jobId).add("jobStatus", jobStatus);
+   }
+   
+   @Override
    public String toString() {
-      return "PublicIPAddress{" +
-            "id=" + id +
-            ", account='" + account + '\'' +
-            ", allocated=" + allocated +
-            ", associatedNetworkId=" + associatedNetworkId +
-            ", domain='" + domain + '\'' +
-            ", domainId=" + domainId +
-            ", usesVirtualNetwork=" + usesVirtualNetwork +
-            ", IPAddress='" + IPAddress + '\'' +
-            ", isSourceNAT=" + isSourceNAT +
-            ", isStaticNAT=" + isStaticNAT +
-            ", networkId=" + networkId +
-            ", state=" + state +
-            ", virtualMachineDisplayName='" + virtualMachineDisplayName + '\'' +
-            ", virtualMachineId=" + virtualMachineId +
-            ", virtualMachineName='" + virtualMachineName + '\'' +
-            ", VLANId=" + VLANId +
-            ", VLANName='" + VLANName + '\'' +
-            ", zoneId=" + zoneId +
-            ", zoneName='" + zoneName + '\'' +
-            ", jobId=" + jobId +
-            ", jobStatus=" + jobStatus +
-            '}';
+      return string().toString();
    }
 
 }
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/ResourceLimit.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/ResourceLimit.java
index f2e6aa8..300dcc4 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/ResourceLimit.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/ResourceLimit.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -16,143 +16,29 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
 package org.jclouds.cloudstack.domain;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import java.beans.ConstructorProperties;
 import java.util.Map;
 
+import javax.inject.Named;
+
+import org.jclouds.javax.annotation.Nullable;
+
 import com.google.common.base.Function;
 import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Maps;
-import com.google.gson.annotations.SerializedName;
 
 /**
+ * Class ResourceLimit
+ * 
  * @author Vijay Kiran
- */
-public class ResourceLimit implements Comparable<ResourceLimit> {
-
-   public static Builder builder() {
-      return new Builder();
-   }
-
-   public static class Builder {
-
-      private String account;
-      private String domain;
-      private String domainId;
-      private int max;
-      private ResourceType resourceType;
-
-      public Builder account(String account) {
-         this.account = account;
-         return this;
-      }
-
-      public Builder domain(String domain) {
-         this.domain = domain;
-         return this;
-      }
-
-      public Builder domainId(String domainId) {
-         this.domainId = domainId;
-         return this;
-      }
-
-      public Builder max(int max) {
-         this.max = max;
-         return this;
-      }
-
-      public Builder resourceType(ResourceType resourceType) {
-         this.resourceType = resourceType;
-         return this;
-      }
-
-      public ResourceLimit build() {
-         return new ResourceLimit(account, domain, domainId, max, resourceType);
-      }
-   }
-
-   // for deserialization
-   ResourceLimit() {
-
-   }
-
-   private String account;
-   private String domain;
-   @SerializedName("domainid")
-   private String domainId;
-   private int max;
-   @SerializedName("resourcetype")
-   private ResourceType resourceType;
-
-   public ResourceLimit(String account, String domain, String domainId, int max, ResourceType resourceType) {
-      this.account = account;
-      this.domain = domain;
-      this.domainId = domainId;
-      this.max = max;
-      this.resourceType = resourceType;
-   }
-
-   public String getAccount() {
-      return account;
-   }
-
-   public String getDomain() {
-      return domain;
-   }
-
-   public String getDomainId() {
-      return domainId;
-   }
-
-   public int getMax() {
-      return max;
-   }
-
-   public ResourceType getResourceType() {
-      return resourceType;
-   }
-
-   @Override
-   public boolean equals(Object o) {
-      if (this == o) return true;
-      if (o == null || getClass() != o.getClass()) return false;
-
-      ResourceLimit that = (ResourceLimit) o;
-
-      if (!Objects.equal(domainId, that.domainId)) return false;
-      if (!Objects.equal(max, that.max)) return false;
-      if (!Objects.equal(resourceType, that.resourceType)) return false;
-      if (!Objects.equal(account, that.account)) return false;
-      if (!Objects.equal(domain, that.domain)) return false;
-
-      return true;
-   }
-
-   @Override
-   public int hashCode() {
-       return Objects.hashCode(domainId, max, resourceType, account, domain);
-   }
-
-   @Override
-   public int compareTo(ResourceLimit that) {
-      return this.account.compareTo(that.account);
-   }
-
-   @Override
-   public String toString() {
-      return "ResourceLimit{" +
-            "account='" + account + '\'' +
-            ", domain='" + domain + '\'' +
-            ", domainId=" + domainId +
-            ", max=" + max +
-            ", resourceType=" + resourceType +
-            '}';
-   }
+*/
+public class ResourceLimit {
 
    /**
     * Type of resource to update.
@@ -200,7 +86,7 @@
       public int getCode(){
          return code;
       }
-      
+
       @Override
       public String toString() {
          return name();
@@ -210,6 +96,155 @@
          Integer code = new Integer(checkNotNull(resourceType, "resourcetype"));
          return INDEX.containsKey(code) ? INDEX.get(code) : UNRECOGNIZED;
       }
-
    }
+
+   public static Builder<?> builder() { 
+      return new ConcreteBuilder();
+   }
+   
+   public Builder<?> toBuilder() { 
+      return new ConcreteBuilder().fromResourceLimit(this);
+   }
+
+   public static abstract class Builder<T extends Builder<T>>  {
+      protected abstract T self();
+
+      protected String account;
+      protected String domain;
+      protected String domainId;
+      protected int max;
+      protected ResourceType resourceType;
+   
+      /** 
+       * @see ResourceLimit#getAccount()
+       */
+      public T account(String account) {
+         this.account = account;
+         return self();
+      }
+
+      /** 
+       * @see ResourceLimit#getDomain()
+       */
+      public T domain(String domain) {
+         this.domain = domain;
+         return self();
+      }
+
+      /** 
+       * @see ResourceLimit#getDomainId()
+       */
+      public T domainId(String domainId) {
+         this.domainId = domainId;
+         return self();
+      }
+
+      /** 
+       * @see ResourceLimit#getMax()
+       */
+      public T max(int max) {
+         this.max = max;
+         return self();
+      }
+
+      /** 
+       * @see ResourceLimit#getResourceType()
+       */
+      public T resourceType(ResourceType resourceType) {
+         this.resourceType = resourceType;
+         return self();
+      }
+
+      public ResourceLimit build() {
+         return new ResourceLimit(account, domain, domainId, max, resourceType);
+      }
+      
+      public T fromResourceLimit(ResourceLimit in) {
+         return this
+                  .account(in.getAccount())
+                  .domain(in.getDomain())
+                  .domainId(in.getDomainId())
+                  .max(in.getMax())
+                  .resourceType(in.getResourceType());
+      }
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
+   }
+
+   private final String account;
+   private final String domain;
+   @Named("domainid")
+   private final String domainId;
+   private final int max;
+   @Named("resourcetype")
+   private final ResourceLimit.ResourceType resourceType;
+
+   @ConstructorProperties({
+      "account", "domain", "domainid", "max", "resourcetype"
+   })
+   protected ResourceLimit(@Nullable String account, @Nullable String domain, @Nullable String domainId, int max,
+                           @Nullable ResourceType resourceType) {
+      this.account = account;
+      this.domain = domain;
+      this.domainId = domainId;
+      this.max = max;
+      this.resourceType = resourceType;
+   }
+
+   @Nullable
+   public String getAccount() {
+      return this.account;
+   }
+
+   @Nullable
+   public String getDomain() {
+      return this.domain;
+   }
+
+   @Nullable
+   public String getDomainId() {
+      return this.domainId;
+   }
+
+   public int getMax() {
+      return this.max;
+   }
+
+   @Nullable
+   public ResourceType getResourceType() {
+      return this.resourceType;
+   }
+
+   @Override
+   public int hashCode() {
+      return Objects.hashCode(account, domain, domainId, max, resourceType);
+   }
+
+   @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      ResourceLimit that = ResourceLimit.class.cast(obj);
+      return Objects.equal(this.account, that.account)
+               && Objects.equal(this.domain, that.domain)
+               && Objects.equal(this.domainId, that.domainId)
+               && Objects.equal(this.max, that.max)
+               && Objects.equal(this.resourceType, that.resourceType);
+   }
+   
+   protected ToStringHelper string() {
+      return Objects.toStringHelper(this)
+            .add("account", account).add("domain", domain).add("domainId", domainId).add("max", max).add("resourceType", resourceType);
+   }
+   
+   @Override
+   public String toString() {
+      return string().toString();
+   }
+
 }
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/SecurityGroup.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/SecurityGroup.java
index 88232c2..2076a01 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/SecurityGroup.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/SecurityGroup.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -20,106 +20,164 @@
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import java.beans.ConstructorProperties;
 import java.util.Set;
-import java.util.SortedSet;
 
-import javax.annotation.Nullable;
+import javax.inject.Named;
+
+import org.jclouds.javax.annotation.Nullable;
 
 import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.ImmutableSortedSet;
-import com.google.gson.annotations.SerializedName;
 
 /**
+ * Class SecurityGroup
+ * 
  * @author Adrian Cole
- */
+*/
 public class SecurityGroup implements Comparable<SecurityGroup> {
-   public static Builder builder() {
-      return new Builder();
+
+   public static Builder<?> builder() { 
+      return new ConcreteBuilder();
+   }
+   
+   public Builder<?> toBuilder() { 
+      return new ConcreteBuilder().fromSecurityGroup(this);
    }
 
-   public static class Builder {
-      private String id;
-      private String account;
-      private String name;
-      private String description;
-      private String domain;
-      private String domainId;
-      private String jobId;
-      private Integer jobStatus;
+   public static abstract class Builder<T extends Builder<T>>  {
+      protected abstract T self();
 
-      private Set<IngressRule> ingressRules = ImmutableSet.of();
-
-      public Builder id(String id) {
+      protected String id;
+      protected String account;
+      protected String name;
+      protected String description;
+      protected String domain;
+      protected String domainId;
+      protected String jobId;
+      protected Integer jobStatus;
+      protected Set<IngressRule> ingressRules;
+   
+      /** 
+       * @see SecurityGroup#getId()
+       */
+      public T id(String id) {
          this.id = id;
-         return this;
+         return self();
       }
 
-      public Builder account(String account) {
+      /** 
+       * @see SecurityGroup#getAccount()
+       */
+      public T account(String account) {
          this.account = account;
-         return this;
+         return self();
       }
 
-      public Builder name(String name) {
+      /** 
+       * @see SecurityGroup#getName()
+       */
+      public T name(String name) {
          this.name = name;
-         return this;
+         return self();
       }
 
-      public Builder description(String description) {
+      /** 
+       * @see SecurityGroup#getDescription()
+       */
+      public T description(String description) {
          this.description = description;
-         return this;
+         return self();
       }
 
-      public Builder domain(String domain) {
+      /** 
+       * @see SecurityGroup#getDomain()
+       */
+      public T domain(String domain) {
          this.domain = domain;
-         return this;
+         return self();
       }
 
-      public Builder domainId(String domainId) {
+      /** 
+       * @see SecurityGroup#getDomainId()
+       */
+      public T domainId(String domainId) {
          this.domainId = domainId;
-         return this;
+         return self();
       }
 
-      public Builder jobId(String jobId) {
+      /** 
+       * @see SecurityGroup#getJobId()
+       */
+      public T jobId(String jobId) {
          this.jobId = jobId;
-         return this;
+         return self();
       }
 
-      public Builder jobStatus(int jobStatus) {
+      /** 
+       * @see SecurityGroup#getJobStatus()
+       */
+      public T jobStatus(Integer jobStatus) {
          this.jobStatus = jobStatus;
-         return this;
+         return self();
       }
 
-      public Builder ingressRules(Set<IngressRule> ingressRules) {
-         this.ingressRules = ImmutableSet.copyOf(checkNotNull(ingressRules, "ingressRules"));
-         return this;
+      /** 
+       * @see SecurityGroup#getIngressRules()
+       */
+      public T ingressRules(Set<IngressRule> ingressRules) {
+         this.ingressRules = ingressRules;
+         return self();
       }
 
       public SecurityGroup build() {
          return new SecurityGroup(id, account, name, description, domain, domainId, jobId, jobStatus, ingressRules);
       }
+      
+      public T fromSecurityGroup(SecurityGroup in) {
+         return this
+                  .id(in.getId())
+                  .account(in.getAccount())
+                  .name(in.getName())
+                  .description(in.getDescription())
+                  .domain(in.getDomain())
+                  .domainId(in.getDomainId())
+                  .jobId(in.getJobId())
+                  .jobStatus(in.getJobStatus())
+                  .ingressRules(in.getIngressRules());
+      }
    }
 
-   private String id;
-   private String account;
-   private String name;
-   private String description;
-   private String domain;
-   @SerializedName("domainid")
-   private String domainId;
-   @SerializedName("jobid")
-   @Nullable
-   private String jobId;
-   @SerializedName("jobstatus")
-   @Nullable
-   private Integer jobStatus;
-   @SerializedName("ingressrule")
-   // so that tests and serialization come out expected
-   private SortedSet<IngressRule> ingressRules = ImmutableSortedSet.<IngressRule>of();
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
+   }
 
-   public SecurityGroup(String id, String account, String name, String description, String domain, String domainId,
-                        String jobId, Integer jobStatus, Set<IngressRule> ingressRules) {
-      this.id = id;
+   private final String id;
+   private final String account;
+   private final String name;
+   private final String description;
+   private final String domain;
+   @Named("domainid")
+   private final String domainId;
+   @Named("jobid")
+   private final String jobId;
+   @Named("jobstatus")
+   private final Integer jobStatus;
+   @Named("ingressrule")
+   private final Set<IngressRule> ingressRules;
+
+   @ConstructorProperties({
+      "id", "account", "name", "description", "domain", "domainid", "jobid", "jobstatus", "ingressrule"
+   })
+   protected SecurityGroup(String id, @Nullable String account, @Nullable String name, @Nullable String description,
+                           @Nullable String domain, @Nullable String domainId, @Nullable String jobId, @Nullable Integer jobStatus,
+                           @Nullable Set<IngressRule> ingressRules) {
+      this.id = checkNotNull(id, "id");
       this.account = account;
       this.name = name;
       this.description = description;
@@ -127,60 +185,64 @@
       this.domainId = domainId;
       this.jobId = jobId;
       this.jobStatus = jobStatus;
-      this.ingressRules = ImmutableSortedSet.copyOf(checkNotNull(ingressRules, "ingressRules"));
-   }
-
-   /**
-    * present only for serializer
-    */
-   SecurityGroup() {
-
+      this.ingressRules = ingressRules == null ? ImmutableSet.<IngressRule>of() : ImmutableSortedSet.copyOf(ingressRules);
    }
 
    /**
     * @return the id of the security group
     */
    public String getId() {
-      return id;
+      return this.id;
+   }
+
+   /**
+    * @return the account owning the security group
+    */
+   @Nullable
+   public String getAccount() {
+      return this.account;
    }
 
    /**
     * @return the name of the security group
     */
-
+   @Nullable
    public String getName() {
-      return name;
+      return this.name;
    }
 
    /**
     * @return an alternate display text of the security group.
     */
+   @Nullable
    public String getDescription() {
-      return description;
+      return this.description;
    }
 
    /**
     * @return Domain name for the security group
     */
+   @Nullable
    public String getDomain() {
-      return domain;
+      return this.domain;
    }
 
    /**
     * @return the domain id of the security group
     */
+   @Nullable
    public String getDomainId() {
-      return domainId;
+      return this.domainId;
    }
 
    /**
     * @return shows the current pending asynchronous job ID. This tag is not
-    *         returned if no current pending jobs are acting on the virtual
-    *         machine
+         returned if no current pending jobs are acting on the virtual
+         machine
     */
    @Nullable
    public String getJobId() {
-      return jobId;
+      return this.jobId;
    }
 
    /**
@@ -188,59 +250,49 @@
     */
    @Nullable
    public Integer getJobStatus() {
-      return jobStatus;
-   }
-
-   /**
-    * @return the account owning the security group
-    */
-   public String getAccount() {
-      return account;
+      return this.jobStatus;
    }
 
    /**
     * @return the list of ingress rules associated with the security group
     */
    public Set<IngressRule> getIngressRules() {
-      return ingressRules;
-   }
-
-   @Override
-   public boolean equals(Object o) {
-      if (this == o) return true;
-      if (o == null || getClass() != o.getClass()) return false;
-
-      SecurityGroup that = (SecurityGroup) o;
-
-      if (!Objects.equal(domainId, that.domainId)) return false;
-      if (!Objects.equal(id, that.id)) return false;
-      if (!Objects.equal(jobStatus, that.jobStatus)) return false;
-
-      return true;
+      return this.ingressRules;
    }
 
    @Override
    public int hashCode() {
-       return Objects.hashCode(domainId, id, jobStatus);
+      return Objects.hashCode(id, account, name, description, domain, domainId, jobId, jobStatus, ingressRules);
    }
 
    @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      SecurityGroup that = SecurityGroup.class.cast(obj);
+      return Objects.equal(this.id, that.id)
+               && Objects.equal(this.account, that.account)
+               && Objects.equal(this.name, that.name)
+               && Objects.equal(this.description, that.description)
+               && Objects.equal(this.domain, that.domain)
+               && Objects.equal(this.domainId, that.domainId)
+               && Objects.equal(this.jobId, that.jobId)
+               && Objects.equal(this.jobStatus, that.jobStatus)
+               && Objects.equal(this.ingressRules, that.ingressRules);
+   }
+   
+   protected ToStringHelper string() {
+      return Objects.toStringHelper(this).add("id", id).add("account", account).add("name", name).add("description", description)
+            .add("domain", domain).add("domainId", domainId).add("jobId", jobId).add("jobStatus", jobStatus).add("ingressRules", ingressRules);
+   }
+   
+   @Override
    public String toString() {
-      return "SecurityGroup{" +
-            "id=" + id +
-            ", account='" + account + '\'' +
-            ", name='" + name + '\'' +
-            ", description='" + description + '\'' +
-            ", domain='" + domain + '\'' +
-            ", domainId=" + domainId +
-            ", jobId=" + jobId +
-            ", jobStatus=" + jobStatus +
-            ", ingressRules=" + ingressRules +
-            '}';
+      return string().toString();
    }
 
    @Override
-   public int compareTo(SecurityGroup arg0) {
-      return id.compareTo(arg0.getId());
+   public int compareTo(SecurityGroup o) {
+      return id.compareTo(o.getId());
    }
 }
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/ServiceOffering.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/ServiceOffering.java
index 8fc731f..b192cf9 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/ServiceOffering.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/ServiceOffering.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -20,180 +20,275 @@
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import java.beans.ConstructorProperties;
 import java.util.Date;
 import java.util.Set;
 
+import javax.inject.Named;
+
+import org.jclouds.javax.annotation.Nullable;
+
 import com.google.common.base.Joiner;
 import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
 import com.google.common.base.Splitter;
 import com.google.common.collect.ImmutableSet;
-import com.google.gson.annotations.SerializedName;
 
 /**
+ * Class ServiceOffering
+ *
  * @author Adrian Cole
  */
 public class ServiceOffering implements Comparable<ServiceOffering> {
-   public static Builder builder() {
-      return new Builder();
+
+   public static Builder<?> builder() {
+      return new ConcreteBuilder();
    }
 
-   public static class Builder {
+   public Builder<?> toBuilder() {
+      return new ConcreteBuilder().fromServiceOffering(this);
+   }
 
-      private String id;
-      private String name;
-      private String displayText;
-      private Date created;
-      private String domain;
-      private String domainId;
-      private int cpuNumber;
-      private int cpuSpeed;
-      private int memory;
-      private boolean haSupport;
-      private StorageType storageType;
-      private boolean defaultUse;
-      private String hostTags;
-      private boolean systemOffering;
-      private boolean cpuUseLimited;
-      private long networkRate;
-      private boolean systemVmType;
+   public static abstract class Builder<T extends Builder<T>> {
+      protected abstract T self();
 
+      protected String id;
+      protected String name;
+      protected String displayText;
+      protected Date created;
+      protected String domain;
+      protected String domainId;
+      protected int cpuNumber;
+      protected int cpuSpeed;
+      protected int memory;
+      protected boolean haSupport;
+      protected StorageType storageType;
+      protected boolean defaultUse;
+      protected String hostTags;
+      protected boolean systemOffering;
+      protected boolean cpuUseLimited;
+      protected long networkRate;
+      protected boolean systemVmType;
       private Set<String> tags = ImmutableSet.of();
 
-      public Builder id(String id) {
+      /**
+       * @see ServiceOffering#getId()
+       */
+      public T id(String id) {
          this.id = id;
-         return this;
+         return self();
       }
 
-      public Builder name(String name) {
+      /**
+       * @see ServiceOffering#getName()
+       */
+      public T name(String name) {
          this.name = name;
-         return this;
+         return self();
       }
 
-      public Builder displayText(String displayText) {
+      /**
+       * @see ServiceOffering#getDisplayText()
+       */
+      public T displayText(String displayText) {
          this.displayText = displayText;
-         return this;
+         return self();
       }
 
-      public Builder created(Date created) {
+      /**
+       * @see ServiceOffering#getCreated()
+       */
+      public T created(Date created) {
          this.created = created;
-         return this;
+         return self();
       }
 
-      public Builder domain(String domain) {
+      /**
+       * @see ServiceOffering#getDomain()
+       */
+      public T domain(String domain) {
          this.domain = domain;
-         return this;
+         return self();
       }
 
-      public Builder domainId(String domainId) {
+      /**
+       * @see ServiceOffering#getDomainId()
+       */
+      public T domainId(String domainId) {
          this.domainId = domainId;
-         return this;
+         return self();
       }
 
-      public Builder cpuNumber(int cpuNumber) {
+      /**
+       * @see ServiceOffering#getCpuNumber()
+       */
+      public T cpuNumber(int cpuNumber) {
          this.cpuNumber = cpuNumber;
-         return this;
+         return self();
       }
 
-      public Builder cpuSpeed(int cpuSpeed) {
+      /**
+       * @see ServiceOffering#getCpuSpeed()
+       */
+      public T cpuSpeed(int cpuSpeed) {
          this.cpuSpeed = cpuSpeed;
-         return this;
+         return self();
       }
 
-      public Builder memory(int memory) {
+      /**
+       * @see ServiceOffering#getMemory()
+       */
+      public T memory(int memory) {
          this.memory = memory;
-         return this;
+         return self();
       }
 
-      public Builder haSupport(boolean haSupport) {
+      /**
+       * @see ServiceOffering#supportsHA()
+       */
+      public T supportsHA(boolean haSupport) {
          this.haSupport = haSupport;
-         return this;
+         return self();
       }
 
-      public Builder storageType(StorageType storageType) {
+      /**
+       * @see ServiceOffering#getStorageType()
+       */
+      public T storageType(StorageType storageType) {
          this.storageType = storageType;
-         return this;
+         return self();
       }
 
-      public Builder tags(Set<String> tags) {
+      /**
+       * @see ServiceOffering#getTags()
+       */
+      public T tags(Set<String> tags) {
          this.tags = ImmutableSet.copyOf(checkNotNull(tags, "tags"));
-         return this;
+         return self();
       }
 
-      public Builder defaultUse(boolean defaultUse) {
+      /**
+       * @see ServiceOffering#isDefaultUse()
+       */
+      public T defaultUse(boolean defaultUse) {
          this.defaultUse = defaultUse;
-         return this;
+         return self();
       }
 
-      public Builder hostTags(String hostTags) {
+      /**
+       * @see ServiceOffering#getHostTags()
+       */
+      public T hostTags(String hostTags) {
          this.hostTags = hostTags;
-         return this;
+         return self();
       }
 
-      public Builder systemOffering(boolean systemOffering) {
+      /**
+       * @see ServiceOffering#isSystemOffering()
+       */
+      public T systemOffering(boolean systemOffering) {
          this.systemOffering = systemOffering;
-         return this;
+         return self();
       }
 
-      public Builder cpuUseLimited(boolean cpuUseLimited) {
+      /**
+       * @see ServiceOffering#isCpuUseLimited()
+       */
+      public T cpuUseLimited(boolean cpuUseLimited) {
          this.cpuUseLimited = cpuUseLimited;
-         return this;
+         return self();
       }
 
-      public Builder networkRate(long networkRate) {
+      /**
+       * @see ServiceOffering#getNetworkRate()
+       */
+      public T networkRate(long networkRate) {
          this.networkRate = networkRate;
-         return this;
+         return self();
       }
 
-      public Builder systemVmType(boolean systemVmType) {
+      /**
+       * @see ServiceOffering#isSystemVmType()
+       */
+      public T systemVmType(boolean systemVmType) {
          this.systemVmType = systemVmType;
-         return this;
+         return self();
       }
 
-
       public ServiceOffering build() {
-         return new ServiceOffering(id, name, displayText, created, domain, domainId, cpuNumber, cpuSpeed, memory,
-               haSupport, storageType, tags, defaultUse, hostTags, systemOffering, cpuUseLimited, networkRate,
-               systemVmType);
+         return new ServiceOffering(id, name, displayText, created, domain, domainId, cpuNumber, cpuSpeed, memory, haSupport, storageType,
+               Joiner.on(",").join(tags), defaultUse, hostTags, systemOffering, cpuUseLimited, networkRate, systemVmType);
+      }
+
+      public T fromServiceOffering(ServiceOffering in) {
+         return this
+               .id(in.getId())
+               .name(in.getName())
+               .displayText(in.getDisplayText())
+               .created(in.getCreated())
+               .domain(in.getDomain())
+               .domainId(in.getDomainId())
+               .cpuNumber(in.getCpuNumber())
+               .cpuSpeed(in.getCpuSpeed())
+               .memory(in.getMemory())
+               .supportsHA(in.supportsHA())
+               .storageType(in.getStorageType())
+               .tags(in.getTags())
+               .defaultUse(in.isDefaultUse())
+               .hostTags(in.getHostTags())
+               .systemOffering(in.isSystemOffering())
+               .cpuUseLimited(in.isCpuUseLimited())
+               .networkRate(in.getNetworkRate())
+               .systemVmType(in.isSystemVmType());
       }
    }
 
-   private String id;
-   private String name;
-   @SerializedName("displaytext")
-   private String displayText;
-   private Date created;
-   private String domain;
-   @SerializedName("domainid")
-   private String domainId;
-   @SerializedName("cpunumber")
-   private int cpuNumber;
-   @SerializedName("cpuspeed")
-   private int cpuSpeed;
-   private int memory;
-   @SerializedName("offerha")
-   private boolean haSupport;
-   @SerializedName("storagetype")
-   private StorageType storageType;
-   private String tags;
-   @SerializedName("defaultuse")
-   private boolean defaultUse;
-   @SerializedName("hosttags")
-   private String hostTags;
-   @SerializedName("issystem")
-   private boolean systemOffering;
-   @SerializedName("limitcpuuse")
-   private boolean cpuUseLimited;
-   @SerializedName("networkrate")
-   private long networkRate;
-   @SerializedName("systemvmtype")
-   private boolean systemVmType;
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
+   }
 
+   private final String id;
+   private final String name;
+   @Named("displaytext")
+   private final String displayText;
+   private final Date created;
+   private final String domain;
+   @Named("domainid")
+   private final String domainId;
+   @Named("cpunumber")
+   private final int cpuNumber;
+   @Named("cpuspeed")
+   private final int cpuSpeed;
+   private final int memory;
+   @Named("offerha")
+   private final boolean haSupport;
+   @Named("storagetype")
+   private final StorageType storageType;
+   private final String tags;
+   @Named("defaultuse")
+   private final boolean defaultUse;
+   @Named("hosttags")
+   private final String hostTags;
+   @Named("issystem")
+   private final boolean systemOffering;
+   @Named("limitcpuuse")
+   private final boolean cpuUseLimited;
+   @Named("networkrate")
+   private final long networkRate;
+   @Named("systemvmtype")
+   private final boolean systemVmType;
 
-   public ServiceOffering(String id, String name, String displayText, Date created, String domain, String domainId,
-                          int cpuNumber, int cpuSpeed, int memory, boolean haSupport, StorageType storageType, Set<String> tags,
-                          boolean defaultUse, String hostTags, boolean systemOffering, boolean cpuUseLimited, long networkRate,
-                          boolean systemVmType) {
-      this.id = id;
+   @ConstructorProperties({
+         "id", "name", "displaytext", "created", "domain", "domainid", "cpunumber", "cpuspeed", "memory", "offerha", "storagetype", "tags", "defaultuse", "hosttags", "issystem", "limitcpuuse", "networkrate", "systemvmtype"
+   })
+   protected ServiceOffering(String id, @Nullable String name, @Nullable String displayText, @Nullable Date created,
+                             @Nullable String domain, @Nullable String domainId, int cpuNumber, int cpuSpeed, int memory,
+                             boolean haSupport, @Nullable StorageType storageType, @Nullable String tags, boolean defaultUse,
+                             @Nullable String hostTags, boolean systemOffering, boolean cpuUseLimited, long networkRate, boolean systemVmType) {
+      this.id = checkNotNull(id, "id");
       this.name = name;
       this.displayText = displayText;
       this.created = created;
@@ -204,198 +299,191 @@
       this.memory = memory;
       this.haSupport = haSupport;
       this.storageType = storageType;
-      this.tags = tags.size() == 0 ? null : Joiner.on(',').join(tags);
-   }
-
-   /**
-    * present only for serializer
-    */
-   ServiceOffering() {
-
+      this.tags = tags;
+      this.defaultUse = defaultUse;
+      this.hostTags = hostTags;
+      this.systemOffering = systemOffering;
+      this.cpuUseLimited = cpuUseLimited;
+      this.networkRate = networkRate;
+      this.systemVmType = systemVmType;
    }
 
    /**
     * @return the id of the service offering
     */
    public String getId() {
-      return id;
+      return this.id;
    }
 
    /**
     * @return the name of the service offering
     */
-
    public String getName() {
-      return name;
+      return this.name;
    }
 
    /**
     * @return an alternate display text of the service offering.
     */
+   @Nullable
    public String getDisplayText() {
-      return displayText;
+      return this.displayText;
    }
 
    /**
     * @return the date this service offering was created
     */
+   @Nullable
    public Date getCreated() {
-      return created;
+      return this.created;
    }
 
    /**
     * @return Domain name for the offering
     */
+   @Nullable
    public String getDomain() {
-      return domain;
+      return this.domain;
    }
 
    /**
     * @return the domain id of the service offering
     */
+   @Nullable
    public String getDomainId() {
-      return domainId;
+      return this.domainId;
    }
 
    /**
     * @return the number of CPU
     */
    public int getCpuNumber() {
-      return cpuNumber;
+      return this.cpuNumber;
    }
 
    /**
     * @return the clock rate CPU speed in Mhz
     */
    public int getCpuSpeed() {
-      return cpuSpeed;
+      return this.cpuSpeed;
    }
 
    /**
     * @return the memory in MB
     */
    public int getMemory() {
-      return memory;
+      return this.memory;
    }
 
-   /**
-    * @return the ha support in the service offering
-    */
    public boolean supportsHA() {
-      return haSupport;
+      return this.haSupport;
    }
 
    /**
     * @return the storage type for this service offering
     */
+   @Nullable
    public StorageType getStorageType() {
-      return storageType;
-   }
-
-   /**
-    * @return whether this is a default system vm offering
-    */
-   public boolean isDefaultUse() {
-      return defaultUse;
-   }
-
-   /**
-    * @return the host tag for the service offering
-    */
-   public String getHostTags() {
-      return hostTags;
-   }
-
-   /**
-    * @return whether this is a system vm offering
-    */
-   public boolean isSystemOffering() {
-      return systemOffering;
-   }
-
-   /**
-    * @return whether restrict the CPU usage to committed service offering
-    */
-   public boolean isCpuUseLimited() {
-      return cpuUseLimited;
-   }
-
-   /**
-    * @return data transfer rate in megabits per second allowed.
-    */
-   public long getNetworkRate() {
-      return networkRate;
-   }
-
-   /**
-    * @return whether this is a the systemvm type for system vm offering
-    */
-   public boolean isSystemVmType() {
-      return systemVmType;
+      return this.storageType;
    }
 
    /**
     * @return the tags for the service offering
     */
    public Set<String> getTags() {
-      return tags != null ? ImmutableSet.copyOf(Splitter.on(',').split(tags)) : ImmutableSet.<String>of();
+      return tags == null ? ImmutableSet.<String>of() : ImmutableSet.copyOf(Splitter.on(',').split(tags));
    }
 
-   @Override
-   public boolean equals(Object o) {
-      if (this == o) return true;
-      if (o == null || getClass() != o.getClass()) return false;
+   /**
+    * @return whether this is a default system vm offering
+    */
+   public boolean isDefaultUse() {
+      return this.defaultUse;
+   }
 
-      ServiceOffering that = (ServiceOffering) o;
+   /**
+    * @return the host tag for the service offering
+    */
+   @Nullable
+   public String getHostTags() {
+      return this.hostTags;
+   }
 
-      if (!Objects.equal(cpuNumber, that.cpuNumber)) return false;
-      if (!Objects.equal(cpuSpeed, that.cpuSpeed)) return false;
-      if (!Objects.equal(created, that.created)) return false;
-      if (!Objects.equal(displayText, that.displayText)) return false;
-      if (!Objects.equal(domain, that.domain)) return false;
-      if (!Objects.equal(domainId, that.domainId)) return false;
-      if (!Objects.equal(haSupport, that.haSupport)) return false;
-      if (!Objects.equal(id, that.id)) return false;
-      if (!Objects.equal(memory, that.memory)) return false;
-      if (!Objects.equal(name, that.name)) return false;
-      if (!Objects.equal(storageType, that.storageType)) return false;
-      if (!Objects.equal(tags, that.tags)) return false;
+   /**
+    * @return whether this is a system vm offering
+    */
+   public boolean isSystemOffering() {
+      return this.systemOffering;
+   }
 
-      return true;
+   /**
+    * @return whether restrict the CPU usage to committed service offering
+    */
+   public boolean isCpuUseLimited() {
+      return this.cpuUseLimited;
+   }
+
+   /**
+    * @return data transfer rate in megabits per second allowed.
+    */
+   public long getNetworkRate() {
+      return this.networkRate;
+   }
+
+   /**
+    * @return whether this is a the systemvm type for system vm offering
+    */
+   public boolean isSystemVmType() {
+      return this.systemVmType;
    }
 
    @Override
    public int hashCode() {
-       return Objects.hashCode(cpuNumber, cpuSpeed, created, displayText, domain, domainId, haSupport, id, memory, name, storageType, tags);
+      return Objects.hashCode(id, name, displayText, created, domain, domainId, cpuNumber, cpuSpeed, memory, haSupport, storageType, tags, defaultUse, hostTags, systemOffering, cpuUseLimited, networkRate, systemVmType);
+   }
+
+   @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      ServiceOffering that = ServiceOffering.class.cast(obj);
+      return Objects.equal(this.id, that.id)
+            && Objects.equal(this.name, that.name)
+            && Objects.equal(this.displayText, that.displayText)
+            && Objects.equal(this.created, that.created)
+            && Objects.equal(this.domain, that.domain)
+            && Objects.equal(this.domainId, that.domainId)
+            && Objects.equal(this.cpuNumber, that.cpuNumber)
+            && Objects.equal(this.cpuSpeed, that.cpuSpeed)
+            && Objects.equal(this.memory, that.memory)
+            && Objects.equal(this.haSupport, that.haSupport)
+            && Objects.equal(this.storageType, that.storageType)
+            && Objects.equal(this.getTags(), that.getTags())
+            && Objects.equal(this.defaultUse, that.defaultUse)
+            && Objects.equal(this.hostTags, that.hostTags)
+            && Objects.equal(this.systemOffering, that.systemOffering)
+            && Objects.equal(this.cpuUseLimited, that.cpuUseLimited)
+            && Objects.equal(this.networkRate, that.networkRate)
+            && Objects.equal(this.systemVmType, that.systemVmType);
+   }
+
+   protected ToStringHelper string() {
+      return Objects.toStringHelper(this)
+            .add("id", id).add("name", name).add("displayText", displayText).add("created", created).add("domain", domain)
+            .add("domainId", domainId).add("cpuNumber", cpuNumber).add("cpuSpeed", cpuSpeed).add("memory", memory)
+            .add("haSupport", haSupport).add("storageType", storageType).add("tags", getTags()).add("defaultUse", defaultUse)
+            .add("hostTags", hostTags).add("systemOffering", systemOffering).add("cpuUseLimited", cpuUseLimited)
+            .add("networkRate", networkRate).add("systemVmType", systemVmType);
    }
 
    @Override
    public String toString() {
-      return "ServiceOffering{" +
-            "id=" + id +
-            ", name='" + name + '\'' +
-            ", displayText='" + displayText + '\'' +
-            ", created=" + created +
-            ", domain='" + domain + '\'' +
-            ", domainId=" + domainId +
-            ", cpuNumber=" + cpuNumber +
-            ", cpuSpeed=" + cpuSpeed +
-            ", memory=" + memory +
-            ", haSupport=" + haSupport +
-            ", storageType=" + storageType +
-            ", tags='" + tags + '\'' +
-            ", defaultUse=" + defaultUse +
-            ", hostTags='" + hostTags + '\'' +
-            ", systemOffering=" + systemOffering +
-            ", cpuUseLimited=" + cpuUseLimited +
-            ", networkRate=" + networkRate +
-            ", systemVmType=" + systemVmType +
-            '}';
+      return string().toString();
    }
 
    @Override
-   public int compareTo(ServiceOffering arg0) {
-      return id.compareTo(arg0.getId());
+   public int compareTo(ServiceOffering o) {
+      return id.compareTo(o.getId());
    }
 
 }
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Snapshot.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Snapshot.java
index a4a9704..7e84da0 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Snapshot.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Snapshot.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -20,158 +20,27 @@
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import java.beans.ConstructorProperties;
 import java.util.Date;
 
+import javax.inject.Named;
+
+import org.jclouds.javax.annotation.Nullable;
+
 import com.google.common.base.CaseFormat;
 import com.google.common.base.Objects;
-import com.google.gson.annotations.SerializedName;
+import com.google.common.base.Objects.ToStringHelper;
 
 /**
+ * Class Snapshot
+ *
  * @author Richard Downer
  */
-public class Snapshot implements Comparable<Snapshot> {
+public class Snapshot {
 
-   public static Builder builder() {
-      return new Builder();
-   }
-
-   public static class Builder {
-
-      private String id;
-      private String account;
-      private Date created;
-      private String domain;
-      private String domainId;
-      private Interval interval;
-      private String jobId;
-      private String jobStatus;
-      private String name;
-      private Type snapshotType;
-      private State state;
-      private String volumeId;
-      private String volumeName;
-      private Volume.Type volumeType;
-
-      /**
-       * @param id ID of the snapshot
-       */
-      public Builder id(String id) {
-         this.id = id;
-         return this;
-      }
-
-      /**
-       * @param account the account associated with the snapshot
-       */
-      public Builder account(String account) {
-         this.account = account;
-         return this;
-      }
-
-      /**
-       * @param created the date the snapshot was created
-       */
-      public Builder created(Date created) {
-         this.created = created;
-         return this;
-      }
-
-      /**
-       * @param domain the domain name of the snapshot's account
-       */
-      public Builder domain(String domain) {
-         this.domain = domain;
-         return this;
-      }
-
-      /**
-       * @param domainId the domain ID of the snapshot's account
-       */
-      public Builder domainId(String domainId) {
-         this.domainId = domainId;
-         return this;
-      }
-
-      /**
-       * @param interval valid types are hourly, daily, weekly, monthy, template, and none.
-       */
-      public Builder interval(Interval interval) {
-         this.interval = interval;
-         return this;
-      }
-
-      /**
-       * @param jobId the job ID associated with the snapshot. This is only displayed if the snapshot listed is part of a currently running asynchronous job.
-       */
-      public Builder jobId(String jobId) {
-         this.jobId = jobId;
-         return this;
-      }
-
-      /**
-       * @param jobStatus the job status associated with the snapshot. This is only displayed if the snapshot listed is part of a currently running asynchronous job.
-       */
-      public Builder jobStatus(String jobStatus) {
-         this.jobStatus = jobStatus;
-         return this;
-      }
-
-      /**
-       * @param name name of the snapshot
-       */
-      public Builder name(String name) {
-         this.name = name;
-         return this;
-      }
-
-      /**
-       * @param snapshotType the type of the snapshot
-       */
-      public Builder snapshotType(Type snapshotType) {
-         this.snapshotType = snapshotType;
-         return this;
-      }
-
-      /**
-       * @param state the state of the snapshot. BackedUp means that snapshot is ready to be used; Creating - the snapshot is being allocated on the primary storage; BackingUp - the snapshot is being backed up on secondary storage
-       */
-      public Builder state(State state) {
-         this.state = state;
-         return this;
-      }
-
-      /**
-       * @param volumeId ID of the disk volume
-       */
-      public Builder volumeId(String volumeId) {
-         this.volumeId = volumeId;
-         return this;
-      }
-
-      /**
-       * @param volumeName name of the disk volume
-       */
-      public Builder volumeName(String volumeName) {
-         this.volumeName = volumeName;
-         return this;
-      }
-
-      /**
-       * @param volumeType type of the disk volume
-       */
-      public Builder volumeType(Volume.Type volumeType) {
-         this.volumeType = volumeType;
-         return this;
-      }
-
-      public Snapshot build() {
-         return new Snapshot(id, account, created, domain, domainId, interval, jobId,
-               jobStatus, name, snapshotType, state, volumeId, volumeName, volumeType);
-      }
-
-   }
-
-   public enum State {
+   /**
+    */
+   public static enum State {
 
       BACKED_UP, CREATING, BACKING_UP, UNRECOGNIZED;
 
@@ -189,7 +58,9 @@
       }
    }
 
-   public enum Type {
+   /**
+    */
+   public static enum Type {
 
       MANUAL, RECURRING, UNRECOGNIZED;
 
@@ -202,7 +73,9 @@
       }
    }
 
-   public enum Interval {
+   /**
+    */
+   public static enum Interval {
 
       HOURLY, DAILY, WEEKLY, MONTHLY, template, none, UNRECOGNIZED;
 
@@ -215,32 +88,206 @@
       }
    }
 
-   private String id;
-   private String account;
-   private Date created;
-   private String domain;
-   @SerializedName("domainid")
-   private String domainId;
-   @SerializedName("intervaltype")
-   private Interval interval;
-   @SerializedName("jobid")
-   private String jobId;
-   @SerializedName("jobstatus")
-   private String jobStatus;
-   private String name;
-   @SerializedName("snapshottype")
-   private Type snapshotType;
-   private State state;
-   @SerializedName("volumeid")
-   private String volumeId;
-   @SerializedName("volumename")
-   private String volumeName;
-   @SerializedName("volumetype")
-   private Volume.Type volumeType;
+   public static Builder<?> builder() {
+      return new ConcreteBuilder();
+   }
 
-   public Snapshot(String id, String account, Date created, String domain, String domainId, Interval interval, String jobId,
-         String jobStatus, String name, Type snapshotType, State state, String volumeId, String volumeName, Volume.Type volumeType) {
-      this.id = id;
+   public Builder<?> toBuilder() {
+      return new ConcreteBuilder().fromSnapshot(this);
+   }
+
+   public static abstract class Builder<T extends Builder<T>> {
+      protected abstract T self();
+
+      protected String id;
+      protected String account;
+      protected Date created;
+      protected String domain;
+      protected String domainId;
+      protected Snapshot.Interval interval;
+      protected String jobId;
+      protected String jobStatus;
+      protected String name;
+      protected Snapshot.Type snapshotType;
+      protected Snapshot.State state;
+      protected String volumeId;
+      protected String volumeName;
+      protected Volume.Type volumeType;
+
+      /**
+       * @see Snapshot#getId()
+       */
+      public T id(String id) {
+         this.id = id;
+         return self();
+      }
+
+      /**
+       * @see Snapshot#getAccount()
+       */
+      public T account(String account) {
+         this.account = account;
+         return self();
+      }
+
+      /**
+       * @see Snapshot#getCreated()
+       */
+      public T created(Date created) {
+         this.created = created;
+         return self();
+      }
+
+      /**
+       * @see Snapshot#getDomain()
+       */
+      public T domain(String domain) {
+         this.domain = domain;
+         return self();
+      }
+
+      /**
+       * @see Snapshot#getDomainId()
+       */
+      public T domainId(String domainId) {
+         this.domainId = domainId;
+         return self();
+      }
+
+      /**
+       * @see Snapshot#getInterval()
+       */
+      public T interval(Snapshot.Interval interval) {
+         this.interval = interval;
+         return self();
+      }
+
+      /**
+       * @see Snapshot#getJobId()
+       */
+      public T jobId(String jobId) {
+         this.jobId = jobId;
+         return self();
+      }
+
+      /**
+       * @see Snapshot#getJobStatus()
+       */
+      public T jobStatus(String jobStatus) {
+         this.jobStatus = jobStatus;
+         return self();
+      }
+
+      /**
+       * @see Snapshot#getName()
+       */
+      public T name(String name) {
+         this.name = name;
+         return self();
+      }
+
+      /**
+       * @see Snapshot#getSnapshotType()
+       */
+      public T snapshotType(Snapshot.Type snapshotType) {
+         this.snapshotType = snapshotType;
+         return self();
+      }
+
+      /**
+       * @see Snapshot#getState()
+       */
+      public T state(Snapshot.State state) {
+         this.state = state;
+         return self();
+      }
+
+      /**
+       * @see Snapshot#getVolumeId()
+       */
+      public T volumeId(String volumeId) {
+         this.volumeId = volumeId;
+         return self();
+      }
+
+      /**
+       * @see Snapshot#getVolumeName()
+       */
+      public T volumeName(String volumeName) {
+         this.volumeName = volumeName;
+         return self();
+      }
+
+      /**
+       * @see Snapshot#getVolumeType()
+       */
+      public T volumeType(Volume.Type volumeType) {
+         this.volumeType = volumeType;
+         return self();
+      }
+
+      public Snapshot build() {
+         return new Snapshot(id, account, created, domain, domainId, interval, jobId, jobStatus, name, snapshotType, state,
+               volumeId, volumeName, volumeType);
+      }
+
+      public T fromSnapshot(Snapshot in) {
+         return this
+               .id(in.getId())
+               .account(in.getAccount())
+               .created(in.getCreated())
+               .domain(in.getDomain())
+               .domainId(in.getDomainId())
+               .interval(in.getInterval())
+               .jobId(in.getJobId())
+               .jobStatus(in.getJobStatus())
+               .name(in.getName())
+               .snapshotType(in.getSnapshotType())
+               .state(in.getState())
+               .volumeId(in.getVolumeId())
+               .volumeName(in.getVolumeName())
+               .volumeType(in.getVolumeType());
+      }
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
+   }
+
+   private final String id;
+   private final String account;
+   private final Date created;
+   private final String domain;
+   @Named("domainid")
+   private final String domainId;
+   @Named("intervaltype")
+   private final Snapshot.Interval interval;
+   @Named("jobid")
+   private final String jobId;
+   @Named("jobstatus")
+   private final String jobStatus;
+   private final String name;
+   @Named("snapshottype")
+   private final Snapshot.Type snapshotType;
+   private final Snapshot.State state;
+   @Named("volumeid")
+   private final String volumeId;
+   @Named("volumename")
+   private final String volumeName;
+   @Named("volumetype")
+   private final Volume.Type volumeType;
+
+   @ConstructorProperties({
+         "id", "account", "created", "domain", "domainid", "intervaltype", "jobid", "jobstatus", "name", "snapshottype", "state", "volumeid", "volumename", "volumetype"
+   })
+   protected Snapshot(String id, @Nullable String account, @Nullable Date created, @Nullable String domain, @Nullable String domainId,
+                      @Nullable Snapshot.Interval interval, @Nullable String jobId, @Nullable String jobStatus, @Nullable String name,
+                      @Nullable Snapshot.Type snapshotType, @Nullable Snapshot.State state, @Nullable String volumeId, @Nullable String volumeName,
+                      @Nullable Volume.Type volumeType) {
+      this.id = checkNotNull(id, "id");
       this.account = account;
       this.created = created;
       this.domain = domain;
@@ -257,163 +304,152 @@
    }
 
    /**
-    * present only for serializer
-    */
-   Snapshot() {
-   }
-
-   /**
     * @return ID of the snapshot
     */
    public String getId() {
-      return id;
+      return this.id;
    }
 
    /**
     * @return the account associated with the snapshot
     */
+   @Nullable
    public String getAccount() {
-      return account;
+      return this.account;
    }
 
    /**
     * @return the date the snapshot was created
     */
+   @Nullable
    public Date getCreated() {
-      return created;
+      return this.created;
    }
 
    /**
     * @return the domain name of the snapshot's account
     */
+   @Nullable
    public String getDomain() {
-      return domain;
+      return this.domain;
    }
 
    /**
     * @return the domain ID of the snapshot's account
     */
+   @Nullable
    public String getDomainId() {
-      return domainId;
+      return this.domainId;
    }
 
    /**
     * @return valid types are hourly, daily, weekly, monthy, template, and none.
     */
-   public Interval getInterval() {
-      return interval;
+   @Nullable
+   public Snapshot.Interval getInterval() {
+      return this.interval;
    }
 
    /**
     * @return the job ID associated with the snapshot. This is only displayed if the snapshot listed is part of a currently running asynchronous job.
     */
+   @Nullable
    public String getJobId() {
-      return jobId;
+      return this.jobId;
    }
 
    /**
     * @return the job status associated with the snapshot. This is only displayed if the snapshot listed is part of a currently running asynchronous job.
     */
+   @Nullable
    public String getJobStatus() {
-      return jobStatus;
+      return this.jobStatus;
    }
 
    /**
     * @return name of the snapshot
     */
+   @Nullable
    public String getName() {
-      return name;
+      return this.name;
    }
 
    /**
     * @return the type of the snapshot
     */
-   public Type getSnapshotType() {
-      return snapshotType;
+   @Nullable
+   public Snapshot.Type getSnapshotType() {
+      return this.snapshotType;
    }
 
    /**
     * @return the state of the snapshot. BackedUp means that snapshot is ready to be used; Creating - the snapshot is being allocated on the primary storage; BackingUp - the snapshot is being backed up on secondary storage
     */
-   public State getState() {
-      return state;
+   @Nullable
+   public Snapshot.State getState() {
+      return this.state;
    }
 
    /**
     * @return ID of the disk volume
     */
+   @Nullable
    public String getVolumeId() {
-      return volumeId;
+      return this.volumeId;
    }
 
    /**
     * @return name of the disk volume
     */
+   @Nullable
    public String getVolumeName() {
-      return volumeName;
+      return this.volumeName;
    }
 
    /**
     * @return type of the disk volume
     */
+   @Nullable
    public Volume.Type getVolumeType() {
-      return volumeType;
-   }
-
-   @Override
-   public boolean equals(Object o) {
-      if (this == o) return true;
-      if (o == null || getClass() != o.getClass()) return false;
-
-      Snapshot that = (Snapshot) o;
-
-      if (!Objects.equal(domainId, that.domainId)) return false;
-      if (!Objects.equal(id, that.id)) return false;
-      if (!Objects.equal(jobId, that.jobId)) return false;
-      if (!Objects.equal(volumeId, that.volumeId)) return false;
-      if (!Objects.equal(account, that.account)) return false;
-      if (!Objects.equal(created, that.created)) return false;
-      if (!Objects.equal(domain, that.domain)) return false;
-      if (!Objects.equal(interval, that.interval)) return false;
-      if (!Objects.equal(jobStatus, that.jobStatus)) return false;
-      if (!Objects.equal(name, that.name)) return false;
-      if (!Objects.equal(snapshotType, that.snapshotType)) return false;
-      if (!Objects.equal(state, that.state)) return false;
-      if (!Objects.equal(volumeName, that.volumeName)) return false;
-      if (!Objects.equal(volumeType, that.volumeType)) return false;
-
-      return true;
+      return this.volumeType;
    }
 
    @Override
    public int hashCode() {
-       return Objects.hashCode(domainId, id, jobId, volumeId, account, created, domain,
-                               interval, jobStatus, name, snapshotType, state, volumeName,
-                               volumeType);
+      return Objects.hashCode(id, account, created, domain, domainId, interval, jobId, jobStatus, name, snapshotType, state, volumeId, volumeName, volumeType);
+   }
+
+   @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      Snapshot that = Snapshot.class.cast(obj);
+      return Objects.equal(this.id, that.id)
+            && Objects.equal(this.account, that.account)
+            && Objects.equal(this.created, that.created)
+            && Objects.equal(this.domain, that.domain)
+            && Objects.equal(this.domainId, that.domainId)
+            && Objects.equal(this.interval, that.interval)
+            && Objects.equal(this.jobId, that.jobId)
+            && Objects.equal(this.jobStatus, that.jobStatus)
+            && Objects.equal(this.name, that.name)
+            && Objects.equal(this.snapshotType, that.snapshotType)
+            && Objects.equal(this.state, that.state)
+            && Objects.equal(this.volumeId, that.volumeId)
+            && Objects.equal(this.volumeName, that.volumeName)
+            && Objects.equal(this.volumeType, that.volumeType);
+   }
+
+   protected ToStringHelper string() {
+      return Objects.toStringHelper(this)
+            .add("id", id).add("account", account).add("created", created).add("domain", domain).add("domainId", domainId)
+            .add("interval", interval).add("jobId", jobId).add("jobStatus", jobStatus).add("name", name).add("snapshotType", snapshotType)
+            .add("state", state).add("volumeId", volumeId).add("volumeName", volumeName).add("volumeType", volumeType);
    }
 
    @Override
    public String toString() {
-      return "Snapshot{" +
-            "id=" + id +
-            ", account='" + account + '\'' +
-            ", created=" + created +
-            ", domain='" + domain + '\'' +
-            ", domainId=" + domainId +
-            ", interval=" + interval +
-            ", jobId=" + jobId +
-            ", jobStatus='" + jobStatus + '\'' +
-            ", name='" + name + '\'' +
-            ", snapshotType=" + snapshotType +
-            ", state=" + state +
-            ", volumeId=" + volumeId +
-            ", volumeName='" + volumeName + '\'' +
-            ", volumeType=" + volumeType +
-            '}';
+      return string().toString();
    }
 
-   @Override
-   public int compareTo(Snapshot other) {
-      return id.compareTo(other.getId());
-   }
 }
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/SnapshotPolicy.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/SnapshotPolicy.java
index b736eb7..3651a56 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/SnapshotPolicy.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/SnapshotPolicy.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -18,184 +18,208 @@
  */
 package org.jclouds.cloudstack.domain;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.beans.ConstructorProperties;
+
+import javax.inject.Named;
+
+import org.jclouds.javax.annotation.Nullable;
+
 import com.google.common.base.Objects;
-import com.google.gson.annotations.SerializedName;
+import com.google.common.base.Objects.ToStringHelper;
 
 /**
+ * Class SnapshotPolicy
+ * 
  * @author Richard Downer
- */
-public class SnapshotPolicy implements Comparable<SnapshotPolicy> {
+*/
+public class SnapshotPolicy {
 
-   public static Builder builder() {
-      return new Builder();
+   public static Builder<?> builder() { 
+      return new ConcreteBuilder();
+   }
+   
+   public Builder<?> toBuilder() { 
+      return new ConcreteBuilder().fromSnapshotPolicy(this);
    }
 
-   public static class Builder {
+   public static abstract class Builder<T extends Builder<T>>  {
+      protected abstract T self();
 
-      private String id;
-      private Snapshot.Interval interval;
-      private long numberToRetain;
-      private String schedule;
-      private String timezone;
-      private String volumeId;
-
-      /**
-       * @param id the ID of the snapshot policy
+      protected String id;
+      protected Snapshot.Interval interval;
+      protected long numberToRetain;
+      protected String schedule;
+      protected String timezone;
+      protected String volumeId;
+   
+      /** 
+       * @see SnapshotPolicy#getId()
        */
-      public Builder id(String id) {
+      public T id(String id) {
          this.id = id;
-         return this;
+         return self();
       }
 
-      /**
-       * @param interval valid types are hourly, daily, weekly, monthy, template, and none.
+      /** 
+       * @see SnapshotPolicy#getInterval()
        */
-      public Builder interval(Snapshot.Interval interval) {
+      public T interval(Snapshot.Interval interval) {
          this.interval = interval;
-         return this;
+         return self();
       }
 
-      /**
-       * @param numberToRetain maximum number of snapshots retained
+      /** 
+       * @see SnapshotPolicy#getNumberToRetain()
        */
-      public Builder numberToRetain(long numberToRetain) {
+      public T numberToRetain(long numberToRetain) {
          this.numberToRetain = numberToRetain;
-         return this;
+         return self();
       }
 
-      /**
-       * @param schedule time the snapshot is scheduled to be taken.
+      /** 
+       * @see SnapshotPolicy#getSchedule()
        */
-      public Builder schedule(String schedule) {
+      public T schedule(String schedule) {
          this.schedule = schedule;
-         return this;
+         return self();
       }
 
-      /**
-       * @param timezone the time zone of the snapshot policy
+      /** 
+       * @see SnapshotPolicy#getTimezone()
        */
-      public Builder timezone(String timezone) {
+      public T timezone(String timezone) {
          this.timezone = timezone;
-         return this;
+         return self();
       }
 
-      /**
-       * @param volumeId ID of the disk volume
+      /** 
+       * @see SnapshotPolicy#getVolumeId()
        */
-      public Builder volumeId(String volumeId) {
+      public T volumeId(String volumeId) {
          this.volumeId = volumeId;
-         return this;
+         return self();
       }
 
       public SnapshotPolicy build() {
          return new SnapshotPolicy(id, interval, numberToRetain, schedule, timezone, volumeId);
       }
+      
+      public T fromSnapshotPolicy(SnapshotPolicy in) {
+         return this
+                  .id(in.getId())
+                  .interval(in.getInterval())
+                  .numberToRetain(in.getNumberToRetain())
+                  .schedule(in.getSchedule())
+                  .timezone(in.getTimezone())
+                  .volumeId(in.getVolumeId());
+      }
    }
 
-   private String id;
-   @SerializedName("intervaltype")
-   private Snapshot.Interval interval;
-   @SerializedName("maxsnaps")
-   private long numberToRetain;
-   private String schedule;
-   private String timezone;
-   @SerializedName("volumeid")
-   private String volumeId;
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
+   }
 
-   public SnapshotPolicy(String id, Snapshot.Interval interval, long numberToRetain, String schedule, String timezone, String volumeId) {
-      this.id = id;
+   private final String id;
+   @Named("intervaltype")
+   private final Snapshot.Interval interval;
+   @Named("maxsnaps")
+   private final long numberToRetain;
+   private final String schedule;
+   private final String timezone;
+   @Named("volumeid")
+   private final String volumeId;
+
+   @ConstructorProperties({
+      "id", "intervaltype", "maxsnaps", "schedule", "timezone", "volumeid"
+   })
+   protected SnapshotPolicy(String id, @Nullable Snapshot.Interval interval, long numberToRetain, @Nullable String schedule,
+                            @Nullable String timezone, @Nullable String volumeId) {
+      this.id = checkNotNull(id, "id");
       this.interval = interval;
       this.numberToRetain = numberToRetain;
       this.schedule = schedule;
       this.timezone = timezone;
       this.volumeId = volumeId;
    }
-   
-   /**
-    * present only for serializer
-    */
-   SnapshotPolicy() {
-   }
 
    /**
     * @return the ID of the snapshot policy
     */
    public String getId() {
-      return id;
+      return this.id;
    }
 
    /**
     * @return valid types are hourly, daily, weekly, monthy, template, and none.
     */
+   @Nullable
    public Snapshot.Interval getInterval() {
-      return interval;
+      return this.interval;
    }
 
    /**
     * @return maximum number of snapshots retained
     */
    public long getNumberToRetain() {
-      return numberToRetain;
+      return this.numberToRetain;
    }
 
    /**
     * @return time the snapshot is scheduled to be taken.
     */
+   @Nullable
    public String getSchedule() {
-      return schedule;
+      return this.schedule;
    }
 
    /**
     * @return the time zone of the snapshot policy
     */
+   @Nullable
    public String getTimezone() {
-      return timezone;
+      return this.timezone;
    }
 
    /**
     * @return ID of the disk volume
     */
+   @Nullable
    public String getVolumeId() {
-      return volumeId;
-   }
-
-   @Override
-   public boolean equals(Object o) {
-      if (this == o) return true;
-      if (o == null || getClass() != o.getClass()) return false;
-
-      SnapshotPolicy that = (SnapshotPolicy) o;
-
-      if (!Objects.equal(id, that.id)) return false;
-      if (!Objects.equal(numberToRetain, that.numberToRetain)) return false;
-      if (!Objects.equal(volumeId, that.volumeId)) return false;
-      if (!Objects.equal(interval, that.interval)) return false;
-      if (!Objects.equal(schedule, that.schedule)) return false;
-      if (!Objects.equal(timezone, that.timezone)) return false;
-
-      return true;
+      return this.volumeId;
    }
 
    @Override
    public int hashCode() {
-       return Objects.hashCode(id, numberToRetain, volumeId, interval, schedule, timezone);
+      return Objects.hashCode(id, interval, numberToRetain, schedule, timezone, volumeId);
    }
 
    @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      SnapshotPolicy that = SnapshotPolicy.class.cast(obj);
+      return Objects.equal(this.id, that.id)
+               && Objects.equal(this.interval, that.interval)
+               && Objects.equal(this.numberToRetain, that.numberToRetain)
+               && Objects.equal(this.schedule, that.schedule)
+               && Objects.equal(this.timezone, that.timezone)
+               && Objects.equal(this.volumeId, that.volumeId);
+   }
+   
+   protected ToStringHelper string() {
+      return Objects.toStringHelper(this)
+            .add("id", id).add("interval", interval).add("numberToRetain", numberToRetain).add("schedule", schedule).add("timezone", timezone)
+            .add("volumeId", volumeId);
+   }
+   
+   @Override
    public String toString() {
-      return "SnapshotPolicy{" +
-            "id=" + id +
-            ", interval=" + interval +
-            ", numberToRetain=" + numberToRetain +
-            ", schedule='" + schedule + '\'' +
-            ", timezone='" + timezone + '\'' +
-            ", volumeId=" + volumeId +
-            '}';
-   }
-
-   @Override
-   public int compareTo(SnapshotPolicy other) {
-      return id.compareTo(other.getId());
+      return string().toString();
    }
 
 }
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/SnapshotPolicySchedule.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/SnapshotPolicySchedule.java
index 82525c2..d006ce7 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/SnapshotPolicySchedule.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/SnapshotPolicySchedule.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -18,35 +18,112 @@
  */
 package org.jclouds.cloudstack.domain;
 
+import java.beans.ConstructorProperties;
+
+import org.jclouds.javax.annotation.Nullable;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
+
 /**
  * Describes the schedule of a snapshot policy.
  *
  * @see org.jclouds.cloudstack.util.SnapshotPolicySchedules
  * @author Richard Downer
- */
+*/
 public class SnapshotPolicySchedule {
 
-   private Snapshot.Interval interval;
-   private String time;
+   public static Builder<?> builder() { 
+      return new ConcreteBuilder();
+   }
+   
+   public Builder<?> toBuilder() { 
+      return new ConcreteBuilder().fromSnapshotPolicySchedule(this);
+   }
 
-   public SnapshotPolicySchedule(Snapshot.Interval interval, String time) {
+   public static abstract class Builder<T extends Builder<T>>  {
+      protected abstract T self();
+
+      protected Snapshot.Interval interval;
+      protected String time;
+   
+      /** 
+       * @see SnapshotPolicySchedule#getInterval()
+       */
+      public T interval(Snapshot.Interval interval) {
+         this.interval = interval;
+         return self();
+      }
+
+      /** 
+       * @see SnapshotPolicySchedule#getTime()
+       */
+      public T time(String time) {
+         this.time = time;
+         return self();
+      }
+
+      public SnapshotPolicySchedule build() {
+         return new SnapshotPolicySchedule(interval, time);
+      }
+      
+      public T fromSnapshotPolicySchedule(SnapshotPolicySchedule in) {
+         return this
+                  .interval(in.getInterval())
+                  .time(in.getTime());
+      }
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
+   }
+
+   private final Snapshot.Interval interval;
+   private final String time;
+
+   @ConstructorProperties({
+      "interval", "time"
+   })
+   protected SnapshotPolicySchedule(@Nullable Snapshot.Interval interval, @Nullable String time) {
       this.interval = interval;
       this.time = time;
    }
 
+   @Nullable
    public Snapshot.Interval getInterval() {
-      return interval;
+      return this.interval;
    }
 
+   @Nullable
    public String getTime() {
-      return time;
+      return this.time;
    }
 
    @Override
-   public String toString() {
-      return "SnapshotPolicySchedule{" +
-            "interval=" + interval +
-            ", time='" + time + '\'' +
-            '}';
+   public int hashCode() {
+      return Objects.hashCode(interval, time);
    }
+
+   @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      SnapshotPolicySchedule that = SnapshotPolicySchedule.class.cast(obj);
+      return Objects.equal(this.interval, that.interval)
+               && Objects.equal(this.time, that.time);
+   }
+   
+   protected ToStringHelper string() {
+      return Objects.toStringHelper(this)
+            .add("interval", interval).add("time", time);
+   }
+   
+   @Override
+   public String toString() {
+      return string().toString();
+   }
+
 }
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/SshKeyPair.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/SshKeyPair.java
index ef36ea3..c1dcfca 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/SshKeyPair.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/SshKeyPair.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -16,106 +16,135 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
 package org.jclouds.cloudstack.domain;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.beans.ConstructorProperties;
+
+import javax.inject.Named;
+
+import org.jclouds.javax.annotation.Nullable;
+
 import com.google.common.base.Objects;
-import com.google.gson.annotations.SerializedName;
+import com.google.common.base.Objects.ToStringHelper;
 
 /**
+ * Class SshKeyPair
+ * 
  * @author Vijay Kiran
- */
-public class SshKeyPair implements Comparable<SshKeyPair> {
+*/
+public class SshKeyPair {
 
-   public static Builder builder() {
-      return new Builder();
+   public static Builder<?> builder() { 
+      return new ConcreteBuilder();
+   }
+   
+   public Builder<?> toBuilder() { 
+      return new ConcreteBuilder().fromSshKeyPair(this);
    }
 
-   public static class Builder {
+   public static abstract class Builder<T extends Builder<T>>  {
+      protected abstract T self();
 
-      private String fingerprint;
-      private String name;
-      private String privateKey;
-
-      public Builder fingerprint(String fingerprint) {
+      protected String fingerprint;
+      protected String name;
+      protected String privateKey;
+   
+      /** 
+       * @see SshKeyPair#getFingerprint()
+       */
+      public T fingerprint(String fingerprint) {
          this.fingerprint = fingerprint;
-         return this;
+         return self();
       }
 
-      public Builder name(String name) {
+      /** 
+       * @see SshKeyPair#getName()
+       */
+      public T name(String name) {
          this.name = name;
-         return this;
+         return self();
       }
 
-      public Builder privateKey(String privateKey) {
+      /** 
+       * @see SshKeyPair#getPrivateKey()
+       */
+      public T privateKey(String privateKey) {
          this.privateKey = privateKey;
-         return this;
+         return self();
       }
 
       public SshKeyPair build() {
          return new SshKeyPair(fingerprint, name, privateKey);
       }
+      
+      public T fromSshKeyPair(SshKeyPair in) {
+         return this
+                  .fingerprint(in.getFingerprint())
+                  .name(in.getName())
+                  .privateKey(in.getPrivateKey());
+      }
    }
 
-   // for deserialization
-   SshKeyPair() {
-
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
    }
 
-   private String fingerprint;
-   private String name;
-   @SerializedName("privatekey")
-   private String privateKey;
+   private final String fingerprint;
+   private final String name;
+   @Named("privatekey")
+   private final String privateKey;
 
-   public SshKeyPair(String fingerprint, String name, String privateKey) {
+   @ConstructorProperties({
+      "fingerprint", "name", "privatekey"
+   })
+   protected SshKeyPair(@Nullable String fingerprint, String name, @Nullable String privateKey) {
       this.fingerprint = fingerprint;
-      this.name = name;
+      this.name = checkNotNull(name, "name");
       this.privateKey = privateKey;
    }
 
+   @Nullable
    public String getFingerprint() {
-      return fingerprint;
+      return this.fingerprint;
    }
 
    public String getName() {
-      return name;
+      return this.name;
    }
 
+   @Nullable
    public String getPrivateKey() {
-      return privateKey;
-   }
-
-   @Override
-   public boolean equals(Object o) {
-      if (this == o) return true;
-      if (o == null || getClass() != o.getClass()) return false;
-
-      SshKeyPair that = (SshKeyPair) o;
-
-      if (!Objects.equal(fingerprint, that.fingerprint)) return false;
-      if (!Objects.equal(name, that.name)) return false;
-      if (!Objects.equal(privateKey, that.privateKey)) return false;
-
-      return true;
+      return this.privateKey;
    }
 
    @Override
    public int hashCode() {
-       return Objects.hashCode(fingerprint, name, privateKey);
+      return Objects.hashCode(fingerprint, name, privateKey);
    }
 
    @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      SshKeyPair that = SshKeyPair.class.cast(obj);
+      return Objects.equal(this.fingerprint, that.fingerprint)
+               && Objects.equal(this.name, that.name)
+               && Objects.equal(this.privateKey, that.privateKey);
+   }
+   
+   protected ToStringHelper string() {
+      return Objects.toStringHelper(this)
+            .add("fingerprint", fingerprint).add("name", name).add("privateKey", privateKey);
+   }
+   
+   @Override
    public String toString() {
-      return "SshKeyPair{" +
-            "fingerprint='" + fingerprint + '\'' +
-            ", name='" + name + '\'' +
-            ", privateKey='" + privateKey + '\'' +
-            '}';
-   }
-
-   @Override
-   public int compareTo(SshKeyPair arg0) {
-      return fingerprint.compareTo(arg0.getFingerprint());
+      return string().toString();
    }
 
 }
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/StoragePool.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/StoragePool.java
index 03471a0..aa1d8fc 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/StoragePool.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/StoragePool.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -20,17 +20,22 @@
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import java.beans.ConstructorProperties;
 import java.util.Date;
 
+import javax.inject.Named;
+
+import org.jclouds.javax.annotation.Nullable;
+
 import com.google.common.base.CaseFormat;
 import com.google.common.base.Objects;
-import com.google.gson.annotations.SerializedName;
+import com.google.common.base.Objects.ToStringHelper;
 
 /**
  * Represents a storage pool in CloudStack
- *
+ * 
  * @author Richard Downer
- */
+*/
 public class StoragePool implements Comparable<StoragePool> {
 
    public enum State {
@@ -56,7 +61,7 @@
       }
    }
 
-   public static enum Type {
+   public enum Type {
       FILESYSTEM,
       NETWORK_FILESYSTEM,
       ISCSI_LUN,
@@ -70,169 +75,264 @@
       EXT,
       OCFS2,
       UNRECOGNIZED;
-
+      
       @Override
       public String toString() {
-         return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, name());
+      return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, name());
       }
-
+      
       public static Type fromValue(String type) {
-         try {
-            return valueOf(CaseFormat.UPPER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, checkNotNull(type, "type")));
-         } catch (IllegalArgumentException e) {
-            return UNRECOGNIZED;
-         }
+      try {
+      return valueOf(CaseFormat.UPPER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, checkNotNull(type, "type")));
+      } catch (IllegalArgumentException e) {
+      return UNRECOGNIZED;
+      }
       }
    }
 
-   public static Builder builder() {
-      return new Builder();
+   public static Builder<?> builder() { 
+      return new ConcreteBuilder();
+   }
+   
+   public Builder<?> toBuilder() { 
+      return new ConcreteBuilder().fromStoragePool(this);
    }
 
-   public static class Builder {
+   public static abstract class Builder<T extends Builder<T>>  {
+      protected abstract T self();
 
-      private Builder() {
-      }
-
-      private String id;
-      private String name;
-      private String path;
-      private String tags;
-      private State state;
-      private Type type;
-      private String zoneId;
-      private String zoneName;
-      private String podId;
-      private String podName;
-      private String clusterId;
-      private String clusterName;
-      private Date created;
-      private long diskSizeAllocated;
-      private long diskSizeTotal;
-      private String ipAddress;
-      private String jobId;
-      private String jobStatus;
-
-      public Builder id(String id) {
+      protected String id;
+      protected String name;
+      protected String path;
+      protected String tags;
+      protected StoragePool.State state;
+      protected StoragePool.Type type;
+      protected String zoneId;
+      protected String zoneName;
+      protected String podId;
+      protected String podName;
+      protected String clusterId;
+      protected String clusterName;
+      protected Date created;
+      protected long diskSizeAllocated;
+      protected long diskSizeTotal;
+      protected String ipAddress;
+      protected String jobId;
+      protected String jobStatus;
+   
+      /** 
+       * @see StoragePool#getId()
+       */
+      public T id(String id) {
          this.id = id;
-         return this;
+         return self();
       }
 
-      public Builder name(String name) {
+      /** 
+       * @see StoragePool#getName()
+       */
+      public T name(String name) {
          this.name = name;
-         return this;
+         return self();
       }
 
-      public Builder path(String path) {
+      /** 
+       * @see StoragePool#getPath()
+       */
+      public T path(String path) {
          this.path = path;
-         return this;
+         return self();
       }
 
-      public Builder tags(String tags) {
+      /** 
+       * @see StoragePool#getTags()
+       */
+      public T tags(String tags) {
          this.tags = tags;
-         return this;
+         return self();
       }
 
-      public Builder state(State state) {
+      /** 
+       * @see StoragePool#getState()
+       */
+      public T state(StoragePool.State state) {
          this.state = state;
-         return this;
+         return self();
       }
 
-      public Builder type(Type type) {
+      /** 
+       * @see StoragePool#getType()
+       */
+      public T type(StoragePool.Type type) {
          this.type = type;
-         return this;
+         return self();
       }
 
-      public Builder zoneId(String zoneId) {
+      /** 
+       * @see StoragePool#getZoneId()
+       */
+      public T zoneId(String zoneId) {
          this.zoneId = zoneId;
-         return this;
+         return self();
       }
 
-      public Builder zoneName(String zoneName) {
+      /** 
+       * @see StoragePool#getZoneName()
+       */
+      public T zoneName(String zoneName) {
          this.zoneName = zoneName;
-         return this;
+         return self();
       }
 
-      public Builder podId(String podId) {
+      /** 
+       * @see StoragePool#getPodId()
+       */
+      public T podId(String podId) {
          this.podId = podId;
-         return this;
+         return self();
       }
 
-      public Builder podName(String podName) {
+      /** 
+       * @see StoragePool#getPodName()
+       */
+      public T podName(String podName) {
          this.podName = podName;
-         return this;
+         return self();
       }
 
-      public Builder clusterId(String clusterId) {
+      /** 
+       * @see StoragePool#getClusterId()
+       */
+      public T clusterId(String clusterId) {
          this.clusterId = clusterId;
-         return this;
+         return self();
       }
 
-      public Builder clusterName(String clusterName) {
+      /** 
+       * @see StoragePool#getClusterName()
+       */
+      public T clusterName(String clusterName) {
          this.clusterName = clusterName;
-         return this;
+         return self();
       }
 
-      public Builder created(Date created) {
+      /** 
+       * @see StoragePool#getCreated()
+       */
+      public T created(Date created) {
          this.created = created;
-         return this;
+         return self();
       }
 
-      public Builder diskSizeAllocated(long diskSizeAllocated) {
+      /** 
+       * @see StoragePool#getDiskSizeAllocated()
+       */
+      public T diskSizeAllocated(long diskSizeAllocated) {
          this.diskSizeAllocated = diskSizeAllocated;
-         return this;
+         return self();
       }
 
-      public Builder diskSizeTotal(long diskSizeTotal) {
+      /** 
+       * @see StoragePool#getDiskSizeTotal()
+       */
+      public T diskSizeTotal(long diskSizeTotal) {
          this.diskSizeTotal = diskSizeTotal;
-         return this;
+         return self();
       }
 
-      public Builder ipAddress(String ipAddress) {
+      /** 
+       * @see StoragePool#getIpAddress()
+       */
+      public T ipAddress(String ipAddress) {
          this.ipAddress = ipAddress;
-         return this;
+         return self();
       }
 
-      public Builder jobId(String jobId) {
+      /** 
+       * @see StoragePool#getJobId()
+       */
+      public T jobId(String jobId) {
          this.jobId = jobId;
-         return this;
+         return self();
       }
 
-      public Builder jobStatus(String jobStatus) {
+      /** 
+       * @see StoragePool#getJobStatus()
+       */
+      public T jobStatus(String jobStatus) {
          this.jobStatus = jobStatus;
-         return this;
+         return self();
       }
 
       public StoragePool build() {
          return new StoragePool(id, name, path, tags, state, type, zoneId, zoneName, podId, podName, clusterId, clusterName, created, diskSizeAllocated, diskSizeTotal, ipAddress, jobId, jobStatus);
       }
+      
+      public T fromStoragePool(StoragePool in) {
+         return this
+                  .id(in.getId())
+                  .name(in.getName())
+                  .path(in.getPath())
+                  .tags(in.getTags())
+                  .state(in.getState())
+                  .type(in.getType())
+                  .zoneId(in.getZoneId())
+                  .zoneName(in.getZoneName())
+                  .podId(in.getPodId())
+                  .podName(in.getPodName())
+                  .clusterId(in.getClusterId())
+                  .clusterName(in.getClusterName())
+                  .created(in.getCreated())
+                  .diskSizeAllocated(in.getDiskSizeAllocated())
+                  .diskSizeTotal(in.getDiskSizeTotal())
+                  .ipAddress(in.getIpAddress())
+                  .jobId(in.getJobId())
+                  .jobStatus(in.getJobStatus());
+      }
    }
 
-   private String id;
-   private String name;
-   private String path;
-   private String tags;
-   private State state;
-   private Type type;
-   @SerializedName("zoneid") private String zoneId;
-   @SerializedName("zonename") private String zoneName;
-   @SerializedName("podid") private String podId;
-   @SerializedName("podname") private String podName;
-   @SerializedName("clusterid") private String clusterId;
-   @SerializedName("clustername") private String clusterName;
-   private Date created;
-   @SerializedName("disksizeallocated") private long diskSizeAllocated;
-   @SerializedName("disksizetotal") private long diskSizeTotal;
-   @SerializedName("ipaddress") private String ipAddress;
-   @SerializedName("jobid") private String jobId;
-   @SerializedName("jobstatus") private String jobStatus;
-
-   /* Exists only for the serializer */
-   StoragePool() {
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
    }
 
-   public StoragePool(String id, String name, String path, String tags, State state, Type type, String zoneId, String zoneName, String podId, String podName, String clusterId, String clusterName, Date created, long diskSizeAllocated, long diskSizeTotal, String ipAddress, String jobId, String jobStatus) {
-      this.id = id;
+   private final String id;
+   private final String name;
+   private final String path;
+   private final String tags;
+   private final StoragePool.State state;
+   private final StoragePool.Type type;
+   @Named("zoneid")
+   private final String zoneId;
+   @Named("zonename")
+   private final String zoneName;
+   @Named("podid")
+   private final String podId;
+   @Named("podname")
+   private final String podName;
+   @Named("clusterid")
+   private final String clusterId;
+   @Named("clustername")
+   private final String clusterName;
+   private final Date created;
+   @Named("disksizeallocated")
+   private final long diskSizeAllocated;
+   @Named("disksizetotal")
+   private final long diskSizeTotal;
+   @Named("ipaddress")
+   private final String ipAddress;
+   @Named("jobid")
+   private final String jobId;
+   @Named("jobstatus")
+   private final String jobStatus;
+
+   @ConstructorProperties({
+      "id", "name", "path", "tags", "state", "type", "zoneid", "zonename", "podid", "podname", "clusterid", "clustername", "created", "disksizeallocated", "disksizetotal", "ipaddress", "jobid", "jobstatus"
+   })
+   protected StoragePool(String id, @Nullable String name, @Nullable String path, @Nullable String tags, @Nullable StoragePool.State state, @Nullable StoragePool.Type type, @Nullable String zoneId, @Nullable String zoneName, @Nullable String podId, @Nullable String podName, @Nullable String clusterId, @Nullable String clusterName, @Nullable Date created, long diskSizeAllocated, long diskSizeTotal, @Nullable String ipAddress, @Nullable String jobId, @Nullable String jobStatus) {
+      this.id = checkNotNull(id, "id");
       this.name = name;
       this.path = path;
       this.tags = tags;
@@ -253,137 +353,132 @@
    }
 
    public String getId() {
-      return id;
+      return this.id;
    }
 
+   @Nullable
    public String getName() {
-      return name;
+      return this.name;
    }
 
+   @Nullable
    public String getPath() {
-      return path;
+      return this.path;
    }
 
+   @Nullable
    public String getTags() {
-      return tags;
+      return this.tags;
    }
 
-   public State getState() {
-      return state;
+   @Nullable
+   public StoragePool.State getState() {
+      return this.state;
    }
 
-   public Type getType() {
-      return type;
+   @Nullable
+   public StoragePool.Type getType() {
+      return this.type;
    }
 
+   @Nullable
    public String getZoneId() {
-      return zoneId;
+      return this.zoneId;
    }
 
+   @Nullable
    public String getZoneName() {
-      return zoneName;
+      return this.zoneName;
    }
 
+   @Nullable
    public String getPodId() {
-      return podId;
+      return this.podId;
    }
 
+   @Nullable
    public String getPodName() {
-      return podName;
+      return this.podName;
    }
 
+   @Nullable
    public String getClusterId() {
-      return clusterId;
+      return this.clusterId;
    }
 
+   @Nullable
    public String getClusterName() {
-      return clusterName;
+      return this.clusterName;
    }
 
+   @Nullable
    public Date getCreated() {
-      return created;
+      return this.created;
    }
 
    public long getDiskSizeAllocated() {
-      return diskSizeAllocated;
+      return this.diskSizeAllocated;
    }
 
    public long getDiskSizeTotal() {
-      return diskSizeTotal;
+      return this.diskSizeTotal;
    }
 
+   @Nullable
    public String getIpAddress() {
-      return ipAddress;
+      return this.ipAddress;
    }
 
+   @Nullable
    public String getJobId() {
-      return jobId;
+      return this.jobId;
    }
 
+   @Nullable
    public String getJobStatus() {
-      return jobStatus;
-   }
-
-   @Override
-   public boolean equals(Object o) {
-      if (this == o) return true;
-      if (o == null || getClass() != o.getClass()) return false;
-
-      StoragePool that = (StoragePool) o;
-
-      if (!Objects.equal(clusterId, that.clusterId)) return false;
-      if (!Objects.equal(diskSizeAllocated, that.diskSizeAllocated)) return false;
-      if (!Objects.equal(diskSizeTotal, that.diskSizeTotal)) return false;
-      if (!Objects.equal(id, that.id)) return false;
-      if (!Objects.equal(podId, that.podId)) return false;
-      if (!Objects.equal(zoneId, that.zoneId)) return false;
-      if (!Objects.equal(clusterName, that.clusterName)) return false;
-      if (!Objects.equal(created, that.created)) return false;
-      if (!Objects.equal(ipAddress, that.ipAddress)) return false;
-      if (!Objects.equal(jobId, that.jobId)) return false;
-      if (!Objects.equal(jobStatus, that.jobStatus)) return false;
-      if (!Objects.equal(name, that.name)) return false;
-      if (!Objects.equal(path, that.path)) return false;
-      if (!Objects.equal(podName, that.podName)) return false;
-      if (!Objects.equal(state, that.state)) return false;
-      if (!Objects.equal(tags, that.tags)) return false;
-      if (!Objects.equal(type, that.type)) return false;
-      if (!Objects.equal(zoneName, that.zoneName)) return false;
-
-      return true;
+      return this.jobStatus;
    }
 
    @Override
    public int hashCode() {
-       return Objects.hashCode(clusterId, diskSizeAllocated, diskSizeTotal, id, podId, zoneId,
-                               clusterName, created, ipAddress, jobId, jobStatus, name, path,
-                               podName, state, tags, type, zoneName);
+      return Objects.hashCode(id, name, path, tags, state, type, zoneId, zoneName, podId, podName, clusterId, clusterName, created, diskSizeAllocated, diskSizeTotal, ipAddress, jobId, jobStatus);
    }
 
    @Override
-   public String toString() {
-      return "StoragePool{" +
-         "id=" + id +
-         ", name='" + name + '\'' +
-         ", path='" + path + '\'' +
-         ", tags='" + tags + '\'' +
-         ", state=" + state +
-         ", type=" + type +
-         ", zoneId=" + zoneId +
-         ", zoneName='" + zoneName + '\'' +
-         ", podId=" + podId +
-         ", podName='" + podName + '\'' +
-         ", clusterId=" + clusterId +
-         ", clusterName='" + clusterName + '\'' +
-         ", created=" + created +
-         ", diskSizeAllocated=" + diskSizeAllocated +
-         ", diskSizeTotal=" + diskSizeTotal +
-         ", ipAddress='" + ipAddress + '\'' +
-         ", jobId=" + jobId +
-         ", jobStatus='" + jobStatus + '\'' +
-         '}';
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      StoragePool that = StoragePool.class.cast(obj);
+      return Objects.equal(this.id, that.id)
+               && Objects.equal(this.name, that.name)
+               && Objects.equal(this.path, that.path)
+               && Objects.equal(this.tags, that.tags)
+               && Objects.equal(this.state, that.state)
+               && Objects.equal(this.type, that.type)
+               && Objects.equal(this.zoneId, that.zoneId)
+               && Objects.equal(this.zoneName, that.zoneName)
+               && Objects.equal(this.podId, that.podId)
+               && Objects.equal(this.podName, that.podName)
+               && Objects.equal(this.clusterId, that.clusterId)
+               && Objects.equal(this.clusterName, that.clusterName)
+               && Objects.equal(this.created, that.created)
+               && Objects.equal(this.diskSizeAllocated, that.diskSizeAllocated)
+               && Objects.equal(this.diskSizeTotal, that.diskSizeTotal)
+               && Objects.equal(this.ipAddress, that.ipAddress)
+               && Objects.equal(this.jobId, that.jobId)
+               && Objects.equal(this.jobStatus, that.jobStatus);
    }
-
+   
+   protected ToStringHelper string() {
+      return Objects.toStringHelper(this)
+            .add("id", id).add("name", name).add("path", path).add("tags", tags).add("state", state).add("type", type).add("zoneId", zoneId).add("zoneName", zoneName).add("podId", podId).add("podName", podName).add("clusterId", clusterId).add("clusterName", clusterName).add("created", created).add("diskSizeAllocated", diskSizeAllocated).add("diskSizeTotal", diskSizeTotal).add("ipAddress", ipAddress).add("jobId", jobId).add("jobStatus", jobStatus);
+   }
+   
+   @Override
+   public String toString() {
+      return string().toString();
+   }
+   
    @Override
    public int compareTo(StoragePool other) {
       return this.id.compareTo(other.id);
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Template.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Template.java
index 52fc1ef..92f382e 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Template.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Template.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -20,66 +20,69 @@
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import java.beans.ConstructorProperties;
 import java.util.Date;
 
-import javax.annotation.Nullable;
+import javax.inject.Named;
+
+import org.jclouds.javax.annotation.Nullable;
 
 import com.google.common.base.Objects;
-import com.google.gson.annotations.SerializedName;
+import com.google.common.base.Objects.ToStringHelper;
 
 /**
  * @author Adrian Cole
- */
+*/
 public class Template implements Comparable<Template> {
-   public static enum Status {
-
+   public enum Status {
+      
       /**
-       * status of download is not known. Example - When the job for downloading doesn't exist
-       * during progress check.
-       */
+      * status of download is not known. Example - When the job for downloading doesn't exist
+      * during progress check.
+      */
       UNKNOWN,
       /**
-       * the download has been cancelled/aborted.
-       */
+      * the download has been cancelled/aborted.
+      */
       ABANDONED,
       /**
-       * the download has reached an error state. Example - there is not route to ssvm agent
-       */
+      * the download has reached an error state. Example - there is not route to ssvm agent
+      */
       DOWNLOAD_ERROR,
       /**
-       * the download hasn't started.
-       */
+      * the download hasn't started.
+      */
       NOT_DOWNLOADED,
       /**
-       * the download is in progress
-       */
+      * the download is in progress
+      */
       DOWNLOAD_IN_PROGRESS,
       /**
-       * the resource has been downloaded on secondary storage.
-       */
+      * the resource has been downloaded on secondary storage.
+      */
       DOWNLOADED,
-
+      
       // These states are specifically used for extraction of resources out of CS(ironically shown
       // as download template in the UI, API - extractTemplate ). Some of the generic states (like
       // abandoned, unknown) above are used for the extraction tasks as well.
-
+      
       /**
-       * the resource has been uploaded
-       */
+      * the resource has been uploaded
+      */
       UPLOADED,
       /**
-       * the resource upload work hasn't started yet
-       */
+      * the resource upload work hasn't started yet
+      */
       NOT_UPLOADED,
       /**
-       * the resource upload has reached error.
-       */
+      * the resource upload has reached error.
+      */
       UPLOAD_ERROR,
       /**
-       * the resource upload is in progress.
-       */
+      * the resource upload is in progress.
+      */
       UPLOAD_IN_PROGRESS, UNRECOGNIZED;
-
+      
       public static Status fromValue(String state) {
          if (state.equals("Download Complete")) {
             return DOWNLOADED;
@@ -90,309 +93,449 @@
             return UNRECOGNIZED;
          }
       }
-
-   }
-   public static Builder builder() {
-      return new Builder();
+      
    }
 
-   public static class Builder {
-      private String id;
-      private String displayText;
-      private String domain;
-      private String domainId;
-      private String account;
-      private String accountId;
-      private String zone;
-      private String zoneId;
-      private String OSType;
-      private String OSTypeId;
-      private String name;
-      private Type type;
-      private Status status;
-      private Format format;
-      private String hypervisor;
-      private Long size;
-      private Date created;
-      private Date removed;
-      private boolean crossZones;
-      private boolean bootable;
-      private boolean extractable;
-      private boolean featured;
-      private boolean isPublic;
-      private boolean ready;
-      private boolean passwordEnabled;
-      private String jobId;
-      private String jobStatus;
-      private String checksum;
-      private String hostId;
-      private String hostName;
-      private String sourceTemplateId;
-      private String templateTag;
-
-      public Builder id(String id) {
-         this.id = id;
-         return this;
-      }
-
-      public Builder displayText(String displayText) {
-         this.displayText = displayText;
-         return this;
-      }
-
-      public Builder domain(String domain) {
-         this.domain = domain;
-         return this;
-      }
-
-      public Builder domainId(String domainId) {
-         this.domainId = domainId;
-         return this;
-      }
-
-      public Builder account(String account) {
-         this.account = account;
-         return this;
-      }
-
-      public Builder accountId(String accountId) {
-         this.accountId = accountId;
-         return this;
-      }
-
-      public Builder zone(String zone) {
-         this.zone = zone;
-         return this;
-      }
-
-      public Builder zoneId(String zoneId) {
-         this.zoneId = zoneId;
-         return this;
-      }
-
-      public Builder OSType(String OSType) {
-         this.OSType = OSType;
-         return this;
-      }
-
-      public Builder OSTypeId(String OSTypeId) {
-         this.OSTypeId = OSTypeId;
-         return this;
-      }
-
-      public Builder name(String name) {
-         this.name = name;
-         return this;
-      }
-
-      public Builder type(Type type) {
-         this.type = type;
-         return this;
-      }
-
-      public Builder status(Status status) {
-         this.status = status;
-         return this;
-      }
-
-      public Builder format(Format format) {
-         this.format = format;
-         return this;
-      }
-
-      public Builder hypervisor(String hypervisor) {
-         this.hypervisor = hypervisor;
-         return this;
-      }
-
-      public Builder size(Long size) {
-         this.size = size;
-         return this;
-      }
-
-      public Builder created(Date created) {
-         this.created = created;
-         return this;
-      }
-
-      public Builder removed(Date removed) {
-         this.removed = removed;
-         return this;
-      }
-
-      public Builder crossZones(boolean crossZones) {
-         this.crossZones = crossZones;
-         return this;
-      }
-
-      public Builder bootable(boolean bootable) {
-         this.bootable = bootable;
-         return this;
-      }
-
-      public Builder extractable(boolean extractable) {
-         this.extractable = extractable;
-         return this;
-      }
-
-      public Builder featured(boolean featured) {
-         this.featured = featured;
-         return this;
-      }
-
-      public Builder isPublic(boolean isPublic) {
-         this.isPublic = isPublic;
-         return this;
-      }
-
-      public Builder ready(boolean ready) {
-         this.ready = ready;
-         return this;
-      }
-
-      public Builder passwordEnabled(boolean passwordEnabled) {
-         this.passwordEnabled = passwordEnabled;
-         return this;
-      }
-
-      public Builder jobId(String jobId) {
-         this.jobId = jobId;
-         return this;
-      }
-
-      public Builder jobStatus(String jobStatus) {
-         this.jobStatus = jobStatus;
-         return this;
-      }
-
-      public Builder checksum(String checksum) {
-         this.checksum = checksum;
-         return this;
-      }
-
-      public Builder hostid(String hostid) {
-         this.hostId = hostid;
-         return this;
-      }
-
-      public Builder hostName(String hostName) {
-         this.hostName = hostName;
-         return this;
-      }
-
-      public Builder sourceTemplateId(String sourceTemplateId) {
-         this.sourceTemplateId = sourceTemplateId;
-         return this;
-      }
-
-      public Builder templateTag(String templateTag) {
-         this.templateTag = templateTag;
-         return this;
-      }
-
-
-      public Template build() {
-         return new Template(id, displayText, domain, domainId, account, accountId, zone, zoneId, OSType, OSTypeId,
-               name, type, status, format, hypervisor, size, created, removed, crossZones, bootable, extractable,
-               featured, isPublic, ready, passwordEnabled, jobId, jobStatus, checksum, hostId, hostName, sourceTemplateId,
-               templateTag);
-      }
-
-   }
-
-   public enum Type {
-
+   public static enum Type {
+      
       USER, BUILTIN, UNRECOGNIZED;
-
+      
       //TODO do we need camel case routines (e.g. see enums in VirtualMachine) ?
       public static Type fromValue(String type) {
-         try {
-            return valueOf(checkNotNull(type, "type"));
-         } catch (IllegalArgumentException e) {
-            return UNRECOGNIZED;
-         }
+      try {
+      return valueOf(checkNotNull(type, "type"));
+      } catch (IllegalArgumentException e) {
+      return UNRECOGNIZED;
+      }
       }
    }
 
    public enum Format {
-
+      
       VHD, QCOW2, OVA, UNRECOGNIZED;
-
+      
       public static Format fromValue(String format) {
-         try {
-            return valueOf(checkNotNull(format, "format"));
-         } catch (IllegalArgumentException e) {
-            return UNRECOGNIZED;
-         }
+      try {
+      return valueOf(checkNotNull(format, "format"));
+      } catch (IllegalArgumentException e) {
+      return UNRECOGNIZED;
+      }
       }
    }
 
-   private String id;
-   @SerializedName("displaytext")
-   private String displayText;
-   private String domain;
-   @SerializedName("domainid")
-   private String domainId;
-   private String account;
-   @SerializedName("accountid")
-   private String accountId;
-   @SerializedName("zonename")
-   private String zone;
-   @SerializedName("zoneid")
-   private String zoneId;
-   @SerializedName("ostypename")
-   private String OSType;
-   @SerializedName("ostypeid")
-   private String OSTypeId;
-   private String name;
-   @SerializedName("templatetype")
-   private Type type;
-   //TODO: this should be a type
-   private Status status;
-   private Format format;
-   private String hypervisor;
-   private Long size;
-   private Date created;
-   private Date removed;
-   @SerializedName("crossZones")
-   private boolean crossZones;
-   @SerializedName("bootable")
-   private boolean bootable;
-   @SerializedName("isextractable")
-   private boolean extractable;
-   @SerializedName("isfeatured")
-   private boolean featured;
-   @SerializedName("ispublic")
-   private boolean ispublic;
-   @SerializedName("isready")
-   private boolean ready;
-   @SerializedName("passwordenabled")
-   private boolean passwordEnabled;
-   @Nullable
-   @SerializedName("jobid")
-   private String jobId;
-   @SerializedName("jobstatus")
-   //TODO: this should be a type
-   private String jobStatus;
-   private String checksum;
-   @SerializedName("hostId")
-   private String hostId;
-   @SerializedName("hostname")
-   private String hostName;
-   @SerializedName("sourcetemplateid")
-   @Nullable
-   private String sourceTemplateId;
-   @SerializedName("templatetag")
-   private String templateTag;
+   public static Builder<?> builder() { 
+      return new ConcreteBuilder();
+   }
+   
+   public Builder<?> toBuilder() { 
+      return new ConcreteBuilder().fromTemplate(this);
+   }
 
+   public static abstract class Builder<T extends Builder<T>>  {
+      protected abstract T self();
 
-   public Template(String id, String displayText, String domain, String domainId, String account, String accountId,
-                   String zone, String zoneId, String oSType, String oSTypeId, String name, Type type, Status status, Format format,
-                   String hypervisor, Long size, Date created, Date removed, boolean crossZones, boolean bootable,
-                   boolean extractable, boolean featured, boolean ispublic, boolean ready, boolean passwordEnabled, String jobId,
-                   String jobStatus, String checksum, String hostId, String hostName, String sourceTemplateId,
-                   String templateTag) {
-      this.id = id;
+      protected String id;
+      protected String displayText;
+      protected String domain;
+      protected String domainId;
+      protected String account;
+      protected String accountId;
+      protected String zone;
+      protected String zoneId;
+      protected String OSType;
+      protected String OSTypeId;
+      protected String name;
+      protected Template.Type type;
+      protected Template.Status status;
+      protected Template.Format format;
+      protected String hypervisor;
+      protected Long size;
+      protected Date created;
+      protected Date removed;
+      protected boolean crossZones;
+      protected boolean bootable;
+      protected boolean extractable;
+      protected boolean featured;
+      protected boolean isPublic;
+      protected boolean ready;
+      protected boolean passwordEnabled;
+      protected String jobId;
+      protected String jobStatus;
+      protected String checksum;
+      protected String hostId;
+      protected String hostName;
+      protected String sourceTemplateId;
+      protected String templateTag;
+   
+      /** 
+       * @see Template#getId()
+       */
+      public T id(String id) {
+         this.id = id;
+         return self();
+      }
+
+      /** 
+       * @see Template#getDisplayText()
+       */
+      public T displayText(String displayText) {
+         this.displayText = displayText;
+         return self();
+      }
+
+      /** 
+       * @see Template#getDomain()
+       */
+      public T domain(String domain) {
+         this.domain = domain;
+         return self();
+      }
+
+      /** 
+       * @see Template#getDomainId()
+       */
+      public T domainId(String domainId) {
+         this.domainId = domainId;
+         return self();
+      }
+
+      /** 
+       * @see Template#getAccount()
+       */
+      public T account(String account) {
+         this.account = account;
+         return self();
+      }
+
+      /** 
+       * @see Template#getAccountId()
+       */
+      public T accountId(String accountId) {
+         this.accountId = accountId;
+         return self();
+      }
+
+      /** 
+       * @see Template#getZone()
+       */
+      public T zone(String zone) {
+         this.zone = zone;
+         return self();
+      }
+
+      /** 
+       * @see Template#getZoneId()
+       */
+      public T zoneId(String zoneId) {
+         this.zoneId = zoneId;
+         return self();
+      }
+
+      /** 
+       * @see Template#getOSType()
+       */
+      public T OSType(String OSType) {
+         this.OSType = OSType;
+         return self();
+      }
+
+      /** 
+       * @see Template#getOSTypeId()
+       */
+      public T OSTypeId(String OSTypeId) {
+         this.OSTypeId = OSTypeId;
+         return self();
+      }
+
+      /** 
+       * @see Template#getName()
+       */
+      public T name(String name) {
+         this.name = name;
+         return self();
+      }
+
+      /** 
+       * @see Template#getType()
+       */
+      public T type(Template.Type type) {
+         this.type = type;
+         return self();
+      }
+
+      /** 
+       * @see Template#getStatus()
+       */
+      public T status(Template.Status status) {
+         this.status = status;
+         return self();
+      }
+
+      /** 
+       * @see Template#getFormat()
+       */
+      public T format(Template.Format format) {
+         this.format = format;
+         return self();
+      }
+
+      /** 
+       * @see Template#getHypervisor()
+       */
+      public T hypervisor(String hypervisor) {
+         this.hypervisor = hypervisor;
+         return self();
+      }
+
+      /** 
+       * @see Template#getSize()
+       */
+      public T size(Long size) {
+         this.size = size;
+         return self();
+      }
+
+      /** 
+       * @see Template#getCreated()
+       */
+      public T created(Date created) {
+         this.created = created;
+         return self();
+      }
+
+      /** 
+       * @see Template#getRemoved()
+       */
+      public T removed(Date removed) {
+         this.removed = removed;
+         return self();
+      }
+
+      /** 
+       * @see Template#isCrossZones()
+       */
+      public T crossZones(boolean crossZones) {
+         this.crossZones = crossZones;
+         return self();
+      }
+
+      /** 
+       * @see Template#isBootable()
+       */
+      public T bootable(boolean bootable) {
+         this.bootable = bootable;
+         return self();
+      }
+
+      /** 
+       * @see Template#isExtractable()
+       */
+      public T extractable(boolean extractable) {
+         this.extractable = extractable;
+         return self();
+      }
+
+      /** 
+       * @see Template#isFeatured()
+       */
+      public T featured(boolean featured) {
+         this.featured = featured;
+         return self();
+      }
+
+      /** 
+       * @see Template#ispublic()
+       */
+      public T isPublic(boolean isPublic) {
+         this.isPublic = isPublic;
+         return self();
+      }
+
+      /** 
+       * @see Template#isReady()
+       */
+      public T ready(boolean ready) {
+         this.ready = ready;
+         return self();
+      }
+
+      /** 
+       * @see Template#isPasswordEnabled()
+       */
+      public T passwordEnabled(boolean passwordEnabled) {
+         this.passwordEnabled = passwordEnabled;
+         return self();
+      }
+
+      /** 
+       * @see Template#getJobId()
+       */
+      public T jobId(String jobId) {
+         this.jobId = jobId;
+         return self();
+      }
+
+      /** 
+       * @see Template#getJobStatus()
+       */
+      public T jobStatus(String jobStatus) {
+         this.jobStatus = jobStatus;
+         return self();
+      }
+
+      /** 
+       * @see Template#getChecksum()
+       */
+      public T checksum(String checksum) {
+         this.checksum = checksum;
+         return self();
+      }
+
+      /** 
+       * @see Template#getHostId()
+       */
+      public T hostId(String hostId) {
+         this.hostId = hostId;
+         return self();
+      }
+
+      /** 
+       * @see Template#getHostName()
+       */
+      public T hostName(String hostName) {
+         this.hostName = hostName;
+         return self();
+      }
+
+      /** 
+       * @see Template#getSourceTemplateId()
+       */
+      public T sourceTemplateId(String sourceTemplateId) {
+         this.sourceTemplateId = sourceTemplateId;
+         return self();
+      }
+
+      /** 
+       * @see Template#getTemplateTag()
+       */
+      public T templateTag(String templateTag) {
+         this.templateTag = templateTag;
+         return self();
+      }
+
+      public Template build() {
+         return new Template(id, displayText, domain, domainId, account, accountId, zone, zoneId, OSType, OSTypeId, name, type, status, format, hypervisor, size, created, removed, crossZones, bootable, extractable, featured, isPublic, ready, passwordEnabled, jobId, jobStatus, checksum, hostId, hostName, sourceTemplateId, templateTag);
+      }
+      
+      public T fromTemplate(Template in) {
+         return this
+                  .id(in.getId())
+                  .displayText(in.getDisplayText())
+                  .domain(in.getDomain())
+                  .domainId(in.getDomainId())
+                  .account(in.getAccount())
+                  .accountId(in.getAccountId())
+                  .zone(in.getZone())
+                  .zoneId(in.getZoneId())
+                  .OSType(in.getOSType())
+                  .OSTypeId(in.getOSTypeId())
+                  .name(in.getName())
+                  .type(in.getType())
+                  .status(in.getStatus())
+                  .format(in.getFormat())
+                  .hypervisor(in.getHypervisor())
+                  .size(in.getSize())
+                  .created(in.getCreated())
+                  .removed(in.getRemoved())
+                  .crossZones(in.isCrossZones())
+                  .bootable(in.isBootable())
+                  .extractable(in.isExtractable())
+                  .featured(in.isFeatured())
+                  .isPublic(in.ispublic())
+                  .ready(in.isReady())
+                  .passwordEnabled(in.isPasswordEnabled())
+                  .jobId(in.getJobId())
+                  .jobStatus(in.getJobStatus())
+                  .checksum(in.getChecksum())
+                  .hostId(in.getHostId())
+                  .hostName(in.getHostName())
+                  .sourceTemplateId(in.getSourceTemplateId())
+                  .templateTag(in.getTemplateTag());
+      }
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
+   }
+
+   private final String id;
+   @Named("displaytext")
+   private final String displayText;
+   private final String domain;
+   @Named("domainid")
+   private final String domainId;
+   private final String account;
+   @Named("accountid")
+   private final String accountId;
+   @Named("zonename")
+   private final String zone;
+   @Named("zoneid")
+   private final String zoneId;
+   @Named("ostypename")
+   private final String OSType;
+   @Named("ostypeid")
+   private final String OSTypeId;
+   private final String name;
+   @Named("templatetype")
+   private final Template.Type type;
+   private final Template.Status status;
+   private final Template.Format format;
+   private final String hypervisor;
+   private final Long size;
+   private final Date created;
+   private final Date removed;
+   private final boolean crossZones;
+   private final boolean bootable;
+   @Named("isextractable")
+   private final boolean extractable;
+   @Named("isfeatured")
+   private final boolean featured;
+   private final boolean ispublic;
+   @Named("isready")
+   private final boolean ready;
+   @Named("passwordenabled")
+   private final boolean passwordEnabled;
+   @Named("jobid")
+   private final String jobId;
+   @Named("jobstatus")
+   private final String jobStatus;
+   private final String checksum;
+   private final String hostId;
+   @Named("hostname")
+   private final String hostName;
+   @Named("sourcetemplateid")
+   private final String sourceTemplateId;
+   @Named("templatetag")
+   private final String templateTag;
+
+   @ConstructorProperties({
+      "id", "displaytext", "domain", "domainid", "account", "accountid", "zonename", "zoneid", "ostypename", "ostypeid",
+      "name", "templatetype", "status", "format", "hypervisor", "size", "created", "removed", "crossZones", "bootable",
+      "isextractable", "isfeatured", "ispublic", "isready", "passwordenabled", "jobid", "jobstatus", "checksum", "hostId",
+      "hostname", "sourcetemplateid", "templatetag"
+   })
+   protected Template(String id, @Nullable String displayText, @Nullable String domain, @Nullable String domainId,
+                      @Nullable String account, @Nullable String accountId, @Nullable String zone, @Nullable String zoneId,
+                      @Nullable String OSType, @Nullable String OSTypeId, @Nullable String name, @Nullable Template.Type type,
+                      @Nullable Template.Status status, @Nullable Template.Format format, @Nullable String hypervisor,
+                      @Nullable Long size, @Nullable Date created, @Nullable Date removed, boolean crossZones,
+                      boolean bootable, boolean extractable, boolean featured, boolean ispublic, boolean ready, boolean passwordEnabled,
+                      @Nullable String jobId, @Nullable String jobStatus, @Nullable String checksum, @Nullable String hostId,
+                      @Nullable String hostName, @Nullable String sourceTemplateId, @Nullable String templateTag) {
+      this.id = checkNotNull(id, "id");
       this.displayText = displayText;
       this.domain = domain;
       this.domainId = domainId;
@@ -400,8 +543,8 @@
       this.accountId = accountId;
       this.zone = zone;
       this.zoneId = zoneId;
-      this.OSType = oSType;
-      this.OSTypeId = oSTypeId;
+      this.OSType = OSType;
+      this.OSTypeId = OSTypeId;
       this.name = name;
       this.type = type;
       this.status = status;
@@ -424,335 +567,312 @@
       this.hostName = hostName;
       this.sourceTemplateId = sourceTemplateId;
       this.templateTag = templateTag;
-
-   }
-
-   /**
-    * present only for serializer
-    */
-   Template() {
-
    }
 
    /**
     * @return Template id
     */
    public String getId() {
-      return id;
+      return this.id;
    }
 
    /**
     * @return the display text of the template
     */
+   @Nullable
    public String getDisplayText() {
-      return displayText;
+      return this.displayText;
    }
 
    /**
     * @return the name of the domain to which the template beLongs
     */
+   @Nullable
    public String getDomain() {
-      return domain;
+      return this.domain;
    }
 
    /**
     * @return the ID of the domain to which the template beLongs
     */
+   @Nullable
    public String getDomainId() {
-      return domainId;
+      return this.domainId;
    }
 
    /**
     * @return the name of the account to which the template beLongs
     */
+   @Nullable
    public String getAccount() {
-      return account;
+      return this.account;
    }
 
    /**
     * @return the ID of the account to which the template beLongs
     */
+   @Nullable
    public String getAccountId() {
-      return accountId;
+      return this.accountId;
    }
 
    /**
     * @return the name of the zone to which the template beLongs
     */
+   @Nullable
    public String getZone() {
-      return zone;
+      return this.zone;
    }
 
    /**
     * @return the ID of the zone to which the template beLongs
     */
+   @Nullable
    public String getZoneId() {
-      return zoneId;
+      return this.zoneId;
    }
 
    /**
     * @return the name of the OS type to which the template beLongs
     */
+   @Nullable
    public String getOSType() {
-      return OSType;
+      return this.OSType;
    }
 
    /**
     * @return the ID of the OS type to which the template beLongs
     */
+   @Nullable
    public String getOSTypeId() {
-      return OSTypeId;
+      return this.OSTypeId;
    }
 
    /**
     * @return Template name
     */
+   @Nullable
    public String getName() {
-      return name;
-   }
-
-   /**
-    * @return status of the template
-    */
-   public Status getStatus() {
-      return status;
-   }
-
-   /**
-    * @return the format of the template.
-    */
-   public Format getFormat() {
-      return format;
-   }
-
-   /**
-    * @return the hypervisor on which the template runs
-    */
-   public String getHypervisor() {
-      return hypervisor;
-   }
-
-   /**
-    * @return the size of the template in kilobytes
-    */
-   public Long getSize() {
-      return size;
+      return this.name;
    }
 
    /**
     * @return the type of the template
     */
-   public Type getType() {
-      return type;
+   @Nullable
+   public Template.Type getType() {
+      return this.type;
+   }
+
+   /**
+    * @return status of the template
+    */
+   @Nullable
+   public Template.Status getStatus() {
+      return this.status;
+   }
+
+   /**
+    * @return the format of the template.
+    */
+   @Nullable
+   public Template.Format getFormat() {
+      return this.format;
+   }
+
+   /**
+    * @return the hypervisor on which the template runs
+    */
+   @Nullable
+   public String getHypervisor() {
+      return this.hypervisor;
+   }
+
+   /**
+    * @return the size of the template in kilobytes
+    */
+   @Nullable
+   public Long getSize() {
+      return this.size;
    }
 
    /**
     * @return the date this template was created
     */
+   @Nullable
    public Date getCreated() {
-      return created;
+      return this.created;
    }
 
    /**
     * @return the date this template was removed
     */
+   @Nullable
    public Date getRemoved() {
-      return removed;
+      return this.removed;
    }
 
    /**
     * @return true if the template is managed across all Zones, false otherwise
     */
    public boolean isCrossZones() {
-      return crossZones;
+      return this.crossZones;
    }
 
    /**
     * @return true if the ISO is bootable, false otherwise
     */
    public boolean isBootable() {
-      return bootable;
+      return this.bootable;
    }
 
    /**
     * @return true if the template is extractable, false otherwise
     */
    public boolean isExtractable() {
-      return extractable;
+      return this.extractable;
    }
 
    /**
     * @return true if this template is a featured template, false otherwise
     */
    public boolean isFeatured() {
-      return featured;
+      return this.featured;
    }
 
-   /**
-    * @return true if this template is a public template, false otherwise
-    */
-   public boolean isPublic() {
-      return ispublic;
+   public boolean ispublic() {
+      return this.ispublic;
    }
 
    /**
     * @return true if the template is ready to be deployed from, false otherwise
     */
    public boolean isReady() {
-      return ready;
+      return this.ready;
    }
 
    /**
     * @return true if the reset password feature is enabled, false otherwise
     */
    public boolean isPasswordEnabled() {
-      return passwordEnabled;
+      return this.passwordEnabled;
    }
 
    /**
     * @return shows the current pending asynchronous job ID, or null if current
-    *         pending jobs are acting on the template
+         pending jobs are acting on the template
     */
    @Nullable
    public String getJobId() {
-      return jobId;
+      return this.jobId;
    }
 
    /**
     * @return shows the current pending asynchronous job status
     */
+   @Nullable
    public String getJobStatus() {
-      return jobStatus;
+      return this.jobStatus;
    }
 
    /**
     * @return checksum of the template
     */
+   @Nullable
    public String getChecksum() {
-      return checksum;
+      return this.checksum;
    }
 
    /**
     * @return the ID of the secondary storage host for the template
     */
+   @Nullable
    public String getHostId() {
-      return hostId;
+      return this.hostId;
    }
 
    /**
     * @return the name of the secondary storage host for the template
     */
+   @Nullable
    public String getHostName() {
-      return hostName;
+      return this.hostName;
    }
 
    /**
     * @return the template ID of the parent template if present
     */
+   @Nullable
    public String getSourceTemplateId() {
-      return sourceTemplateId;
+      return this.sourceTemplateId;
    }
 
    /**
     * @return the tag of this template
     */
+   @Nullable
    public String getTemplateTag() {
-      return templateTag;
-   }
-
-   @Override
-   public boolean equals(Object o) {
-      if (this == o) return true;
-      if (o == null || getClass() != o.getClass()) return false;
-
-      Template that = (Template) o;
-
-      if (!Objects.equal(id, that.id)) return false;
-      if (!Objects.equal(displayText, that.displayText)) return false;
-      if (!Objects.equal(domain, that.domain)) return false;
-      if (!Objects.equal(domainId, that.domainId)) return false;
-      if (!Objects.equal(account, that.account)) return false;
-      if (!Objects.equal(accountId, that.accountId)) return false;
-      if (!Objects.equal(zone, that.zone)) return false;
-      if (!Objects.equal(zoneId, that.zoneId)) return false;
-      if (!Objects.equal(OSType, that.OSType)) return false;
-      if (!Objects.equal(OSTypeId, that.OSTypeId)) return false;
-      if (!Objects.equal(name, that.name)) return false;
-      if (!Objects.equal(type, that.type)) return false;
-      if (!Objects.equal(status, that.status)) return false;
-      if (!Objects.equal(format, that.format)) return false;
-      if (!Objects.equal(hypervisor, that.hypervisor)) return false;
-      if (!Objects.equal(size, that.size)) return false;
-      if (!Objects.equal(created, that.created)) return false;
-      if (!Objects.equal(removed, that.removed)) return false;
-      if (!Objects.equal(crossZones, that.crossZones)) return false;
-      if (!Objects.equal(bootable, that.bootable)) return false;
-      if (!Objects.equal(extractable, that.extractable)) return false;
-      if (!Objects.equal(featured, that.featured)) return false;
-      if (!Objects.equal(ispublic, that.ispublic)) return false;
-      if (!Objects.equal(ready, that.ready)) return false;
-      if (!Objects.equal(passwordEnabled, that.passwordEnabled)) return false;
-      if (!Objects.equal(jobId, that.jobId)) return false;
-      if (!Objects.equal(jobStatus, that.jobStatus)) return false;
-      if (!Objects.equal(checksum, that.checksum)) return false;
-      if (!Objects.equal(hostId, that.hostId)) return false;
-      if (!Objects.equal(hostName, that.hostName)) return false;
-      if (!Objects.equal(sourceTemplateId, that.sourceTemplateId)) return false;
-      if (!Objects.equal(templateTag, that.templateTag)) return false;
-
-      return true;
+      return this.templateTag;
    }
 
    @Override
    public int hashCode() {
-       return Objects.hashCode(id, displayText, domain, domainId, account, accountId,
-                               zone, zoneId, OSType, OSTypeId, name, type, status, format,
-                               hypervisor, size, created, removed, crossZones, bootable,
-                               extractable, featured, ispublic, ready, passwordEnabled,
-                               jobId, jobStatus, checksum, hostId, hostName,
-                               sourceTemplateId, templateTag);
+      return Objects.hashCode(id, displayText, domain, domainId, account, accountId, zone, zoneId, OSType, OSTypeId, name, type, status, format, hypervisor, size, created, removed, crossZones, bootable, extractable, featured, ispublic, ready, passwordEnabled, jobId, jobStatus, checksum, hostId, hostName, sourceTemplateId, templateTag);
    }
 
    @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      Template that = Template.class.cast(obj);
+      return Objects.equal(this.id, that.id)
+               && Objects.equal(this.displayText, that.displayText)
+               && Objects.equal(this.domain, that.domain)
+               && Objects.equal(this.domainId, that.domainId)
+               && Objects.equal(this.account, that.account)
+               && Objects.equal(this.accountId, that.accountId)
+               && Objects.equal(this.zone, that.zone)
+               && Objects.equal(this.zoneId, that.zoneId)
+               && Objects.equal(this.OSType, that.OSType)
+               && Objects.equal(this.OSTypeId, that.OSTypeId)
+               && Objects.equal(this.name, that.name)
+               && Objects.equal(this.type, that.type)
+               && Objects.equal(this.status, that.status)
+               && Objects.equal(this.format, that.format)
+               && Objects.equal(this.hypervisor, that.hypervisor)
+               && Objects.equal(this.size, that.size)
+               && Objects.equal(this.created, that.created)
+               && Objects.equal(this.removed, that.removed)
+               && Objects.equal(this.crossZones, that.crossZones)
+               && Objects.equal(this.bootable, that.bootable)
+               && Objects.equal(this.extractable, that.extractable)
+               && Objects.equal(this.featured, that.featured)
+               && Objects.equal(this.ispublic, that.ispublic)
+               && Objects.equal(this.ready, that.ready)
+               && Objects.equal(this.passwordEnabled, that.passwordEnabled)
+               && Objects.equal(this.jobId, that.jobId)
+               && Objects.equal(this.jobStatus, that.jobStatus)
+               && Objects.equal(this.checksum, that.checksum)
+               && Objects.equal(this.hostId, that.hostId)
+               && Objects.equal(this.hostName, that.hostName)
+               && Objects.equal(this.sourceTemplateId, that.sourceTemplateId)
+               && Objects.equal(this.templateTag, that.templateTag);
+   }
+   
+   protected ToStringHelper string() {
+      return Objects.toStringHelper(this)
+            .add("id", id).add("displayText", displayText).add("domain", domain).add("domainId", domainId).add("account", account).add("accountId", accountId).add("zone", zone).add("zoneId", zoneId).add("OSType", OSType).add("OSTypeId", OSTypeId).add("name", name).add("type", type).add("status", status).add("format", format).add("hypervisor", hypervisor).add("size", size).add("created", created).add("removed", removed).add("crossZones", crossZones).add("bootable", bootable).add("extractable", extractable).add("featured", featured).add("ispublic", ispublic).add("ready", ready).add("passwordEnabled", passwordEnabled).add("jobId", jobId).add("jobStatus", jobStatus).add("checksum", checksum).add("hostId", hostId).add("hostName", hostName).add("sourceTemplateId", sourceTemplateId).add("templateTag", templateTag);
+   }
+   
+   @Override
    public String toString() {
-      return "Template{" +
-            "id=" + id +
-            ", displayText='" + displayText + '\'' +
-            ", domain='" + domain + '\'' +
-            ", domainId=" + domainId +
-            ", account='" + account + '\'' +
-            ", accountId=" + accountId +
-            ", zone='" + zone + '\'' +
-            ", zoneId=" + zoneId +
-            ", OSType='" + OSType + '\'' +
-            ", OSTypeId=" + OSTypeId +
-            ", name='" + name + '\'' +
-            ", type=" + type +
-            ", status='" + status + '\'' +
-            ", format=" + format +
-            ", hypervisor='" + hypervisor + '\'' +
-            ", size=" + size +
-            ", created=" + created +
-            ", removed=" + removed +
-            ", crossZones=" + crossZones +
-            ", bootable=" + bootable +
-            ", extractable=" + extractable +
-            ", featured=" + featured +
-            ", ispublic=" + ispublic +
-            ", ready=" + ready +
-            ", passwordEnabled=" + passwordEnabled +
-            ", jobId=" + jobId +
-            ", jobStatus='" + jobStatus + '\'' +
-            ", checksum='" + checksum + '\'' +
-            ", hostId=" + hostId +
-            ", hostName='" + hostName + '\'' +
-            ", sourceTemplateId=" + sourceTemplateId +
-            ", templateTag='" + templateTag + '\'' +
-            '}';
+      return string().toString();
    }
 
+
    @Override
-   public int compareTo(Template arg0) {
-      return id.compareTo(arg0.getId());
+   public int compareTo(Template o) {
+      return id.compareTo(o.getId());
    }
+
 }
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/TemplateExtraction.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/TemplateExtraction.java
index 57e9c87..aa83a57 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/TemplateExtraction.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/TemplateExtraction.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -18,172 +18,208 @@
  */
 package org.jclouds.cloudstack.domain;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.beans.ConstructorProperties;
 import java.util.Date;
 
+import javax.inject.Named;
+
+import org.jclouds.javax.annotation.Nullable;
+
 import com.google.common.base.Objects;
-import com.google.gson.annotations.SerializedName;
+import com.google.common.base.Objects.ToStringHelper;
 
 /**
  * @author Richard Downer
- */
+*/
 public class TemplateExtraction implements Comparable<TemplateExtraction> {
 
-   public static Builder builder() {
-      return new Builder();
+   public static Builder<?> builder() { 
+      return new ConcreteBuilder();
+   }
+   
+   public Builder<?> toBuilder() { 
+      return new ConcreteBuilder().fromTemplateExtraction(this);
    }
 
-   public static class Builder {
+   public static abstract class Builder<T extends Builder<T>>  {
+      protected abstract T self();
 
-      private String id;
-      private String accountId;
-      private Date created;
-      private String extractId;
-      private ExtractMode extractMode;
-      private String name;
-      private String state;
-      private String status;
-      private String storageType;
-      private int uploadPercentage;
-      private String url;
-      private String zoneId;
-      private String zoneName;
-
-      /**
-       * @param id the id of extracted object
+      protected String id;
+      protected String accountId;
+      protected Date created;
+      protected String extractId;
+      protected ExtractMode extractMode;
+      protected String name;
+      protected String state;
+      protected String status;
+      protected String storageType;
+      protected int uploadPercentage;
+      protected String url;
+      protected String zoneId;
+      protected String zoneName;
+   
+      /** 
+       * @see TemplateExtraction#getId()
        */
-      public Builder id(String id) {
+      public T id(String id) {
          this.id = id;
-         return this;
+         return self();
       }
 
-      /**
-       * @param accountId the account id to which the extracted object belongs
+      /** 
+       * @see TemplateExtraction#getAccountId()
        */
-      public Builder accountId(String accountId) {
+      public T accountId(String accountId) {
          this.accountId = accountId;
-         return this;
+         return self();
       }
 
-      /**
-       * @param created the time and date the object was created
+      /** 
+       * @see TemplateExtraction#getCreated()
        */
-      public Builder created(Date created) {
+      public T created(Date created) {
          this.created = created;
-         return this;
+         return self();
       }
 
-      /**
-       * @param extractId the upload id of extracted object
+      /** 
+       * @see TemplateExtraction#getExtractId()
        */
-      public Builder extractId(String extractId) {
+      public T extractId(String extractId) {
          this.extractId = extractId;
-         return this;
+         return self();
       }
 
-      /**
-       * @param extractMode the mode of extraction - upload or download
+      /** 
+       * @see TemplateExtraction#getExtractMode()
        */
-      public Builder extractMode(ExtractMode extractMode) {
+      public T extractMode(ExtractMode extractMode) {
          this.extractMode = extractMode;
-         return this;
+         return self();
       }
 
-      /**
-       * @param name the name of the extracted object
+      /** 
+       * @see TemplateExtraction#getName()
        */
-      public Builder name(String name) {
+      public T name(String name) {
          this.name = name;
-         return this;
+         return self();
       }
 
-      /**
-       * @param state the state of the extracted object
+      /** 
+       * @see TemplateExtraction#getState()
        */
-      public Builder state(String state) {
+      public T state(String state) {
          this.state = state;
-         return this;
+         return self();
       }
 
-      /**
-       * @param status the status of the extraction
+      /** 
+       * @see TemplateExtraction#getStatus()
        */
-      public Builder status(String status) {
+      public T status(String status) {
          this.status = status;
-         return this;
+         return self();
       }
 
-      /**
-       * @param storageType type of the storage
+      /** 
+       * @see TemplateExtraction#getStorageType()
        */
-      public Builder storageType(String storageType) {
+      public T storageType(String storageType) {
          this.storageType = storageType;
-         return this;
+         return self();
       }
 
-      /**
-       * @param uploadPercentage the percentage of the entity uploaded to the specified location
+      /** 
+       * @see TemplateExtraction#getUploadPercentage()
        */
-      public Builder uploadPercentage(int uploadPercentage) {
+      public T uploadPercentage(int uploadPercentage) {
          this.uploadPercentage = uploadPercentage;
-         return this;
+         return self();
       }
 
-      /**
-       * @param url if mode = upload then url of the uploaded entity. if mode = download the url from which the entity can be downloaded
+      /** 
+       * @see TemplateExtraction#getUrl()
        */
-      public Builder url(String url) {
+      public T url(String url) {
          this.url = url;
-         return this;
+         return self();
       }
 
-      /**
-       * @param zoneId zone ID the object was extracted from
+      /** 
+       * @see TemplateExtraction#getZoneId()
        */
-      public Builder zoneId(String zoneId) {
+      public T zoneId(String zoneId) {
          this.zoneId = zoneId;
-         return this;
+         return self();
       }
 
-      /**
-       * @param zoneName zone name the object was extracted from
+      /** 
+       * @see TemplateExtraction#getZoneName()
        */
-      public Builder zoneName(String zoneName) {
+      public T zoneName(String zoneName) {
          this.zoneName = zoneName;
-         return this;
+         return self();
       }
 
       public TemplateExtraction build() {
-         return new TemplateExtraction(id, accountId, created, extractId,
-            extractMode, name, state, status, storageType, uploadPercentage,
-            url,zoneId, zoneName);
+         return new TemplateExtraction(id, accountId, created, extractId, extractMode, name, state, status, storageType, uploadPercentage, url, zoneId, zoneName);
+      }
+      
+      public T fromTemplateExtraction(TemplateExtraction in) {
+         return this
+                  .id(in.getId())
+                  .accountId(in.getAccountId())
+                  .created(in.getCreated())
+                  .extractId(in.getExtractId())
+                  .extractMode(in.getExtractMode())
+                  .name(in.getName())
+                  .state(in.getState())
+                  .status(in.getStatus())
+                  .storageType(in.getStorageType())
+                  .uploadPercentage(in.getUploadPercentage())
+                  .url(in.getUrl())
+                  .zoneId(in.getZoneId())
+                  .zoneName(in.getZoneName());
       }
    }
 
-   private String id;
-   @SerializedName("accountid")
-   private String accountId;
-   private Date created;
-   private String extractId;
-   private ExtractMode extractMode;
-   private String name;
-   private String state;
-   private String status;
-   @SerializedName("storagetype")
-   private String storageType;
-   @SerializedName("uploadpercentage")
-   private int uploadPercentage;
-   private String url;
-   @SerializedName("zoneid")
-   private String zoneId;
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
+   }
 
-   /**
-    *  Construct a new TemplateExtraction instance
-    */
-   public TemplateExtraction(String id, String accountId, Date created, String extractId,
-                             ExtractMode extractMode, String name, String state, String status,
-                             String storageType, int uploadPercentage, String url,
-                             String zoneId, String zoneName) {
-      this.id = id;
+   private final String id;
+   @Named("accountid")
+   private final String accountId;
+   private final Date created;
+   private final String extractId;
+   private final ExtractMode extractMode;
+   private final String name;
+   private final String state;
+   private final String status;
+   @Named("storagetype")
+   private final String storageType;
+   @Named("uploadpercentage")
+   private final int uploadPercentage;
+   private final String url;
+   @Named("zoneid")
+   private final String zoneId;
+   @Named("zonename")
+   private final String zoneName;
+
+   @ConstructorProperties({
+      "id", "accountid", "created", "extractId", "extractMode", "name", "state", "status", "storagetype", "uploadpercentage", "url", "zoneid", "zonename"
+   })
+   protected TemplateExtraction(String id, @Nullable String accountId, @Nullable Date created, @Nullable String extractId,
+                                @Nullable ExtractMode extractMode, @Nullable String name, @Nullable String state, @Nullable String status,
+                                @Nullable String storageType, int uploadPercentage, @Nullable String url, @Nullable String zoneId,
+                                @Nullable String zoneName) {
+      this.id = checkNotNull(id, "id");
       this.accountId = accountId;
       this.created = created;
       this.extractId = extractId;
@@ -198,153 +234,141 @@
       this.zoneName = zoneName;
    }
 
-   @SerializedName("zonename")
-   private String zoneName;
-
-
-   /**
-    * present only for serializer
-    */
-   TemplateExtraction() {
-   }
-
    /**
     * @return the id of extracted object
     */
    public String getId() {
-      return id;
+      return this.id;
    }
 
    /**
     * @return the account id to which the extracted object belongs
     */
+   @Nullable
    public String getAccountId() {
-      return accountId;
+      return this.accountId;
    }
 
    /**
     * @return the time and date the object was created
     */
+   @Nullable
    public Date getCreated() {
-      return created;
+      return this.created;
    }
 
    /**
     * @return the upload id of extracted object
     */
+   @Nullable
    public String getExtractId() {
-      return extractId;
+      return this.extractId;
    }
 
    /**
     * @return the mode of extraction - upload or download
     */
+   @Nullable
    public ExtractMode getExtractMode() {
-      return extractMode;
+      return this.extractMode;
    }
 
    /**
     * @return the name of the extracted object
     */
+   @Nullable
    public String getName() {
-      return name;
+      return this.name;
    }
 
    /**
     * @return the state of the extracted object
     */
+   @Nullable
    public String getState() {
-      return state;
+      return this.state;
    }
 
    /**
     * @return the status of the extraction
     */
+   @Nullable
    public String getStatus() {
-      return status;
+      return this.status;
    }
 
    /**
     * @return type of the storage
     */
+   @Nullable
    public String getStorageType() {
-      return storageType;
+      return this.storageType;
    }
 
    /**
     * @return the percentage of the entity uploaded to the specified location
     */
    public int getUploadPercentage() {
-      return uploadPercentage;
+      return this.uploadPercentage;
    }
 
    /**
     * @return if mode = upload then url of the uploaded entity. if mode = download the url from which the entity can be downloaded
     */
+   @Nullable
    public String getUrl() {
-      return url;
+      return this.url;
    }
 
    /**
     * @return zone ID the object was extracted from
     */
+   @Nullable
    public String getZoneId() {
-      return zoneId;
+      return this.zoneId;
    }
 
    /**
     * @return zone name the object was extracted from
     */
+   @Nullable
    public String getZoneName() {
-      return zoneName;
-   }
-
-   @Override
-   public boolean equals(Object o) {
-      if (this == o) return true;
-      if (o == null || getClass() != o.getClass()) return false;
-
-      TemplateExtraction that = (TemplateExtraction) o;
-
-      if (!Objects.equal(id, that.id)) return false;
-      if (!Objects.equal(accountId, that.accountId)) return false;
-      if (!Objects.equal(created, that.created)) return false;
-      if (!Objects.equal(extractId, that.extractId)) return false;
-      if (!Objects.equal(extractMode, that.extractMode)) return false;
-      if (!Objects.equal(name, that.name)) return false;
-      if (!Objects.equal(state, that.state)) return false;
-      if (!Objects.equal(status, that.status)) return false;
-      if (!Objects.equal(storageType, that.storageType)) return false;
-      if (!Objects.equal(uploadPercentage, that.uploadPercentage)) return false;
-      if (!Objects.equal(url, that.url)) return false;
-      if (!Objects.equal(zoneId, that.zoneId)) return false;
-      if (!Objects.equal(zoneName, that.zoneName)) return false;
-
-      return true;
+      return this.zoneName;
    }
 
    @Override
    public int hashCode() {
-       return Objects.hashCode(id, accountId, created, extractId, extractMode, name, state, status, storageType, uploadPercentage, url, zoneId, zoneName);
+      return Objects.hashCode(id, accountId, created, extractId, extractMode, name, state, status, storageType, uploadPercentage, url, zoneId, zoneName);
    }
 
    @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      TemplateExtraction that = TemplateExtraction.class.cast(obj);
+      return Objects.equal(this.id, that.id)
+               && Objects.equal(this.accountId, that.accountId)
+               && Objects.equal(this.created, that.created)
+               && Objects.equal(this.extractId, that.extractId)
+               && Objects.equal(this.extractMode, that.extractMode)
+               && Objects.equal(this.name, that.name)
+               && Objects.equal(this.state, that.state)
+               && Objects.equal(this.status, that.status)
+               && Objects.equal(this.storageType, that.storageType)
+               && Objects.equal(this.uploadPercentage, that.uploadPercentage)
+               && Objects.equal(this.url, that.url)
+               && Objects.equal(this.zoneId, that.zoneId)
+               && Objects.equal(this.zoneName, that.zoneName);
+   }
+   
+   protected ToStringHelper string() {
+      return Objects.toStringHelper(this)
+            .add("id", id).add("accountId", accountId).add("created", created).add("extractId", extractId).add("extractMode", extractMode).add("name", name).add("state", state).add("status", status).add("storageType", storageType).add("uploadPercentage", uploadPercentage).add("url", url).add("zoneId", zoneId).add("zoneName", zoneName);
+   }
+   
+   @Override
    public String toString() {
-      return "TemplateExtraction{" +
-            "id=" + id +
-            ", accountId=" + accountId +
-            ", created=" + created +
-            ", extractId=" + extractId +
-            ", extractMode=" + extractMode +
-            ", name='" + name + '\'' +
-            ", state='" + state + '\'' +
-            ", status='" + status + '\'' +
-            ", storageType='" + storageType + '\'' +
-            ", uploadPercentage=" + uploadPercentage +
-            ", url='" + url + '\'' +
-            ", zoneId=" + zoneId +
-            ", zoneName='" + zoneName + '\'' +
-            '}';
+      return string().toString();
    }
 
    @Override
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/TemplateMetadata.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/TemplateMetadata.java
index 5f1fdfe..ac20881 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/TemplateMetadata.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/TemplateMetadata.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -18,219 +18,222 @@
  */
 package org.jclouds.cloudstack.domain;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.beans.ConstructorProperties;
+
+import org.jclouds.javax.annotation.Nullable;
+
 import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
 
 /**
+ * Class TemplateMetadata
+ * 
  * @author Richard Downer
- */
+*/
 public class TemplateMetadata {
 
-    public static Builder builder() {
-        return new Builder();
-    }
+   public static Builder<?> builder() { 
+      return new ConcreteBuilder();
+   }
+   
+   public Builder<?> toBuilder() { 
+      return new ConcreteBuilder().fromTemplateMetadata(this);
+   }
 
-    public static class Builder {
-        private String name;
-        private String osTypeId;
-        private String displayText;
-        private String snapshotId;
-        private String volumeId;
-        private String virtualMachineId;
-        private Boolean passwordEnabled;
+   public static abstract class Builder<T extends Builder<T>>  {
+      protected abstract T self();
 
-        /**
-         * @param name
-         *            the name of the template
-         */
-        public Builder name(String name) {
-            this.name = name;
-            return this;
-        }
+      protected String name;
+      protected String osTypeId;
+      protected String displayText;
+      protected String snapshotId;
+      protected String volumeId;
+      protected String virtualMachineId;
+      protected Boolean passwordEnabled;
+   
+      /** 
+       * @see TemplateMetadata#getName()
+       */
+      public T name(String name) {
+         this.name = name;
+         return self();
+      }
 
-        /**
-         * @param osTypeId
-         *            the ID of the OS Type that best represents the OS of this template.
-         */
-        public Builder osTypeId(String osTypeId) {
-            this.osTypeId = osTypeId;
-            return this;
-        }
+      /** 
+       * @see TemplateMetadata#getOsTypeId()
+       */
+      public T osTypeId(String osTypeId) {
+         this.osTypeId = osTypeId;
+         return self();
+      }
 
-        /**
-         * @param displayText
-         *            the display text of the template. This is usually used for display purposes.
-         */
-        public Builder displayText(String displayText) {
-            this.displayText = displayText;
-            return this;
-        }
+      /** 
+       * @see TemplateMetadata#getDisplayText()
+       */
+      public T displayText(String displayText) {
+         this.displayText = displayText;
+         return self();
+      }
 
-        /**
-         * @param snapshotId
-         *      the ID of the snapshot the template is being created from.
-         *          Either this parameter, or volumeId has to be passed in
-         */
-        public Builder snapshotId(String snapshotId) {
-            this.snapshotId = snapshotId;
-            return this;
-        }
+      /** 
+       * @see TemplateMetadata#getSnapshotId()
+       */
+      public T snapshotId(String snapshotId) {
+         this.snapshotId = snapshotId;
+         return self();
+      }
 
-        /**
-         * @param volumeId
-         *          the ID of the disk volume the template is being created from.
-         *          Either this parameter, or snapshotId has to be passed in
-         */
-        public Builder volumeId(String volumeId) {
-            this.volumeId = volumeId;
-            return this;
-        }
+      /** 
+       * @see TemplateMetadata#getVolumeId()
+       */
+      public T volumeId(String volumeId) {
+         this.volumeId = volumeId;
+         return self();
+      }
 
-        /**
-         * @param virtualMachineId
-         *          the ID of the disk volume the template is being created from
-         */
-        public Builder virtualMachineId(String virtualMachineId) {
-            this.virtualMachineId = virtualMachineId;
-            return this;
-        }
+      /** 
+       * @see TemplateMetadata#getVirtualMachineId()
+       */
+      public T virtualMachineId(String virtualMachineId) {
+         this.virtualMachineId = virtualMachineId;
+         return self();
+      }
 
-        /**
-         * the template supports the password reset feature.
-         */
-        public Builder passwordEnabled() {
-            this.passwordEnabled = true;
-            return this;
-        }
+      /** 
+       * @see TemplateMetadata#isPasswordEnabled()
+       */
+      public T passwordEnabled(Boolean passwordEnabled) {
+         this.passwordEnabled = passwordEnabled;
+         return self();
+      }
 
-        public TemplateMetadata build() {
-            TemplateMetadata template = new TemplateMetadata(name, osTypeId, displayText);
-            template.setPasswordEnabled(passwordEnabled);
-            template.setSnapshotId(snapshotId);
-            template.setVirtualMachineId(virtualMachineId);
-            template.setVolumeId(volumeId);
-            return template;
-        }
-    }
+      public TemplateMetadata build() {
+         return new TemplateMetadata(name, osTypeId, displayText, snapshotId, volumeId, virtualMachineId, passwordEnabled);
+      }
+      
+      public T fromTemplateMetadata(TemplateMetadata in) {
+         return this
+                  .name(in.getName())
+                  .osTypeId(in.getOsTypeId())
+                  .displayText(in.getDisplayText())
+                  .snapshotId(in.getSnapshotId())
+                  .volumeId(in.getVolumeId())
+                  .virtualMachineId(in.getVirtualMachineId())
+                  .passwordEnabled(in.isPasswordEnabled());
+      }
+   }
 
-    private String name;
-    private String osTypeId;
-    private String displayText;
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
+   }
 
-    private String snapshotId;
-    private String volumeId;
-    private String virtualMachineId;;
-    private Boolean passwordEnabled;
+   private final String name;
+   private final String osTypeId;
+   private final String displayText;
+   private final String snapshotId;
+   private final String volumeId;
+   private final String virtualMachineId;
+   private final Boolean passwordEnabled;
 
-    public TemplateMetadata(String name, String osTypeId, String displayText) {
-        this.name = name;
-        this.osTypeId = osTypeId;
-        this.displayText = displayText;
-    }
+   @ConstructorProperties({
+      "name", "osTypeId", "displayText", "snapshotId", "volumeId", "virtualMachineId", "passwordEnabled"
+   })
+   protected TemplateMetadata(String name, @Nullable String osTypeId, @Nullable String displayText, @Nullable String snapshotId,
+                              @Nullable String volumeId, String virtualMachineId, Boolean passwordEnabled) {
+      this.name = checkNotNull(name, "name");
+      this.osTypeId = osTypeId;
+      this.displayText = displayText;
+      this.snapshotId = snapshotId;
+      this.volumeId = volumeId;
+      this.virtualMachineId = virtualMachineId;
+      this.passwordEnabled = passwordEnabled;
+   }
 
-    /**
-     * present only for serializer
-     */
-    TemplateMetadata() {
-    }
+   /**
+    * @return the name of the template
+    */
+   public String getName() {
+      return this.name;
+   }
 
-    /**
-     * @return the ID of the snapshot the template is being created from
-     */
-    public String getSnapshotId() {
-        return snapshotId;
-    }
+   /**
+    * @return the ID of the OS Type that best represents the OS of this template.
+    */
+   @Nullable
+   public String getOsTypeId() {
+      return this.osTypeId;
+   }
 
-    public void setSnapshotId(String snapshotId) {
-        this.snapshotId = snapshotId;
-    }
+   /**
+    * @return the display text of the template. This is usually used for display purposes.
+    */
+   @Nullable
+   public String getDisplayText() {
+      return this.displayText;
+   }
 
-    /**
-     * @return the ID of the disk volume the template is being created from
-     */
-    public String getVolumeId() {
-        return volumeId;
-    }
+   /**
+    * @return the ID of the snapshot the template is being created from
+    */
+   @Nullable
+   public String getSnapshotId() {
+      return this.snapshotId;
+   }
 
-    public void setVolumeId(String volumeId) {
-        this.volumeId = volumeId;
-    }
+   /**
+    * @return the ID of the disk volume the template is being created from
+    */
+   @Nullable
+   public String getVolumeId() {
+      return this.volumeId;
+   }
 
-    /**
-     * @return Optional, VM ID
-     */
-    public String getVirtualMachineId() {
-        return virtualMachineId;
-    }
+   /**
+    * @return Optional, VM ID
+    */
+   @Nullable
+   public String getVirtualMachineId() {
+      return this.virtualMachineId;
+   }
 
-    public void setVirtualMachineId(String virtualMachineId) {
-        this.virtualMachineId = virtualMachineId;
-    }
-
-    /**
-     * @return true if the template supports the password reset feature; default is false
-     */
-    public Boolean getPasswordEnabled() {
-        return passwordEnabled;
-    }
-
-    public void setPasswordEnabled(Boolean passwordEnabled) {
-        this.passwordEnabled = passwordEnabled;
-    }
-
-    /**
-     * @return the name of the template
-     */
-    public String getName() {
-        return name;
-    }
-
-    /**
-     * @return the ID of the OS Type that best represents the OS of this template.
-     */
-    public String getOsTypeId() {
-        return osTypeId;
-    }
-
-    /**
-     * @return the display text of the template. This is usually used for display purposes.
-     */
-    public String getDisplayText() {
-        return displayText;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
-
-        TemplateMetadata that = (TemplateMetadata) o;
-
-        if (!Objects.equal(osTypeId, that.osTypeId)) return false;
-        if (!Objects.equal(snapshotId, that.snapshotId)) return false;
-        if (!Objects.equal(volumeId, that.volumeId)) return false;
-        if (!Objects.equal(virtualMachineId, that.virtualMachineId)) return false;
-        if (!Objects.equal(passwordEnabled, that.passwordEnabled)) return false;
-        if (!Objects.equal(displayText, that.displayText)) return false;
-        if (!Objects.equal(name, that.name)) return false;
-        return true;
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hashCode(name, displayText, osTypeId, snapshotId, volumeId, passwordEnabled, virtualMachineId);
-    }
+   @Nullable
+   public Boolean isPasswordEnabled() {
+      return this.passwordEnabled;
+   }
 
    @Override
+   public int hashCode() {
+      return Objects.hashCode(name, osTypeId, displayText, snapshotId, volumeId, virtualMachineId, passwordEnabled);
+   }
+
+   @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      TemplateMetadata that = TemplateMetadata.class.cast(obj);
+      return Objects.equal(this.name, that.name)
+               && Objects.equal(this.osTypeId, that.osTypeId)
+               && Objects.equal(this.displayText, that.displayText)
+               && Objects.equal(this.snapshotId, that.snapshotId)
+               && Objects.equal(this.volumeId, that.volumeId)
+               && Objects.equal(this.virtualMachineId, that.virtualMachineId)
+               && Objects.equal(this.passwordEnabled, that.passwordEnabled);
+   }
+   
+   protected ToStringHelper string() {
+      return Objects.toStringHelper(this)
+            .add("name", name).add("osTypeId", osTypeId).add("displayText", displayText).add("snapshotId", snapshotId)
+            .add("volumeId", volumeId).add("virtualMachineId", virtualMachineId).add("passwordEnabled", passwordEnabled);
+   }
+   
+   @Override
    public String toString() {
-      return "TemplateMetadata{" +
-            "name='" + name + '\'' +
-            ", osTypeId=" + osTypeId +
-            ", displayText='" + displayText + '\'' +
-            ", snapshotId=" + snapshotId +
-            ", volumeId=" + volumeId +
-            ", virtualMachineId=" + virtualMachineId +
-            ", passwordEnabled=" + passwordEnabled +
-            '}';
+      return string().toString();
    }
 
 }
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/TemplatePermission.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/TemplatePermission.java
index 75c8baa..27085c3 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/TemplatePermission.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/TemplatePermission.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -18,139 +18,169 @@
  */
 package org.jclouds.cloudstack.domain;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.beans.ConstructorProperties;
+
+import javax.inject.Named;
+
+import org.jclouds.javax.annotation.Nullable;
+
 import com.google.common.base.Objects;
-import com.google.gson.annotations.SerializedName;
+import com.google.common.base.Objects.ToStringHelper;
 
 /**
  * @author Richard Downer
- */
-public class TemplatePermission implements Comparable<TemplatePermission> {
+*/
+public class TemplatePermission {
 
-   public static Builder builder() {
-      return new Builder();
+   public static Builder<?> builder() { 
+      return new ConcreteBuilder();
+   }
+   
+   public Builder<?> toBuilder() { 
+      return new ConcreteBuilder().fromTemplatePermission(this);
    }
 
-   public static class Builder {
-      private String id;
-      private String account;
-      private String domainId;
-      private boolean isPublic;
+   public static abstract class Builder<T extends Builder<T>>  {
+      protected abstract T self();
 
-      public Builder id(String id) {
+      protected String id;
+      protected String account;
+      protected String domainId;
+      protected boolean isPublic;
+   
+      /** 
+       * @see TemplatePermission#getId()
+       */
+      public T id(String id) {
          this.id = id;
-         return this;
+         return self();
       }
 
-      public Builder account(String account) {
+      /** 
+       * @see TemplatePermission#getAccount()
+       */
+      public T account(String account) {
          this.account = account;
-         return this;
+         return self();
       }
 
-      public Builder domainId(String domainId) {
+      /** 
+       * @see TemplatePermission#getDomainId()
+       */
+      public T domainId(String domainId) {
          this.domainId = domainId;
-         return this;
+         return self();
       }
 
-      public Builder isPublic(boolean isPublic) {
+      /** 
+       * @see TemplatePermission#isPublic()
+       */
+      public T isPublic(boolean isPublic) {
          this.isPublic = isPublic;
-         return this;
+         return self();
       }
 
       public TemplatePermission build() {
          return new TemplatePermission(id, account, domainId, isPublic);
       }
+      
+      public T fromTemplatePermission(TemplatePermission in) {
+         return this
+                  .id(in.getId())
+                  .account(in.getAccount())
+                  .domainId(in.getDomainId())
+                  .isPublic(in.isPublic());
+      }
    }
 
-   private String id;
-   private String account;
-   @SerializedName("domainid") private String domainId;
-   @SerializedName("ispublic") private boolean isPublic;
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
+   }
 
-   /**
-    * Construct a new TemplatePermission instance.
-    * @param id the template ID
-    * @param account the list of accounts the template is available for
-    * @param domainId the ID of the domain to which the template belongs
-    * @param isPublic true if this template is a public template, false otherwise
-    */
-   public TemplatePermission(String id, String account, String domainId, boolean isPublic) {
-      this.id = id;
+   private final String id;
+   private final String account;
+   @Named("domainid")
+   private final String domainId;
+   @Named("ispublic")
+   private final boolean isPublic;
+
+   @ConstructorProperties({
+      "id", "account", "domainid", "ispublic"
+   })
+   protected TemplatePermission(String id, @Nullable String account, @Nullable String domainId, boolean isPublic) {
+      this.id = checkNotNull(id, "id");
       this.account = account;
       this.domainId = domainId;
       this.isPublic = isPublic;
    }
 
    /**
-    * present only for serializer
-    */
-   TemplatePermission() {
-   }
-
-   /**
     * Gets the template ID
+    * 
     * @return the template ID
     */
    public String getId() {
-      return id;
+      return this.id;
    }
 
    /**
     * Gets the list of accounts the template is available for
+    * 
     * @return the list of accounts the template is available for
     */
+   @Nullable
    public String getAccount() {
-      return account;
+      return this.account;
    }
 
    /**
     * Gets the ID of the domain to which the template belongs
+    * 
     * @return the ID of the domain to which the template belongs
     */
+   @Nullable
    public String getDomainId() {
-      return domainId;
+      return this.domainId;
    }
 
    /**
     * Returns true if this template is a public template, false otherwise
+    * 
     * @return true if this template is a public template, false otherwise
     */
    public boolean isPublic() {
-      return isPublic;
-   }
-
-   @Override
-   public boolean equals(Object o) {
-      if (this == o) return true;
-      if (o == null || getClass() != o.getClass()) return false;
-
-      TemplatePermission that = (TemplatePermission) o;
-
-      if (!Objects.equal(domainId, that.domainId)) return false;
-      if (!Objects.equal(id, that.id)) return false;
-      if (!Objects.equal(isPublic, that.isPublic)) return false;
-      if (!Objects.equal(account, that.account)) return false;
-
-      return true;
+      return this.isPublic;
    }
 
    @Override
    public int hashCode() {
-       return Objects.hashCode(domainId, id, isPublic, account);
+      return Objects.hashCode(id, account, domainId, isPublic);
    }
 
    @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      TemplatePermission that = TemplatePermission.class.cast(obj);
+      return Objects.equal(this.id, that.id)
+               && Objects.equal(this.account, that.account)
+               && Objects.equal(this.domainId, that.domainId)
+               && Objects.equal(this.isPublic, that.isPublic);
+   }
+   
+   protected ToStringHelper string() {
+      return Objects.toStringHelper(this)
+            .add("id", id).add("account", account).add("domainId", domainId).add("isPublic", isPublic);
+   }
+   
+   @Override
    public String toString() {
-      return "TemplatePermission{" +
-            "id=" + id +
-            ", account='" + account + '\'' +
-            ", domainId=" + domainId +
-            ", isPublic=" + isPublic +
-            '}';
-   }
-
-   @Override
-   public int compareTo(TemplatePermission other) {
-      return id.compareTo(other.getId());
+      return string().toString();
    }
 
 }
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/UsageRecord.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/UsageRecord.java
index 3472d25..f055cca 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/UsageRecord.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/UsageRecord.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -20,23 +20,30 @@
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import java.beans.ConstructorProperties;
 import java.util.Date;
 import java.util.Map;
 
+import javax.inject.Named;
+
+import org.jclouds.javax.annotation.Nullable;
+
 import com.google.common.base.Function;
 import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Maps;
-import com.google.gson.annotations.SerializedName;
 
 /**
  * Represents a usage record from CloudStack
- *
+ * 
  * @author Richard Downer
- */
-public class UsageRecord implements Comparable<UsageRecord> {
+*/
+public class UsageRecord {
 
-   public enum UsageType {
+   /**
+    */
+   public static enum UsageType {
       RUNNING_VM(1),
       ALLOCATED_VM(2),
       IP_ADDRESS(3),
@@ -52,197 +59,314 @@
       NETWORK_OFFERING(13),
       VPN_USERS(14),
       UNRECOGNIZED(0);
-
+      
       private int code;
-
+      
       private static final Map<Integer, UsageType> INDEX = Maps.uniqueIndex(ImmutableSet.copyOf(UsageType.values()),
-         new Function<UsageType, Integer>() {
-
-            @Override
-            public Integer apply(UsageType input) {
-               return input.code;
-            }
-
-         });
-
-      UsageType(int code) {
-         this.code = code;
+      new Function<UsageType, Integer>() {
+      
+      @Override
+      public Integer apply(UsageType input) {
+      return input.code;
       }
-
+      
+      });
+      
+      UsageType(int code) {
+      this.code = code;
+      }
+      
       @Override
       public String toString() {
-         return "" + code;
+      return "" + code;
       }
-
+      
       public static UsageType fromValue(String usageType) {
-         Integer code = new Integer(checkNotNull(usageType, "usageType"));
-         return INDEX.containsKey(code) ? INDEX.get(code) : UNRECOGNIZED;
+      Integer code = new Integer(checkNotNull(usageType, "usageType"));
+      return INDEX.containsKey(code) ? INDEX.get(code) : UNRECOGNIZED;
       }
-
+      
    }
 
-   public static Builder builder() {
-      return new Builder();
+   public static Builder<?> builder() { 
+      return new ConcreteBuilder();
+   }
+   
+   public Builder<?> toBuilder() { 
+      return new ConcreteBuilder().fromUsageRecord(this);
    }
 
-   public static class Builder {
+   public static abstract class Builder<T extends Builder<T>>  {
+      protected abstract T self();
 
-      private Builder() {
-      }
-
-      private String id;
-      private String description;
-      private String accountId;
-      private String accountName;
-      private String domainId;
-      private Date startDate;
-      private Date endDate;
-      private Date assignDate;
-      private String releaseDate;
-      private String zoneId;
-      private String virtualMachineId;
-      private String virtualMachineName;
-      private String serviceOfferingId;
-      private String templateId;
-      private String ipAddress;
-      private boolean isSourceNAT;
-      private double rawUsageHours;
-      private String usage;
-      private String type;
-      private UsageType usageType;
-
-      public Builder id(String id) {
+      protected String id;
+      protected String description;
+      protected String accountId;
+      protected String accountName;
+      protected String domainId;
+      protected Date startDate;
+      protected Date endDate;
+      protected Date assignDate;
+      protected String releaseDate;
+      protected String zoneId;
+      protected String virtualMachineId;
+      protected String virtualMachineName;
+      protected String serviceOfferingId;
+      protected String templateId;
+      protected String ipAddress;
+      protected boolean isSourceNAT;
+      protected double rawUsageHours;
+      protected String usage;
+      protected String type;
+      protected UsageType usageType;
+   
+      /** 
+       * @see UsageRecord#getId()
+       */
+      public T id(String id) {
          this.id = id;
-         return this;
+         return self();
       }
 
-      public Builder description(String description) {
+      /** 
+       * @see UsageRecord#getDescription()
+       */
+      public T description(String description) {
          this.description = description;
-         return this;
+         return self();
       }
 
-      public Builder accountId(String accountId) {
+      /** 
+       * @see UsageRecord#getAccountId()
+       */
+      public T accountId(String accountId) {
          this.accountId = accountId;
-         return this;
+         return self();
       }
 
-      public Builder accountName(String accountName) {
+      /** 
+       * @see UsageRecord#getAccountName()
+       */
+      public T accountName(String accountName) {
          this.accountName = accountName;
-         return this;
+         return self();
       }
 
-      public Builder domainId(String domainId) {
+      /** 
+       * @see UsageRecord#getDomainId()
+       */
+      public T domainId(String domainId) {
          this.domainId = domainId;
-         return this;
+         return self();
       }
 
-      public Builder startDate(Date startDate) {
+      /** 
+       * @see UsageRecord#getStartDate()
+       */
+      public T startDate(Date startDate) {
          this.startDate = startDate;
-         return this;
+         return self();
       }
 
-      public Builder endDate(Date endDate) {
+      /** 
+       * @see UsageRecord#getEndDate()
+       */
+      public T endDate(Date endDate) {
          this.endDate = endDate;
-         return this;
+         return self();
       }
 
-      public Builder assignDate(Date assignDate) {
+      /** 
+       * @see UsageRecord#getAssignDate()
+       */
+      public T assignDate(Date assignDate) {
          this.assignDate = assignDate;
-         return this;
+         return self();
       }
 
-      public Builder releaseDate(String releaseDate) {
+      /** 
+       * @see UsageRecord#getReleaseDate()
+       */
+      public T releaseDate(String releaseDate) {
          this.releaseDate = releaseDate;
-         return this;
+         return self();
       }
 
-      public Builder zoneId(String zoneId) {
+      /** 
+       * @see UsageRecord#getZoneId()
+       */
+      public T zoneId(String zoneId) {
          this.zoneId = zoneId;
-         return this;
+         return self();
       }
 
-      public Builder virtualMachineId(String virtualMachineId) {
+      /** 
+       * @see UsageRecord#getVirtualMachineId()
+       */
+      public T virtualMachineId(String virtualMachineId) {
          this.virtualMachineId = virtualMachineId;
-         return this;
+         return self();
       }
 
-      public Builder virtualMachineName(String virtualMachineName) {
+      /** 
+       * @see UsageRecord#getVirtualMachineName()
+       */
+      public T virtualMachineName(String virtualMachineName) {
          this.virtualMachineName = virtualMachineName;
-         return this;
+         return self();
       }
 
-      public Builder serviceOfferingId(String serviceOfferingId) {
+      /** 
+       * @see UsageRecord#getServiceOfferingId()
+       */
+      public T serviceOfferingId(String serviceOfferingId) {
          this.serviceOfferingId = serviceOfferingId;
-         return this;
+         return self();
       }
 
-      public Builder templateId(String templateId) {
+      /** 
+       * @see UsageRecord#getTemplateId()
+       */
+      public T templateId(String templateId) {
          this.templateId = templateId;
-         return this;
+         return self();
       }
 
-      public Builder ipAddress(String ipAddress) {
+      /** 
+       * @see UsageRecord#getIpAddress()
+       */
+      public T ipAddress(String ipAddress) {
          this.ipAddress = ipAddress;
-         return this;
+         return self();
       }
 
-      public Builder surceNAT(boolean sourceNAT) {
-         isSourceNAT = sourceNAT;
-         return this;
+      /** 
+       * @see UsageRecord#isSourceNAT()
+       */
+      public T isSourceNAT(boolean isSourceNAT) {
+         this.isSourceNAT = isSourceNAT;
+         return self();
       }
 
-      public Builder rawUsageHours(double rawUsageHours) {
+      /** 
+       * @see UsageRecord#getRawUsageHours()
+       */
+      public T rawUsageHours(double rawUsageHours) {
          this.rawUsageHours = rawUsageHours;
-         return this;
+         return self();
       }
 
-      public Builder usage(String usage) {
+      /** 
+       * @see UsageRecord#getUsage()
+       */
+      public T usage(String usage) {
          this.usage = usage;
-         return this;
+         return self();
       }
 
-      public Builder type(String type) {
+      /** 
+       * @see UsageRecord#getType()
+       */
+      public T type(String type) {
          this.type = type;
-         return this;
+         return self();
       }
 
-      public Builder usageType(UsageType usageType) {
+      /** 
+       * @see UsageRecord#getUsageType()
+       */
+      public T usageType(UsageType usageType) {
          this.usageType = usageType;
-         return this;
+         return self();
       }
 
       public UsageRecord build() {
-         return new UsageRecord(id, description, accountId, accountName, domainId, startDate, endDate, assignDate, releaseDate, zoneId, virtualMachineId, virtualMachineName, serviceOfferingId, templateId, ipAddress, isSourceNAT, rawUsageHours, usage, type, usageType);
+         return new UsageRecord(id, description, accountId, accountName, domainId, startDate, endDate, assignDate, releaseDate,
+               zoneId, virtualMachineId, virtualMachineName, serviceOfferingId, templateId, ipAddress, isSourceNAT, rawUsageHours,
+               usage, type, usageType);
+      }
+      
+      public T fromUsageRecord(UsageRecord in) {
+         return this
+                  .id(in.getId())
+                  .description(in.getDescription())
+                  .accountId(in.getAccountId())
+                  .accountName(in.getAccountName())
+                  .domainId(in.getDomainId())
+                  .startDate(in.getStartDate())
+                  .endDate(in.getEndDate())
+                  .assignDate(in.getAssignDate())
+                  .releaseDate(in.getReleaseDate())
+                  .zoneId(in.getZoneId())
+                  .virtualMachineId(in.getVirtualMachineId())
+                  .virtualMachineName(in.getVirtualMachineName())
+                  .serviceOfferingId(in.getServiceOfferingId())
+                  .templateId(in.getTemplateId())
+                  .ipAddress(in.getIpAddress())
+                  .isSourceNAT(in.isSourceNAT())
+                  .rawUsageHours(in.getRawUsageHours())
+                  .usage(in.getUsage())
+                  .type(in.getType())
+                  .usageType(in.getUsageType());
       }
    }
 
-   @SerializedName("usageid") private String id;
-   private String description;
-   @SerializedName("accountid") private String accountId;
-   @SerializedName("account") private String accountName;
-   @SerializedName("domainid") private String domainId;
-   @SerializedName("startdate") private Date startDate;
-   @SerializedName("enddate") private Date endDate;
-   @SerializedName("assigndate") private Date assignDate;
-   @SerializedName("releasedate") private String releaseDate;
-   @SerializedName("zoneid") private String zoneId;
-   @SerializedName("virtualmachineid") private String virtualMachineId;
-   @SerializedName("name") private String virtualMachineName;
-   @SerializedName("offeringid") private String serviceOfferingId;
-   @SerializedName("templateid") private String templateId;
-   @SerializedName("ipaddress") private String ipAddress;
-   @SerializedName("issourcenat") private boolean isSourceNAT;
-   @SerializedName("rawusage") private double rawUsageHours;
-   @SerializedName("usage") private String usage;
-   private String type;
-   @SerializedName("usagetype") private UsageType usageType;
-
-   /* Exists only for the deserializer */
-   UsageRecord(){
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
    }
 
-   public UsageRecord(String id, String description, String accountId, String accountName, String domainId, Date startDate, Date endDate, Date assignDate, String releaseDate, String zoneId, String virtualMachineId, String virtualMachineName, String serviceOfferingId, String templateId, String ipAddress, boolean sourceNAT, double rawUsageHours, String usage, String type, UsageType usageType) {
-      this.id = id;
+   @Named("usageid")
+   private final String id;
+   private final String description;
+   @Named("accountid")
+   private final String accountId;
+   @Named("account")
+   private final String accountName;
+   @Named("domainid")
+   private final String domainId;
+   @Named("startdate")
+   private final Date startDate;
+   @Named("enddate")
+   private final Date endDate;
+   @Named("assigndate")
+   private final Date assignDate;
+   @Named("releasedate")
+   private final String releaseDate;
+   @Named("zoneid")
+   private final String zoneId;
+   @Named("virtualmachineid")
+   private final String virtualMachineId;
+   @Named("name")
+   private final String virtualMachineName;
+   @Named("offeringid")
+   private final String serviceOfferingId;
+   @Named("templateid")
+   private final String templateId;
+   @Named("ipaddress")
+   private final String ipAddress;
+   @Named("issourcenat")
+   private final boolean isSourceNAT;
+   @Named("rawusage")
+   private final double rawUsageHours;
+   private final String usage;
+   private final String type;
+   @Named("usagetype")
+   private final UsageType usageType;
+
+   @ConstructorProperties({
+      "usageid", "description", "accountid", "account", "domainid", "startdate", "enddate", "assigndate", "releasedate",
+         "zoneid", "virtualmachineid", "name", "offeringid", "templateid", "ipaddress", "issourcenat", "rawusage", "usage",
+         "type", "usagetype"
+   })
+   protected UsageRecord(String id, @Nullable String description, @Nullable String accountId, @Nullable String accountName,
+                         @Nullable String domainId, @Nullable Date startDate, @Nullable Date endDate, @Nullable Date assignDate,
+                         @Nullable String releaseDate, @Nullable String zoneId, @Nullable String virtualMachineId, @Nullable String virtualMachineName,
+                         @Nullable String serviceOfferingId, @Nullable String templateId, @Nullable String ipAddress,
+                         boolean isSourceNAT, double rawUsageHours, @Nullable String usage, @Nullable String type, @Nullable UsageType usageType) {
+      this.id = checkNotNull(id, "id");
       this.description = description;
       this.accountId = accountId;
       this.accountName = accountName;
@@ -257,7 +381,7 @@
       this.serviceOfferingId = serviceOfferingId;
       this.templateId = templateId;
       this.ipAddress = ipAddress;
-      isSourceNAT = sourceNAT;
+      this.isSourceNAT = isSourceNAT;
       this.rawUsageHours = rawUsageHours;
       this.usage = usage;
       this.type = type;
@@ -265,152 +389,149 @@
    }
 
    public String getId() {
-      return id;
+      return this.id;
    }
 
+   @Nullable
    public String getDescription() {
-      return description;
+      return this.description;
    }
 
+   @Nullable
    public String getAccountId() {
-      return accountId;
+      return this.accountId;
    }
 
+   @Nullable
    public String getAccountName() {
-      return accountName;
+      return this.accountName;
    }
 
+   @Nullable
    public String getDomainId() {
-      return domainId;
+      return this.domainId;
    }
 
+   @Nullable
    public Date getStartDate() {
-      return startDate;
+      return this.startDate;
    }
 
+   @Nullable
    public Date getEndDate() {
-      return endDate;
+      return this.endDate;
    }
 
+   @Nullable
    public Date getAssignDate() {
-      return assignDate;
+      return this.assignDate;
    }
 
+   @Nullable
    public String getReleaseDate() {
-      return releaseDate;
+      return this.releaseDate;
    }
 
+   @Nullable
    public String getZoneId() {
-      return zoneId;
+      return this.zoneId;
    }
 
+   @Nullable
    public String getVirtualMachineId() {
-      return virtualMachineId;
+      return this.virtualMachineId;
    }
 
+   @Nullable
    public String getVirtualMachineName() {
-      return virtualMachineName;
+      return this.virtualMachineName;
    }
 
+   @Nullable
    public String getServiceOfferingId() {
-      return serviceOfferingId;
+      return this.serviceOfferingId;
    }
 
+   @Nullable
    public String getTemplateId() {
-      return templateId;
+      return this.templateId;
    }
 
+   @Nullable
    public String getIpAddress() {
-      return ipAddress;
+      return this.ipAddress;
    }
 
    public boolean isSourceNAT() {
-      return isSourceNAT;
+      return this.isSourceNAT;
    }
 
    public double getRawUsageHours() {
-      return rawUsageHours;
+      return this.rawUsageHours;
    }
 
+   @Nullable
    public String getUsage() {
-      return usage;
+      return this.usage;
    }
 
+   @Nullable
    public String getType() {
-      return type;
+      return this.type;
    }
 
+   @Nullable
    public UsageType getUsageType() {
-      return usageType;
-   }
-
-   @Override
-   public boolean equals(Object o) {
-      if (this == o) return true;
-      if (o == null || getClass() != o.getClass()) return false;
-
-      UsageRecord that = (UsageRecord) o;
-
-      if (!Objects.equal(accountId, that.accountId)) return false;
-      if (!Objects.equal(domainId, that.domainId)) return false;
-      if (!Objects.equal(id, that.id)) return false;
-      if (!Objects.equal(isSourceNAT, that.isSourceNAT)) return false;
-      if (!Objects.equal(rawUsageHours, that.rawUsageHours)) return false;
-      if (!Objects.equal(releaseDate, that.releaseDate)) return false;
-      if (!Objects.equal(serviceOfferingId, that.serviceOfferingId)) return false;
-      if (!Objects.equal(templateId, that.templateId)) return false;
-      if (!Objects.equal(virtualMachineId, that.virtualMachineId)) return false;
-      if (!Objects.equal(zoneId, that.zoneId)) return false;
-      if (!Objects.equal(accountName, that.accountName)) return false;
-      if (!Objects.equal(assignDate, that.assignDate)) return false;
-      if (!Objects.equal(description, that.description)) return false;
-      if (!Objects.equal(endDate, that.endDate)) return false;
-      if (!Objects.equal(ipAddress, that.ipAddress)) return false;
-      if (!Objects.equal(startDate, that.startDate)) return false;
-      if (!Objects.equal(type, that.type)) return false;
-      if (!Objects.equal(usage, that.usage)) return false;
-      if (!Objects.equal(usageType, that.usageType)) return false;
-      if (!Objects.equal(virtualMachineName, that.virtualMachineName)) return false;
-
-      return true;
+      return this.usageType;
    }
 
    @Override
    public int hashCode() {
-       return Objects.hashCode(accountId, domainId, id, isSourceNAT, rawUsageHours, releaseDate,
-                               serviceOfferingId, templateId, virtualMachineId, zoneId, accountName,
-                               assignDate, description, endDate, ipAddress, startDate, type, usage,
-                               usageType, virtualMachineName);
+      return Objects.hashCode(id, description, accountId, accountName, domainId, startDate, endDate, assignDate, releaseDate,
+            zoneId, virtualMachineId, virtualMachineName, serviceOfferingId, templateId, ipAddress, isSourceNAT, rawUsageHours,
+            usage, type, usageType);
    }
 
    @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      UsageRecord that = UsageRecord.class.cast(obj);
+      return Objects.equal(this.id, that.id)
+               && Objects.equal(this.description, that.description)
+               && Objects.equal(this.accountId, that.accountId)
+               && Objects.equal(this.accountName, that.accountName)
+               && Objects.equal(this.domainId, that.domainId)
+               && Objects.equal(this.startDate, that.startDate)
+               && Objects.equal(this.endDate, that.endDate)
+               && Objects.equal(this.assignDate, that.assignDate)
+               && Objects.equal(this.releaseDate, that.releaseDate)
+               && Objects.equal(this.zoneId, that.zoneId)
+               && Objects.equal(this.virtualMachineId, that.virtualMachineId)
+               && Objects.equal(this.virtualMachineName, that.virtualMachineName)
+               && Objects.equal(this.serviceOfferingId, that.serviceOfferingId)
+               && Objects.equal(this.templateId, that.templateId)
+               && Objects.equal(this.ipAddress, that.ipAddress)
+               && Objects.equal(this.isSourceNAT, that.isSourceNAT)
+               && Objects.equal(this.rawUsageHours, that.rawUsageHours)
+               && Objects.equal(this.usage, that.usage)
+               && Objects.equal(this.type, that.type)
+               && Objects.equal(this.usageType, that.usageType);
+   }
+   
+   protected ToStringHelper string() {
+      return Objects.toStringHelper(this)
+            .add("id", id).add("description", description).add("accountId", accountId).add("accountName", accountName)
+            .add("domainId", domainId).add("startDate", startDate).add("endDate", endDate).add("assignDate", assignDate)
+            .add("releaseDate", releaseDate).add("zoneId", zoneId).add("virtualMachineId", virtualMachineId)
+            .add("virtualMachineName", virtualMachineName).add("serviceOfferingId", serviceOfferingId).add("templateId", templateId)
+            .add("ipAddress", ipAddress).add("isSourceNAT", isSourceNAT).add("rawUsageHours", rawUsageHours).add("usage", usage)
+            .add("type", type).add("usageType", usageType);
+   }
+   
+   @Override
    public String toString() {
-      return "UsageRecord{" +
-         "id=" + id +
-         ", description='" + description + '\'' +
-         ", accountId=" + accountId +
-         ", accountName='" + accountName + '\'' +
-         ", domainId=" + domainId +
-         ", startDate=" + startDate +
-         ", endDate=" + endDate +
-         ", assignDate=" + assignDate +
-         ", releaseDate=" + releaseDate +
-         ", zoneId=" + zoneId +
-         ", virtualMachineId=" + virtualMachineId +
-         ", virtualMachineName='" + virtualMachineName + '\'' +
-         ", serviceOfferingId=" + serviceOfferingId +
-         ", templateId=" + templateId +
-         ", ipAddress='" + ipAddress + '\'' +
-         ", isSourceNAT=" + isSourceNAT +
-         ", rawUsageHours=" + rawUsageHours +
-         ", usage='" + usage + '\'' +
-         ", type='" + type + '\'' +
-         ", usageType=" + usageType +
-         '}';
+      return string().toString();
    }
 
-   @Override
-   public int compareTo(UsageRecord other) {
-      return this.id.compareTo(other.id);
-   }
 }
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/User.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/User.java
index b8a405f..5befc88 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/User.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/User.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -18,172 +18,249 @@
  */
 package org.jclouds.cloudstack.domain;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.beans.ConstructorProperties;
 import java.util.Date;
 
-import org.jclouds.cloudstack.domain.Account.Type;
+import javax.inject.Named;
+
+import org.jclouds.javax.annotation.Nullable;
 
 import com.google.common.base.Objects;
-import com.google.gson.annotations.SerializedName;
+import com.google.common.base.Objects.ToStringHelper;
 
 /**
+ * Class User
  * 
  * @author Adrian Cole
- */
-public class User implements Comparable<User> {
+*/
+public class User {
 
+   /**
+    */
    public static enum State {
       ENABLED,
       DISABLED,
       UNKNOWN;
-
+      
       public static State fromValue(String value) {
-         try {
-            return valueOf(value.toUpperCase());
-         } catch(IllegalArgumentException e) {
-            return UNKNOWN;
-         }
+      try {
+      return valueOf(value.toUpperCase());
+      } catch(IllegalArgumentException e) {
+      return UNKNOWN;
       }
-
+      }
+      
       @Override
       public String toString() {
-         return name().toLowerCase();
+      return name().toLowerCase();
       }
    }
 
-   public static Builder builder() {
-      return new Builder();
+   public static Builder<?> builder() { 
+      return new ConcreteBuilder();
+   }
+   
+   public Builder<?> toBuilder() { 
+      return new ConcreteBuilder().fromUser(this);
    }
 
-   public static class Builder {
-      private String id;
-      private String name;
-      private String firstName;
-      private String lastName;
-      private String email;
-      private Date created;
-      private State state;
-      private String account;
-      private Account.Type accountType;
-      private String domain;
-      private String domainId;
-      private String timeZone;
-      private String apiKey;
-      private String secretKey;
+   public static abstract class Builder<T extends Builder<T>>  {
+      protected abstract T self();
 
-      public Builder id(String id) {
+      protected String id;
+      protected String name;
+      protected String firstName;
+      protected String lastName;
+      protected String email;
+      protected Date created;
+      protected User.State state;
+      protected String account;
+      protected Account.Type accountType;
+      protected String domain;
+      protected String domainId;
+      protected String timeZone;
+      protected String apiKey;
+      protected String secretKey;
+   
+      /** 
+       * @see User#getId()
+       */
+      public T id(String id) {
          this.id = id;
-         return this;
+         return self();
       }
 
-      public Builder name(String name) {
+      /** 
+       * @see User#getName()
+       */
+      public T name(String name) {
          this.name = name;
-         return this;
+         return self();
       }
 
-      public Builder firstName(String firstName) {
+      /** 
+       * @see User#getFirstName()
+       */
+      public T firstName(String firstName) {
          this.firstName = firstName;
-         return this;
+         return self();
       }
 
-      public Builder lastName(String lastName) {
+      /** 
+       * @see User#getLastName()
+       */
+      public T lastName(String lastName) {
          this.lastName = lastName;
-         return this;
+         return self();
       }
 
-      public Builder email(String email) {
+      /** 
+       * @see User#getEmail()
+       */
+      public T email(String email) {
          this.email = email;
-         return this;
+         return self();
       }
 
-      public Builder created(Date created) {
+      /** 
+       * @see User#getCreated()
+       */
+      public T created(Date created) {
          this.created = created;
-         return this;
+         return self();
       }
 
-      public Builder state(State state) {
+      /** 
+       * @see User#getState()
+       */
+      public T state(User.State state) {
          this.state = state;
-         return this;
+         return self();
       }
 
-      public Builder account(String account) {
+      /** 
+       * @see User#getAccount()
+       */
+      public T account(String account) {
          this.account = account;
-         return this;
+         return self();
       }
 
-      public Builder accountType(Account.Type accountType) {
+      /** 
+       * @see User#getAccountType()
+       */
+      public T accountType(Account.Type accountType) {
          this.accountType = accountType;
-         return this;
+         return self();
       }
 
-      public Builder domain(String domain) {
+      /** 
+       * @see User#getDomain()
+       */
+      public T domain(String domain) {
          this.domain = domain;
-         return this;
+         return self();
       }
 
-      public Builder domainId(String domainId) {
+      /** 
+       * @see User#getDomainId()
+       */
+      public T domainId(String domainId) {
          this.domainId = domainId;
-         return this;
+         return self();
       }
 
-      public Builder timeZone(String timeZone) {
+      /** 
+       * @see User#getTimeZone()
+       */
+      public T timeZone(String timeZone) {
          this.timeZone = timeZone;
-         return this;
+         return self();
       }
 
-      public Builder apiKey(String apiKey) {
+      /** 
+       * @see User#getApiKey()
+       */
+      public T apiKey(String apiKey) {
          this.apiKey = apiKey;
-         return this;
+         return self();
       }
 
-      public Builder secretKey(String secretKey) {
+      /** 
+       * @see User#getSecretKey()
+       */
+      public T secretKey(String secretKey) {
          this.secretKey = secretKey;
-         return this;
+         return self();
       }
 
       public User build() {
-         return new User(id, name, firstName, lastName, email, created, state, account, accountType, domain, domainId,
-               timeZone, apiKey, secretKey);
+         return new User(id, name, firstName, lastName, email, created, state, account, accountType, domain, domainId, timeZone, apiKey, secretKey);
+      }
+      
+      public T fromUser(User in) {
+         return this
+                  .id(in.getId())
+                  .name(in.getName())
+                  .firstName(in.getFirstName())
+                  .lastName(in.getLastName())
+                  .email(in.getEmail())
+                  .created(in.getCreated())
+                  .state(in.getState())
+                  .account(in.getAccount())
+                  .accountType(in.getAccountType())
+                  .domain(in.getDomain())
+                  .domainId(in.getDomainId())
+                  .timeZone(in.getTimeZone())
+                  .apiKey(in.getApiKey())
+                  .secretKey(in.getSecretKey());
       }
    }
 
-   /**
-    * present only for serializer
-    * 
-    */
-   User() {
-
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
    }
 
-   private String id;
-   @SerializedName("username")
-   private String name;
-   @SerializedName("firstname")
-   private String firstName;
-   @SerializedName("lastname")
-   private String lastName;
-   private String email;
-   private Date created;
-   private State state;
-   private String account;
-   @SerializedName("accounttype")
-   private Account.Type accountType;
-   private String domain;
-   @SerializedName("domainid")
-   private String domainId;
-   @SerializedName("timezone")
-   private String timeZone;
-   @SerializedName("apikey")
-   private String apiKey;
-   @SerializedName("secretkey")
-   private String secretKey;
+   private final String id;
+   @Named("username")
+   private final String name;
+   @Named("firstname")
+   private final String firstName;
+   @Named("lastname")
+   private final String lastName;
+   private final String email;
+   private final Date created;
+   private final User.State state;
+   private final String account;
+   @Named("accounttype")
+   private final Account.Type accountType;
+   private final String domain;
+   @Named("domainid")
+   private final String domainId;
+   @Named("timezone")
+   private final String timeZone;
+   @Named("apikey")
+   private final String apiKey;
+   @Named("secretkey")
+   private final String secretKey;
 
-   public User(String id, String name, String firstname, String lastname, String email, Date created, State state,
-         String account, Type accountType, String domain, String domainId, String timeZone, String apiKey,
-         String secretKey) {
-      this.id = id;
+   @ConstructorProperties({
+      "id", "username", "firstname", "lastname", "email", "created", "state", "account", "accounttype", "domain",
+         "domainid", "timezone", "apikey", "secretkey"
+   })
+   protected User(String id, @Nullable String name, @Nullable String firstName, @Nullable String lastName,
+                  @Nullable String email, @Nullable Date created, @Nullable User.State state, @Nullable String account,
+                  @Nullable Account.Type accountType, @Nullable String domain, @Nullable String domainId, @Nullable String timeZone,
+                  @Nullable String apiKey, @Nullable String secretKey) {
+      this.id = checkNotNull(id, "id");
       this.name = name;
-      this.firstName = firstname;
-      this.lastName = lastname;
+      this.firstName = firstName;
+      this.lastName = lastName;
       this.email = email;
       this.created = created;
       this.state = state;
@@ -197,159 +274,152 @@
    }
 
    /**
-    * 
     * @return the user ID
     */
    public String getId() {
-      return id;
+      return this.id;
    }
 
    /**
-    * 
     * @return the user name
     */
+   @Nullable
    public String getName() {
-      return name;
+      return this.name;
    }
 
    /**
-    * 
     * @return the user firstname
     */
+   @Nullable
    public String getFirstName() {
-      return firstName;
+      return this.firstName;
    }
 
    /**
-    * 
     * @return the user lastname
     */
+   @Nullable
    public String getLastName() {
-      return lastName;
+      return this.lastName;
    }
 
    /**
-    * 
     * @return the user email address
     */
+   @Nullable
    public String getEmail() {
-      return email;
+      return this.email;
    }
 
    /**
-    * 
     * @return the date and time the user account was created
     */
+   @Nullable
    public Date getCreated() {
-      return created;
+      return this.created;
    }
 
    /**
-    * 
     * @return the user state
     */
-   public State getState() {
-      return state;
+   @Nullable
+   public User.State getState() {
+      return this.state;
    }
 
    /**
-    * 
     * @return the account name of the user
     */
+   @Nullable
    public String getAccount() {
-      return account;
+      return this.account;
    }
 
    /**
-    * 
     * @return the account type of the user
     */
+   @Nullable
    public Account.Type getAccountType() {
-      return accountType;
+      return this.accountType;
    }
 
    /**
-    * 
     * @return the domain name of the user
     */
+   @Nullable
    public String getDomain() {
-      return domain;
+      return this.domain;
    }
 
    /**
-    * 
     * @return the domain ID of the user
     */
+   @Nullable
    public String getDomainId() {
-      return domainId;
+      return this.domainId;
    }
 
    /**
-    * 
     * @return the timezone user was created in
     */
+   @Nullable
    public String getTimeZone() {
-      return timeZone;
+      return this.timeZone;
    }
 
    /**
-    * 
     * @return the api key of the user
     */
+   @Nullable
    public String getApiKey() {
-      return apiKey;
+      return this.apiKey;
    }
 
    /**
-    * 
     * @return the secret key of the user
     */
+   @Nullable
    public String getSecretKey() {
-      return secretKey;
-   }
-
-   @Override
-   public int compareTo(User arg0) {
-       return id.compareTo(arg0.getId());
-   }
-
-   @Override
-   public boolean equals(Object o) {
-      if (this == o) return true;
-      if (o == null || getClass() != o.getClass()) return false;
-
-      User that = (User) o;
-
-      if (!Objects.equal(account, that.account)) return false;
-      if (!Objects.equal(domainId, that.domainId)) return false;
-      if (!Objects.equal(id, that.id)) return false;
-
-      return true;
+      return this.secretKey;
    }
 
    @Override
    public int hashCode() {
-       return Objects.hashCode(account, domainId, id);
+      return Objects.hashCode(id, name, firstName, lastName, email, created, state, account, accountType, domain, domainId, timeZone, apiKey, secretKey);
    }
 
    @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      User that = User.class.cast(obj);
+      return Objects.equal(this.id, that.id)
+               && Objects.equal(this.name, that.name)
+               && Objects.equal(this.firstName, that.firstName)
+               && Objects.equal(this.lastName, that.lastName)
+               && Objects.equal(this.email, that.email)
+               && Objects.equal(this.created, that.created)
+               && Objects.equal(this.state, that.state)
+               && Objects.equal(this.account, that.account)
+               && Objects.equal(this.accountType, that.accountType)
+               && Objects.equal(this.domain, that.domain)
+               && Objects.equal(this.domainId, that.domainId)
+               && Objects.equal(this.timeZone, that.timeZone)
+               && Objects.equal(this.apiKey, that.apiKey)
+               && Objects.equal(this.secretKey, that.secretKey);
+   }
+   
+   protected ToStringHelper string() {
+      return Objects.toStringHelper(this)
+            .add("id", id).add("name", name).add("firstName", firstName).add("lastName", lastName).add("email", email)
+            .add("created", created).add("state", state).add("account", account).add("accountType", accountType).add("domain", domain)
+            .add("domainId", domainId).add("timeZone", timeZone).add("apiKey", apiKey).add("secretKey", secretKey);
+   }
+   
+   @Override
    public String toString() {
-      return "User{" +
-            "id=" + id +
-            ", name='" + name + '\'' +
-            ", firstName='" + firstName + '\'' +
-            ", lastName='" + lastName + '\'' +
-            ", email='" + email + '\'' +
-            ", created=" + created +
-            ", state='" + state + '\'' +
-            ", account='" + account + '\'' +
-            ", accountType=" + accountType +
-            ", domain='" + domain + '\'' +
-            ", domainId=" + domainId +
-            ", timeZone='" + timeZone + '\'' +
-            ", apiKey='" + apiKey + '\'' +
-            ", secretKey='" + secretKey + '\'' +
-            '}';
+      return string().toString();
    }
 
 }
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/VMGroup.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/VMGroup.java
index d5e7134..d8c207a 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/VMGroup.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/VMGroup.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -18,74 +18,127 @@
  */
 package org.jclouds.cloudstack.domain;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.beans.ConstructorProperties;
 import java.util.Date;
 
+import javax.inject.Named;
+
+import org.jclouds.javax.annotation.Nullable;
+
 import com.google.common.base.Objects;
-import com.google.gson.annotations.SerializedName;
+import com.google.common.base.Objects.ToStringHelper;
 
 /**
+ * Class VMGroup
+ * 
  * @author Richard Downer
- */
-public class VMGroup implements Comparable<VMGroup> {
+*/
+public class VMGroup {
 
-   public static Builder builder() {
-      return new Builder();
+   public static Builder<?> builder() { 
+      return new ConcreteBuilder();
+   }
+   
+   public Builder<?> toBuilder() { 
+      return new ConcreteBuilder().fromVMGroup(this);
    }
 
-   public static class Builder {
+   public static abstract class Builder<T extends Builder<T>>  {
+      protected abstract T self();
 
-      private String id;
-      private String account;
-      private Date created;
-      private String domain;
-      private String domainId;
-      private String name;
-
-      public Builder id(String id) {
+      protected String id;
+      protected String account;
+      protected Date created;
+      protected String domain;
+      protected String domainId;
+      protected String name;
+   
+      /** 
+       * @see VMGroup#getId()
+       */
+      public T id(String id) {
          this.id = id;
-         return this;
+         return self();
       }
 
-      public Builder account(String account) {
+      /** 
+       * @see VMGroup#getAccount()
+       */
+      public T account(String account) {
          this.account = account;
-         return this;
+         return self();
       }
 
-      public Builder created(Date created) {
+      /** 
+       * @see VMGroup#getCreated()
+       */
+      public T created(Date created) {
          this.created = created;
-         return this;
+         return self();
       }
 
-      public Builder domain(String domain) {
+      /** 
+       * @see VMGroup#getDomain()
+       */
+      public T domain(String domain) {
          this.domain = domain;
-         return this;
+         return self();
       }
 
-      public Builder domainId(String domainId) {
+      /** 
+       * @see VMGroup#getDomainId()
+       */
+      public T domainId(String domainId) {
          this.domainId = domainId;
-         return this;
+         return self();
       }
 
-      public Builder name(String name) {
+      /** 
+       * @see VMGroup#getName()
+       */
+      public T name(String name) {
          this.name = name;
-         return this;
+         return self();
       }
 
       public VMGroup build() {
          return new VMGroup(id, account, created, domain, domainId, name);
       }
+      
+      public T fromVMGroup(VMGroup in) {
+         return this
+                  .id(in.getId())
+                  .account(in.getAccount())
+                  .created(in.getCreated())
+                  .domain(in.getDomain())
+                  .domainId(in.getDomainId())
+                  .name(in.getName());
+      }
    }
 
-   private String id;
-   private String account;
-   private Date created;
-   private String domain;
-   @SerializedName("domainid")
-   private String domainId;
-   private String name;
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
+   }
 
-   public VMGroup(String id, String account, Date created, String domain, String domainId, String name) {
-      this.id = id;
+   private final String id;
+   private final String account;
+   private final Date created;
+   private final String domain;
+   @Named("domainid")
+   private final String domainId;
+   private final String name;
+
+   @ConstructorProperties({
+      "id", "account", "created", "domain", "domainid", "name"
+   })
+   protected VMGroup(String id, @Nullable String account, @Nullable Date created, @Nullable String domain,
+                     @Nullable String domainId, @Nullable String name) {
+      this.id = checkNotNull(id, "id");
       this.account = account;
       this.created = created;
       this.domain = domain;
@@ -94,89 +147,77 @@
    }
 
    /**
-    * present only for serializer
-    */
-   VMGroup() {
-   }
-
-   /**
     * @return the VMGroup's ID
     */
    public String getId() {
-      return id;
+      return this.id;
    }
 
    /**
     * @return the account that owns the VMGroup
     */
+   @Nullable
    public String getAccount() {
-      return account;
+      return this.account;
    }
 
    /**
     * @return the VMGroup's creation timestamp
     */
+   @Nullable
    public Date getCreated() {
-      return created;
+      return this.created;
    }
 
    /**
     * @return the domain that contains the VMGroup
     */
+   @Nullable
    public String getDomain() {
-      return domain;
+      return this.domain;
    }
 
    /**
     * @return the ID of the domain that contains the VMGroup
     */
+   @Nullable
    public String getDomainId() {
-      return domainId;
+      return this.domainId;
    }
 
    /**
     * @return the name of the VMGroup
     */
    public String getName() {
-      return name;
-   }
-
-   @Override
-   public boolean equals(Object o) {
-      if (this == o) return true;
-      if (o == null || getClass() != o.getClass()) return false;
-
-      VMGroup that = (VMGroup) o;
-
-      if (!Objects.equal(domainId, that.domainId)) return false;
-      if (!Objects.equal(id, that.id)) return false;
-      if (!Objects.equal(account, that.account)) return false;
-      if (!Objects.equal(created, that.created)) return false;
-      if (!Objects.equal(domain, that.domain)) return false;
-      if (!Objects.equal(name, that.name)) return false;
-
-      return true;
+      return this.name;
    }
 
    @Override
    public int hashCode() {
-       return Objects.hashCode(domainId, id, account, created, domain, name);
+      return Objects.hashCode(id, account, created, domain, domainId, name);
    }
 
    @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      VMGroup that = VMGroup.class.cast(obj);
+      return Objects.equal(this.id, that.id)
+               && Objects.equal(this.account, that.account)
+               && Objects.equal(this.created, that.created)
+               && Objects.equal(this.domain, that.domain)
+               && Objects.equal(this.domainId, that.domainId)
+               && Objects.equal(this.name, that.name);
+   }
+   
+   protected ToStringHelper string() {
+      return Objects.toStringHelper(this)
+            .add("id", id).add("account", account).add("created", created).add("domain", domain).add("domainId", domainId).add("name", name);
+   }
+   
+   @Override
    public String toString() {
-      return "VMGroup{" +
-            "id=" + id +
-            ", account='" + account + '\'' +
-            ", created=" + created +
-            ", domain='" + domain + '\'' +
-            ", domainId=" + domainId +
-            ", name='" + name + '\'' +
-            '}';
+      return string().toString();
    }
 
-   @Override
-   public int compareTo(VMGroup vmGroup) {
-      return id.compareTo(vmGroup.getId());
-   }
 }
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/VirtualMachine.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/VirtualMachine.java
index 53be570..6a6b442 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/VirtualMachine.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/VirtualMachine.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -20,407 +20,613 @@
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import java.beans.ConstructorProperties;
 import java.util.Date;
 import java.util.Set;
 
-import javax.annotation.Nullable;
+import javax.inject.Named;
+
+import org.jclouds.javax.annotation.Nullable;
 
 import com.google.common.base.CaseFormat;
 import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
 import com.google.common.base.Preconditions;
 import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
-import com.google.gson.annotations.SerializedName;
 
 /**
+ * Class VirtualMachine
  * 
  * @author Adrian Cole
- */
-public class VirtualMachine implements Comparable<VirtualMachine> {
-   public static Builder builder() {
-      return new Builder();
-   }
+*/
+public class VirtualMachine {
 
-   public static class Builder {
-      private String id;
-      private String account;
-      private long cpuCount;
-      private long cpuSpeed;
-      private String cpuUsed;
-      private String displayName;
-      private Date created;
-      private String domain;
-      private String domainId;
-      private boolean usesVirtualNetwork;
-      private String group;
-      private String groupId;
-      private String guestOSId;
-      private boolean HAEnabled;
-      private String hostId;
-      private String hostname;
-      private String IPAddress;
-      private String ISODisplayText;
-      private String ISOId;
-      private String ISOName;
-      private String jobId;
-      private Integer jobStatus;
-      private long memory;
-      private String name;
-      private Long networkKbsRead;
-      private Long networkKbsWrite;
-      private String password;
-      private boolean passwordEnabled;
-      private String publicIP;
-      private String publicIPId;
-      private String rootDeviceId;
-      private String rootDeviceType;
-      private String serviceOfferingId;
-      private String serviceOfferingName;
-      private State state;
-      private String templateDisplayText;
-      private String templateId;
-      private String templateName;
-      private String zoneId;
-      private String zoneName;
-      private Set<NIC> nics = ImmutableSet.<NIC> of();
-      private String hypervisor;
-      private Set<SecurityGroup> securityGroups = ImmutableSet.<SecurityGroup> of();
-
-      public Builder id(String id) {
-         this.id = id;
-         return this;
-      }
-
-      public Builder account(String account) {
-         this.account = account;
-         return this;
-      }
-
-      public Builder cpuCount(long cpuCount) {
-         this.cpuCount = cpuCount;
-         return this;
-      }
-
-      public Builder cpuSpeed(long cpuSpeed) {
-         this.cpuSpeed = cpuSpeed;
-         return this;
-      }
-
-      public Builder cpuUsed(String cpuUsed) {
-         this.cpuUsed = cpuUsed;
-         return this;
-      }
-
-      public Builder displayName(String displayName) {
-         this.displayName = displayName;
-         return this;
-      }
-
-      public Builder created(Date created) {
-         this.created = created;
-         return this;
-      }
-
-      public Builder domain(String domain) {
-         this.domain = domain;
-         return this;
-      }
-
-      public Builder domainId(String domainId) {
-         this.domainId = domainId;
-         return this;
-      }
-
-      public Builder usesVirtualNetwork(boolean usesVirtualNetwork) {
-         this.usesVirtualNetwork = usesVirtualNetwork;
-         return this;
-      }
-
-      public Builder group(String group) {
-         this.group = group;
-         return this;
-      }
-
-      public Builder groupId(String groupId) {
-         this.groupId = groupId;
-         return this;
-      }
-
-      public Builder guestOSId(String guestOSId) {
-         this.guestOSId = guestOSId;
-         return this;
-      }
-
-      public Builder isHAEnabled(boolean HAEnabled) {
-         this.HAEnabled = HAEnabled;
-         return this;
-      }
-
-      public Builder hostId(String hostId) {
-         this.hostId = hostId;
-         return this;
-      }
-
-      public Builder hostname(String hostname) {
-         this.hostname = hostname;
-         return this;
-      }
-
-      public Builder IPAddress(String IPAddress) {
-         this.IPAddress = IPAddress;
-         return this;
-      }
-
-      public Builder ISODisplayText(String ISODisplayText) {
-         this.ISODisplayText = ISODisplayText;
-         return this;
-      }
-
-      public Builder ISOId(String ISOId) {
-         this.ISOId = ISOId;
-         return this;
-      }
-
-      public Builder ISOName(String ISOName) {
-         this.ISOName = ISOName;
-         return this;
-      }
-
-      public Builder jobId(String jobId) {
-         this.jobId = jobId;
-         return this;
-      }
-
-      public Builder jobStatus(int jobStatus) {
-         this.jobStatus = jobStatus;
-         return this;
-      }
-
-      public Builder memory(long memory) {
-         this.memory = memory;
-         return this;
-      }
-
-      public Builder name(String name) {
-         this.name = name;
-         return this;
-      }
-
-      public Builder networkKbsRead(Long networkKbsRead) {
-         this.networkKbsRead = networkKbsRead;
-         return this;
-      }
-
-      public Builder networkKbsWrite(Long networkKbsWrite) {
-         this.networkKbsWrite = networkKbsWrite;
-         return this;
-      }
-
-      public Builder password(String password) {
-         this.password = password;
-         return this;
-      }
-
-      public Builder passwordEnabled(boolean passwordEnabled) {
-         this.passwordEnabled = passwordEnabled;
-         return this;
-      }
-
-      public Builder publicIP(String publicIP) {
-         this.publicIP = publicIP;
-         return this;
-      }
-
-      public Builder publicIPId(String publicIPId) {
-         this.publicIPId = publicIPId;
-         return this;
-      }
-
-      public Builder rootDeviceId(String rootDeviceId) {
-         this.rootDeviceId = rootDeviceId;
-         return this;
-      }
-
-      public Builder rootDeviceType(String rootDeviceType) {
-         this.rootDeviceType = rootDeviceType;
-         return this;
-      }
-
-      public Builder serviceOfferingId(String serviceOfferingId) {
-         this.serviceOfferingId = serviceOfferingId;
-         return this;
-      }
-
-      public Builder serviceOfferingName(String serviceOfferingName) {
-         this.serviceOfferingName = serviceOfferingName;
-         return this;
-      }
-
-      public Builder state(State state) {
-         this.state = state;
-         return this;
-      }
-
-      public Builder templateDisplayText(String templateDisplayText) {
-         this.templateDisplayText = templateDisplayText;
-         return this;
-      }
-
-      public Builder templateId(String templateId) {
-         this.templateId = templateId;
-         return this;
-      }
-
-      public Builder templateName(String templateName) {
-         this.templateName = templateName;
-         return this;
-      }
-
-      public Builder zoneId(String zoneId) {
-         this.zoneId = zoneId;
-         return this;
-      }
-
-      public Builder zoneName(String zoneName) {
-         this.zoneName = zoneName;
-         return this;
-      }
-
-      public Builder nics(Iterable<NIC> nics) {
-         this.nics = ImmutableSet.<NIC> copyOf(checkNotNull(nics, "nics"));
-         return this;
-      }
-
-      public Builder hypervisor(String hypervisor) {
-         this.hypervisor = hypervisor;
-         return this;
-      }
-
-      public Builder securityGroups(Set<SecurityGroup> securityGroups) {
-         this.securityGroups = ImmutableSet.<SecurityGroup> copyOf(checkNotNull(securityGroups, "securityGroups"));
-         return this;
-      }
-
-      public VirtualMachine build() {
-         return new VirtualMachine(id, account, cpuCount, cpuSpeed, cpuUsed, displayName, created, domain, domainId,
-               usesVirtualNetwork, group, groupId, guestOSId, HAEnabled, hostId, hostname, IPAddress, ISODisplayText,
-               ISOId, ISOName, jobId, jobStatus, memory, name, networkKbsRead, networkKbsWrite, password,
-                                   passwordEnabled, publicIP, publicIPId, rootDeviceId, rootDeviceType, securityGroups, serviceOfferingId, serviceOfferingName,
-               state, templateDisplayText, templateId, templateName, zoneId, zoneName, nics, hypervisor);
-      }
-   }
-
+   /**
+    */
    public static enum State {
       STARTING, RUNNING, STOPPING, STOPPED, DESTROYED, EXPUNGING, MIGRATING, ERROR, UNKNOWN, SHUTDOWNED, UNRECOGNIZED;
       @Override
       public String toString() {
-         return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, name());
+      return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, name());
       }
-
+      
       public static State fromValue(String state) {
-         try {
-            return valueOf(CaseFormat.UPPER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, checkNotNull(state, "state")));
-         } catch (IllegalArgumentException e) {
-            return UNRECOGNIZED;
-         }
+      try {
+      return valueOf(CaseFormat.UPPER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, checkNotNull(state, "state")));
+      } catch (IllegalArgumentException e) {
+      return UNRECOGNIZED;
       }
-
+      }
+      
    }
 
-   private String id;
-   private String account;
-   @SerializedName("cpunumber")
-   private long cpuCount;
-   @SerializedName("cpuspeed")
-   private long cpuSpeed;
-   @SerializedName("cpuused")
-   private String cpuUsed;
-   @SerializedName("displayname")
-   private String displayName;
-   private Date created;
-   private String domain;
-   @SerializedName("domainid")
-   private String domainId;
-   @SerializedName("forvirtualnetwork")
-   private boolean usesVirtualNetwork;
-   private String group;
-   @SerializedName("groupid")
-   private String groupId;
-   @SerializedName("guestosid")
-   private String guestOSId;
-   @SerializedName("haenable")
-   private boolean HAEnabled;
-   @SerializedName("hostid")
-   private String hostId;
-   private String hostname;
-   @SerializedName("ipaddress")
-   private String IPAddress;
-   @SerializedName("isodisplaytext")
-   private String ISODisplayText;
-   @SerializedName("isoid")
-   private String ISOId;
-   @SerializedName("isoname")
-   private String ISOName;
-   @SerializedName("jobid")
-   @Nullable
-   private String jobId;
-   @SerializedName("jobstatus")
-   @Nullable
-   private Integer jobStatus;
-   private long memory;
-   private String name;
-   @SerializedName("networkkbsread")
-   private Long networkKbsRead;
-   @SerializedName("networkkbswrite")
-   private Long networkKbsWrite;
-   @Nullable
-   private String password;
-   @SerializedName("passwordenabled")
-   private boolean passwordEnabled;
-   @SerializedName("publicip")
-   private String publicIP;
-   @SerializedName("publicipid")
-   private String publicIPId;
-   @SerializedName("rootdeviceid")
-   private String rootDeviceId;
-   @SerializedName("rootdevicetype")
-   private String rootDeviceType;
-   @SerializedName("serviceofferingid")
-   private String serviceOfferingId;
-   @SerializedName("serviceofferingname")
-   private String serviceOfferingName;
-   private State state;
-   @SerializedName("templatedisplaytext")
-   private String templateDisplayText;
-   @SerializedName("templateid")
-   private String templateId;
-   @SerializedName("templatename")
-   private String templateName;
-   @SerializedName("zoneid")
-   private String zoneId;
-   @SerializedName("zonename")
-   private String zoneName;
-   @SerializedName("nic")
-   private Set<NIC> nics = ImmutableSet.<NIC> of();
-   private String hypervisor;
-   @SerializedName("securitygroup")
-   private Set<SecurityGroup> securityGroups = ImmutableSet.<SecurityGroup> of();
+   public static Builder<?> builder() { 
+      return new ConcreteBuilder();
+   }
+   
+   public Builder<?> toBuilder() { 
+      return new ConcreteBuilder().fromVirtualMachine(this);
+   }
 
-   public VirtualMachine(String id, String account, long cpuCount, long cpuSpeed, String cpuUsed, String displayName,
-         Date created, String domain, String domainId, boolean usesVirtualNetwork, String group, String groupId,
-         String guestOSId, boolean hAEnabled, String hostId, String hostname, String iPAddress, String iSODisplayText,
-         String iSOId, String iSOName, String jobId, Integer jobStatus, long memory, String name, Long networkKbsRead,
-                         Long networkKbsWrite, String password, boolean passwordEnabled, String publicIP, String publicIPId, String rootDeviceId, String rootDeviceType,
-         Set<SecurityGroup> securityGroups, String serviceOfferingId, String serviceOfferingName, State state,
-         String templateDisplayText, String templateId, String templateName, String zoneId, String zoneName, Set<NIC> nics,
-         String hypervisor) {
+   public static abstract class Builder<T extends Builder<T>>  {
+      protected abstract T self();
+
+      protected String id;
+      protected String account;
+      protected long cpuCount;
+      protected long cpuSpeed;
+      protected String cpuUsed;
+      protected String displayName;
+      protected Date created;
+      protected String domain;
+      protected String domainId;
+      protected boolean usesVirtualNetwork;
+      protected String group;
+      protected String groupId;
+      protected String guestOSId;
+      protected boolean HAEnabled;
+      protected String hostId;
+      protected String hostname;
+      protected String IPAddress;
+      protected String ISODisplayText;
+      protected String ISOId;
+      protected String ISOName;
+      protected String jobId;
+      protected Integer jobStatus;
+      protected long memory;
+      protected String name;
+      protected Long networkKbsRead;
+      protected Long networkKbsWrite;
+      protected String password;
+      protected boolean passwordEnabled;
+      protected String publicIP;
+      protected String publicIPId;
+      protected String rootDeviceId;
+      protected String rootDeviceType;
+      protected String serviceOfferingId;
+      protected String serviceOfferingName;
+      protected VirtualMachine.State state;
+      protected String templateDisplayText;
+      protected String templateId;
+      protected String templateName;
+      protected String zoneId;
+      protected String zoneName;
+      protected Set<NIC> nics = ImmutableSet.of();
+      protected String hypervisor;
+      protected Set<SecurityGroup> securityGroups = ImmutableSet.of();
+   
+      /** 
+       * @see VirtualMachine#getId()
+       */
+      public T id(String id) {
+         this.id = id;
+         return self();
+      }
+
+      /** 
+       * @see VirtualMachine#getAccount()
+       */
+      public T account(String account) {
+         this.account = account;
+         return self();
+      }
+
+      /** 
+       * @see VirtualMachine#getCpuCount()
+       */
+      public T cpuCount(long cpuCount) {
+         this.cpuCount = cpuCount;
+         return self();
+      }
+
+      /** 
+       * @see VirtualMachine#getCpuSpeed()
+       */
+      public T cpuSpeed(long cpuSpeed) {
+         this.cpuSpeed = cpuSpeed;
+         return self();
+      }
+
+      /** 
+       * @see VirtualMachine#getCpuUsed()
+       */
+      public T cpuUsed(String cpuUsed) {
+         this.cpuUsed = cpuUsed;
+         return self();
+      }
+
+      /** 
+       * @see VirtualMachine#getDisplayName()
+       */
+      public T displayName(String displayName) {
+         this.displayName = displayName;
+         return self();
+      }
+
+      /** 
+       * @see VirtualMachine#getCreated()
+       */
+      public T created(Date created) {
+         this.created = created;
+         return self();
+      }
+
+      /** 
+       * @see VirtualMachine#getDomain()
+       */
+      public T domain(String domain) {
+         this.domain = domain;
+         return self();
+      }
+
+      /** 
+       * @see VirtualMachine#getDomainId()
+       */
+      public T domainId(String domainId) {
+         this.domainId = domainId;
+         return self();
+      }
+
+      /** 
+       * @see VirtualMachine#usesVirtualNetwork()
+       */
+      public T usesVirtualNetwork(boolean usesVirtualNetwork) {
+         this.usesVirtualNetwork = usesVirtualNetwork;
+         return self();
+      }
+
+      /** 
+       * @see VirtualMachine#getGroup()
+       */
+      public T group(String group) {
+         this.group = group;
+         return self();
+      }
+
+      /** 
+       * @see VirtualMachine#getGroupId()
+       */
+      public T groupId(String groupId) {
+         this.groupId = groupId;
+         return self();
+      }
+
+      /** 
+       * @see VirtualMachine#getGuestOSId()
+       */
+      public T guestOSId(String guestOSId) {
+         this.guestOSId = guestOSId;
+         return self();
+      }
+
+      /** 
+       * @see VirtualMachine#isHAEnabled()
+       */
+      public T isHAEnabled(boolean HAEnabled) {
+         this.HAEnabled = HAEnabled;
+         return self();
+      }
+
+      /** 
+       * @see VirtualMachine#getHostId()
+       */
+      public T hostId(String hostId) {
+         this.hostId = hostId;
+         return self();
+      }
+
+      /** 
+       * @see VirtualMachine#getHostname()
+       */
+      public T hostname(String hostname) {
+         this.hostname = hostname;
+         return self();
+      }
+
+      /** 
+       * @see VirtualMachine#getIPAddress()
+       */
+      public T IPAddress(String IPAddress) {
+         this.IPAddress = IPAddress;
+         return self();
+      }
+
+      /** 
+       * @see VirtualMachine#getISODisplayText()
+       */
+      public T ISODisplayText(String ISODisplayText) {
+         this.ISODisplayText = ISODisplayText;
+         return self();
+      }
+
+      /** 
+       * @see VirtualMachine#getISOId()
+       */
+      public T ISOId(String ISOId) {
+         this.ISOId = ISOId;
+         return self();
+      }
+
+      /** 
+       * @see VirtualMachine#getISOName()
+       */
+      public T ISOName(String ISOName) {
+         this.ISOName = ISOName;
+         return self();
+      }
+
+      /** 
+       * @see VirtualMachine#getJobId()
+       */
+      public T jobId(String jobId) {
+         this.jobId = jobId;
+         return self();
+      }
+
+      /** 
+       * @see VirtualMachine#getJobStatus()
+       */
+      public T jobStatus(Integer jobStatus) {
+         this.jobStatus = jobStatus;
+         return self();
+      }
+
+      /** 
+       * @see VirtualMachine#getMemory()
+       */
+      public T memory(long memory) {
+         this.memory = memory;
+         return self();
+      }
+
+      /** 
+       * @see VirtualMachine#getName()
+       */
+      public T name(String name) {
+         this.name = name;
+         return self();
+      }
+
+      /** 
+       * @see VirtualMachine#getNetworkKbsRead()
+       */
+      public T networkKbsRead(Long networkKbsRead) {
+         this.networkKbsRead = networkKbsRead;
+         return self();
+      }
+
+      /** 
+       * @see VirtualMachine#getNetworkKbsWrite()
+       */
+      public T networkKbsWrite(Long networkKbsWrite) {
+         this.networkKbsWrite = networkKbsWrite;
+         return self();
+      }
+
+      /** 
+       * @see VirtualMachine#getPassword()
+       */
+      public T password(String password) {
+         this.password = password;
+         return self();
+      }
+
+      /** 
+       * @see VirtualMachine#isPasswordEnabled()
+       */
+      public T passwordEnabled(boolean passwordEnabled) {
+         this.passwordEnabled = passwordEnabled;
+         return self();
+      }
+
+      /** 
+       * @see VirtualMachine#getPublicIP()
+       */
+      public T publicIP(String publicIP) {
+         this.publicIP = publicIP;
+         return self();
+      }
+
+      /** 
+       * @see VirtualMachine#getPublicIPId()
+       */
+      public T publicIPId(String publicIPId) {
+         this.publicIPId = publicIPId;
+         return self();
+      }
+
+      /** 
+       * @see VirtualMachine#getRootDeviceId()
+       */
+      public T rootDeviceId(String rootDeviceId) {
+         this.rootDeviceId = rootDeviceId;
+         return self();
+      }
+
+      /** 
+       * @see VirtualMachine#getRootDeviceType()
+       */
+      public T rootDeviceType(String rootDeviceType) {
+         this.rootDeviceType = rootDeviceType;
+         return self();
+      }
+
+      /** 
+       * @see VirtualMachine#getServiceOfferingId()
+       */
+      public T serviceOfferingId(String serviceOfferingId) {
+         this.serviceOfferingId = serviceOfferingId;
+         return self();
+      }
+
+      /** 
+       * @see VirtualMachine#getServiceOfferingName()
+       */
+      public T serviceOfferingName(String serviceOfferingName) {
+         this.serviceOfferingName = serviceOfferingName;
+         return self();
+      }
+
+      /** 
+       * @see VirtualMachine#getState()
+       */
+      public T state(VirtualMachine.State state) {
+         this.state = state;
+         return self();
+      }
+
+      /** 
+       * @see VirtualMachine#getTemplateDisplayText()
+       */
+      public T templateDisplayText(String templateDisplayText) {
+         this.templateDisplayText = templateDisplayText;
+         return self();
+      }
+
+      /** 
+       * @see VirtualMachine#getTemplateId()
+       */
+      public T templateId(String templateId) {
+         this.templateId = templateId;
+         return self();
+      }
+
+      /** 
+       * @see VirtualMachine#getTemplateName()
+       */
+      public T templateName(String templateName) {
+         this.templateName = templateName;
+         return self();
+      }
+
+      /** 
+       * @see VirtualMachine#getZoneId()
+       */
+      public T zoneId(String zoneId) {
+         this.zoneId = zoneId;
+         return self();
+      }
+
+      /** 
+       * @see VirtualMachine#getZoneName()
+       */
+      public T zoneName(String zoneName) {
+         this.zoneName = zoneName;
+         return self();
+      }
+
+      /** 
+       * @see VirtualMachine#getNICs()
+       */
+      public T nics(Set<NIC> nics) {
+         this.nics = ImmutableSet.copyOf(checkNotNull(nics, "nics"));      
+         return self();
+      }
+
+      public T nics(NIC... in) {
+         return nics(ImmutableSet.copyOf(in));
+      }
+
+      /** 
+       * @see VirtualMachine#getHypervisor()
+       */
+      public T hypervisor(String hypervisor) {
+         this.hypervisor = hypervisor;
+         return self();
+      }
+
+      /** 
+       * @see VirtualMachine#getSecurityGroups()
+       */
+      public T securityGroups(Set<SecurityGroup> securityGroups) {
+         this.securityGroups = ImmutableSet.copyOf(checkNotNull(securityGroups, "securityGroups"));      
+         return self();
+      }
+
+      public T securityGroups(SecurityGroup... in) {
+         return securityGroups(ImmutableSet.copyOf(in));
+      }
+
+      public VirtualMachine build() {
+         return new VirtualMachine(id, account, cpuCount, cpuSpeed, cpuUsed, displayName, created, domain, domainId,
+               usesVirtualNetwork, group, groupId, guestOSId, HAEnabled, hostId, hostname, IPAddress, ISODisplayText, ISOId,
+               ISOName, jobId, jobStatus, memory, name, networkKbsRead, networkKbsWrite, password, passwordEnabled, publicIP,
+               publicIPId, rootDeviceId, rootDeviceType, serviceOfferingId, serviceOfferingName, state, templateDisplayText,
+               templateId, templateName, zoneId, zoneName, nics, hypervisor, securityGroups);
+      }
+      
+      public T fromVirtualMachine(VirtualMachine in) {
+         return this
+                  .id(in.getId())
+                  .account(in.getAccount())
+                  .cpuCount(in.getCpuCount())
+                  .cpuSpeed(in.getCpuSpeed())
+                  .cpuUsed(in.getCpuUsedAsString())
+                  .displayName(in.getDisplayName())
+                  .created(in.getCreated())
+                  .domain(in.getDomain())
+                  .domainId(in.getDomainId())
+                  .usesVirtualNetwork(in.usesVirtualNetwork())
+                  .group(in.getGroup())
+                  .groupId(in.getGroupId())
+                  .guestOSId(in.getGuestOSId())
+                  .isHAEnabled(in.isHAEnabled())
+                  .hostId(in.getHostId())
+                  .hostname(in.getHostname())
+                  .IPAddress(in.getIPAddress())
+                  .ISODisplayText(in.getISODisplayText())
+                  .ISOId(in.getISOId())
+                  .ISOName(in.getISOName())
+                  .jobId(in.getJobId())
+                  .jobStatus(in.getJobStatus())
+                  .memory(in.getMemory())
+                  .name(in.getName())
+                  .networkKbsRead(in.getNetworkKbsRead())
+                  .networkKbsWrite(in.getNetworkKbsWrite())
+                  .password(in.getPassword())
+                  .passwordEnabled(in.isPasswordEnabled())
+                  .publicIP(in.getPublicIP())
+                  .publicIPId(in.getPublicIPId())
+                  .rootDeviceId(in.getRootDeviceId())
+                  .rootDeviceType(in.getRootDeviceType())
+                  .serviceOfferingId(in.getServiceOfferingId())
+                  .serviceOfferingName(in.getServiceOfferingName())
+                  .state(in.getState())
+                  .templateDisplayText(in.getTemplateDisplayText())
+                  .templateId(in.getTemplateId())
+                  .templateName(in.getTemplateName())
+                  .zoneId(in.getZoneId())
+                  .zoneName(in.getZoneName())
+                  .nics(in.getNICs())
+                  .hypervisor(in.getHypervisor())
+                  .securityGroups(in.getSecurityGroups());
+      }
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
+   }
+
+   private final String id;
+   private final String account;
+   @Named("cpunumber")
+   private final long cpuCount;
+   @Named("cpuspeed")
+   private final long cpuSpeed;
+   @Named("cpuused")
+   private final String cpuUsed;
+   @Named("displayname")
+   private final String displayName;
+   private final Date created;
+   private final String domain;
+   @Named("domainid")
+   private final String domainId;
+   @Named("forvirtualnetwork")
+   private final boolean usesVirtualNetwork;
+   private final String group;
+   @Named("groupid")
+   private final String groupId;
+   @Named("guestosid")
+   private final String guestOSId;
+   @Named("haenable")
+   private final boolean HAEnabled;
+   @Named("hostid")
+   private final String hostId;
+   private final String hostname;
+   @Named("ipaddress")
+   private final String IPAddress;
+   @Named("isodisplaytext")
+   private final String ISODisplayText;
+   @Named("isoid")
+   private final String ISOId;
+   @Named("isoname")
+   private final String ISOName;
+   @Named("jobid")
+   private final String jobId;
+   @Named("jobstatus")
+   private final Integer jobStatus;
+   private final long memory;
+   private final String name;
+   @Named("networkkbsread")
+   private final Long networkKbsRead;
+   @Named("networkkbswrite")
+   private final Long networkKbsWrite;
+   private final String password;
+   @Named("passwordenabled")
+   private final boolean passwordEnabled;
+   @Named("publicip")
+   private final String publicIP;
+   @Named("publicipid")
+   private final String publicIPId;
+   @Named("rootdeviceid")
+   private final String rootDeviceId;
+   @Named("rootdevicetype")
+   private final String rootDeviceType;
+   @Named("serviceofferingid")
+   private final String serviceOfferingId;
+   @Named("serviceofferingname")
+   private final String serviceOfferingName;
+   private final VirtualMachine.State state;
+   @Named("templatedisplaytext")
+   private final String templateDisplayText;
+   @Named("templateid")
+   private final String templateId;
+   @Named("templatename")
+   private final String templateName;
+   @Named("zoneid")
+   private final String zoneId;
+   @Named("zonename")
+   private final String zoneName;
+   @Named("nic")
+   private final Set<NIC> nics;
+   private final String hypervisor;
+   @Named("securitygroup")
+   private final Set<SecurityGroup> securityGroups;
+
+   @ConstructorProperties({
+      "id", "account", "cpunumber", "cpuspeed", "cpuused", "displayname", "created", "domain", "domainid", "forvirtualnetwork", "group", "groupid", "guestosid", "haenable", "hostid", "hostname", "ipaddress", "isodisplaytext", "isoid", "isoname", "jobid", "jobstatus", "memory", "name", "networkkbsread", "networkkbswrite", "password", "passwordenabled", "publicip", "publicipid", "rootdeviceid", "rootdevicetype", "serviceofferingid", "serviceofferingname", "state", "templatedisplaytext", "templateid", "templatename", "zoneid", "zonename", "nic", "hypervisor", "securitygroup"
+   })
+   protected VirtualMachine(String id, @Nullable String account, long cpuCount, long cpuSpeed, @Nullable String cpuUsed,
+                            @Nullable String displayName, @Nullable Date created, @Nullable String domain, @Nullable String domainId,
+                            boolean usesVirtualNetwork, @Nullable String group, @Nullable String groupId, @Nullable String guestOSId,
+                            boolean HAEnabled, @Nullable String hostId, @Nullable String hostname, String IPAddress, String ISODisplayText,
+                            @Nullable String ISOId, @Nullable String ISOName, @Nullable String jobId, @Nullable Integer jobStatus,
+                            long memory, @Nullable String name, @Nullable Long networkKbsRead, @Nullable Long networkKbsWrite, @Nullable String password,
+                            boolean passwordEnabled, @Nullable String publicIP, @Nullable String publicIPId, @Nullable String rootDeviceId,
+                            @Nullable String rootDeviceType, @Nullable String serviceOfferingId, @Nullable String serviceOfferingName,
+                            @Nullable VirtualMachine.State state, @Nullable String templateDisplayText, @Nullable String templateId,
+                            @Nullable String templateName, @Nullable String zoneId, @Nullable String zoneName, @Nullable Set<NIC> nics,
+                            @Nullable String hypervisor, @Nullable Set<SecurityGroup> securityGroups) {
       Preconditions.checkArgument(Strings.isNullOrEmpty(cpuUsed) || cpuUsed.matches("^[0-9\\.]+%$"), "cpuUsed value should be a decimal number followed by %");
-      this.id = id;
+      this.id = checkNotNull(id, "id");
       this.account = account;
       this.cpuCount = cpuCount;
       this.cpuSpeed = cpuSpeed;
-      this.cpuUsed = cpuUsed != null ? cpuUsed + "" : null;
+      this.cpuUsed = cpuUsed;
       this.displayName = displayName;
       this.created = created;
       this.domain = domain;
@@ -429,13 +635,13 @@
       this.group = group;
       this.groupId = groupId;
       this.guestOSId = guestOSId;
-      this.HAEnabled = hAEnabled;
+      this.HAEnabled = HAEnabled;
       this.hostId = hostId;
       this.hostname = hostname;
-      this.IPAddress = iPAddress;
-      this.ISODisplayText = iSODisplayText;
-      this.ISOId = iSOId;
-      this.ISOName = iSOName;
+      this.IPAddress = IPAddress;
+      this.ISODisplayText = ISODisplayText;
+      this.ISOId = ISOId;
+      this.ISOName = ISOName;
       this.jobId = jobId;
       this.jobStatus = jobStatus;
       this.memory = memory;
@@ -448,7 +654,6 @@
       this.publicIPId = publicIPId;
       this.rootDeviceId = rootDeviceId;
       this.rootDeviceType = rootDeviceType;
-      this.securityGroups = ImmutableSet.copyOf(checkNotNull(securityGroups, "securityGroups"));
       this.serviceOfferingId = serviceOfferingId;
       this.serviceOfferingName = serviceOfferingName;
       this.state = state;
@@ -457,43 +662,38 @@
       this.templateName = templateName;
       this.zoneId = zoneId;
       this.zoneName = zoneName;
-      this.nics = nics;
+      this.nics = nics == null ? ImmutableSet.<NIC>of() : ImmutableSet.copyOf(nics);      
       this.hypervisor = hypervisor;
-   }
-
-   /**
-    * present only for serializer
-    * 
-    */
-   VirtualMachine() {
+      this.securityGroups = securityGroups == null ? ImmutableSet.<SecurityGroup>of() : ImmutableSet.copyOf(securityGroups);      
    }
 
    /**
     * @return the ID of the virtual machine
     */
    public String getId() {
-      return id;
+      return this.id;
    }
 
    /**
     * @return the account associated with the virtual machine
     */
+   @Nullable
    public String getAccount() {
-      return account;
+      return this.account;
    }
 
    /**
     * @return the number of cpu this virtual machine is running with
     */
    public long getCpuCount() {
-      return cpuCount;
+      return this.cpuCount;
    }
 
    /**
     * @return the speed of each cpu
     */
    public long getCpuSpeed() {
-      return cpuSpeed;
+      return this.cpuSpeed;
    }
 
    /**
@@ -503,127 +703,138 @@
       return cpuUsed != null ? Float.parseFloat(cpuUsed.substring(0, cpuUsed.length() - 1)) : 0.0f;
    }
 
-   /**
-    * @return user generated name. The name of the virtual machine is returned
-    *         if no displayname exists.
-    */
+   private String getCpuUsedAsString() {
+      return cpuUsed;
+   }
+
+      /**
+      * @return user generated name. The name of the virtual machine is returned
+           if no displayname exists.
+      */
+   @Nullable
    public String getDisplayName() {
-      return displayName;
+      return this.displayName;
    }
 
    /**
     * @return the date when this virtual machine was created
     */
+   @Nullable
    public Date getCreated() {
-      return created;
+      return this.created;
    }
 
    /**
     * @return the name of the domain in which the virtual machine exists
     */
+   @Nullable
    public String getDomain() {
-      return domain;
+      return this.domain;
    }
 
    /**
     * @return the ID of the domain in which the virtual machine exists
     */
+   @Nullable
    public String getDomainId() {
-      return domainId;
+      return this.domainId;
    }
 
    /**
     * @return the virtual network for the service offering
     */
    public boolean usesVirtualNetwork() {
-      return usesVirtualNetwork;
+      return this.usesVirtualNetwork;
    }
 
    /**
     * @return the group name of the virtual machine
     */
+   @Nullable
    public String getGroup() {
-      return group;
+      return this.group;
    }
 
    /**
     * @return the group ID of the virtual machine
     */
+   @Nullable
    public String getGroupId() {
-      return groupId;
+      return this.groupId;
    }
 
    /**
     * @return Os type ID of the virtual machine
     */
+   @Nullable
    public String getGuestOSId() {
-      return guestOSId;
+      return this.guestOSId;
    }
 
    /**
     * @return true if high-availability is enabled, false otherwise
     */
    public boolean isHAEnabled() {
-      return HAEnabled;
+      return this.HAEnabled;
    }
 
    /**
     * @return the ID of the host for the virtual machine
     */
+   @Nullable
    public String getHostId() {
-      return hostId;
+      return this.hostId;
    }
 
    /**
     * @return the name of the host for the virtual machine
     */
+   @Nullable
    public String getHostname() {
-      return hostname;
+      return this.hostname;
    }
 
    /**
     * @return the ip address of the virtual machine
     */
+   @Nullable
    public String getIPAddress() {
-      if (IPAddress != null)
-         return IPAddress;
-      // some versions of 2.2.0 do not populate the IP address field
-      if (getNICs().size() > 0) {
-         return Iterables.get(getNICs(), 0).getIPAddress();
-      }
-      return null;
+      return this.IPAddress;
    }
 
    /**
     * @return an alternate display text of the ISO attached to the virtual
-    *         machine
+         machine
     */
+   @Nullable
    public String getISODisplayText() {
-      return ISODisplayText;
+      return this.ISODisplayText;
    }
 
    /**
     * @return the ID of the ISO attached to the virtual machine
     */
+   @Nullable
    public String getISOId() {
-      return ISOId;
+      return this.ISOId;
    }
 
    /**
     * @return the name of the ISO attached to the virtual machine
     */
+   @Nullable
    public String getISOName() {
-      return ISOName;
+      return this.ISOName;
    }
 
    /**
     * @return shows the current pending asynchronous job ID. This tag is not
-    *         returned if no current pending jobs are acting on the virtual
-    *         machine
+         returned if no current pending jobs are acting on the virtual
+         machine
     */
    @Nullable
    public String getJobId() {
-      return jobId;
+      return this.jobId;
    }
 
    /**
@@ -631,35 +842,38 @@
     */
    @Nullable
    public Integer getJobStatus() {
-      return jobStatus;
+      return this.jobStatus;
    }
 
    /**
     * @return the memory allocated for the virtual machine
     */
    public long getMemory() {
-      return memory;
+      return this.memory;
    }
 
    /**
     * @return the name of the virtual machine
     */
+   @Nullable
    public String getName() {
-      return name;
+      return this.name;
    }
 
    /**
     * @return the incoming network traffic on the vm
     */
+   @Nullable
    public Long getNetworkKbsRead() {
-      return networkKbsRead;
+      return this.networkKbsRead;
    }
 
    /**
     * @return the outgoing network traffic on the host
     */
+   @Nullable
    public Long getNetworkKbsWrite() {
-      return networkKbsWrite;
+      return this.networkKbsWrite;
    }
 
    /**
@@ -667,239 +881,205 @@
     */
    @Nullable
    public String getPassword() {
-      return password;
+      return this.password;
    }
 
    /**
     * @return true if the password rest feature is enabled, false otherwise
     */
    public boolean isPasswordEnabled() {
-      return passwordEnabled;
+      return this.passwordEnabled;
    }
 
    /**
     * @return public IP of this virtual machine
     */
+   @Nullable
    public String getPublicIP() {
-      return publicIP;
+      return this.publicIP;
    }
 
    /**
     * @return ID of the public IP of this virtual machine
     */
+   @Nullable
    public String getPublicIPId() {
-      return publicIPId;
+      return this.publicIPId;
    }
 
    /**
     * @return device ID of the root volume
     */
+   @Nullable
    public String getRootDeviceId() {
-      return rootDeviceId;
+      return this.rootDeviceId;
    }
 
    /**
     * @return device type of the root volume
     */
+   @Nullable
    public String getRootDeviceType() {
-      return rootDeviceType;
+      return this.rootDeviceType;
+   }
+
+   /**
+    * @return the ID of the service offering of the virtual machine
+    */
+   @Nullable
+   public String getServiceOfferingId() {
+      return this.serviceOfferingId;
+   }
+
+   /**
+    * @return the name of the service offering of the virtual machine
+    */
+   @Nullable
+   public String getServiceOfferingName() {
+      return this.serviceOfferingName;
+   }
+
+   /**
+    * @return the state of the virtual machine
+    */
+   @Nullable
+   public VirtualMachine.State getState() {
+      return this.state;
+   }
+
+   /**
+    * @return an alternate display text of the template for the virtual machine
+    */
+   @Nullable
+   public String getTemplateDisplayText() {
+      return this.templateDisplayText;
+   }
+
+   /**
+    * @return the ID of the template for the virtual machine. A -1 is returned
+         if the virtual machine was created from an ISO file.
+    */
+   @Nullable
+   public String getTemplateId() {
+      return this.templateId;
+   }
+
+   /**
+    * @return the name of the template for the virtual machine
+    */
+   @Nullable
+   public String getTemplateName() {
+      return this.templateName;
+   }
+
+   /**
+    * @return the ID of the availablility zone for the virtual machine
+    */
+   @Nullable
+   public String getZoneId() {
+      return this.zoneId;
+   }
+
+   /**
+    * @return the name of the availability zone for the virtual machine
+    */
+   @Nullable
+   public String getZoneName() {
+      return this.zoneName;
+   }
+
+   public Set<NIC> getNICs() {
+      return this.nics;
+   }
+
+   /**
+    * @return type of the hypervisor
+    */
+   @Nullable
+   public String getHypervisor() {
+      return this.hypervisor;
    }
 
    /**
     * @return list of security groups associated with the virtual machine
     */
    public Set<SecurityGroup> getSecurityGroups() {
-      return securityGroups;
-   }
-
-   /**
-    * @return the ID of the service offering of the virtual machine
-    */
-   public String getServiceOfferingId() {
-      return serviceOfferingId;
-   }
-
-   /**
-    * @return the name of the service offering of the virtual machine
-    */
-   public String getServiceOfferingName() {
-      return serviceOfferingName;
-   }
-
-   /**
-    * @return the state of the virtual machine
-    */
-   public State getState() {
-      return state;
-   }
-
-   /**
-    * @return an alternate display text of the template for the virtual machine
-    */
-   public String getTemplateDisplayText() {
-      return templateDisplayText;
-   }
-
-   /**
-    * @return the ID of the template for the virtual machine. A -1 is returned
-    *         if the virtual machine was created from an ISO file.
-    */
-   public String getTemplateId() {
-      return templateId;
-   }
-
-   /**
-    * @return the name of the template for the virtual machine
-    */
-   public String getTemplateName() {
-      return templateName;
-   }
-
-   /**
-    * @return the ID of the availablility zone for the virtual machine
-    */
-   public String getZoneId() {
-      return zoneId;
-   }
-
-   /**
-    * @return the name of the availability zone for the virtual machine
-    */
-   public String getZoneName() {
-      return zoneName;
-   }
-
-   /**
-    * @return the list of nics associated with vm
-    */
-   public Set<NIC> getNICs() {
-      return nics;
-   }
-
-   /**
-    * @return type of the hypervisor
-    */
-   public String getHypervisor() {
-      return hypervisor;
-   }
-
-   @Override
-   public boolean equals(Object o) {
-      if (this == o) return true;
-      if (o == null || getClass() != o.getClass()) return false;
-
-      VirtualMachine that = (VirtualMachine) o;
-
-      if (!Objects.equal(id, that.id)) return false;
-      if (!Objects.equal(account, that.account)) return false;
-      if (!Objects.equal(cpuCount, that.cpuCount)) return false;
-      if (!Objects.equal(cpuSpeed, that.cpuSpeed)) return false;
-      if (!Objects.equal(cpuUsed, that.cpuUsed)) return false;
-      if (!Objects.equal(displayName, that.displayName)) return false;
-      if (!Objects.equal(created, that.created)) return false;
-      if (!Objects.equal(domain, that.domain)) return false;
-      if (!Objects.equal(domainId, that.domainId)) return false;
-      if (!Objects.equal(usesVirtualNetwork, that.usesVirtualNetwork)) return false;
-      if (!Objects.equal(group, that.group)) return false;
-      if (!Objects.equal(groupId, that.groupId)) return false;
-      if (!Objects.equal(guestOSId, that.guestOSId)) return false;
-      if (!Objects.equal(HAEnabled, that.HAEnabled)) return false;
-      if (!Objects.equal(hostId, that.hostId)) return false;
-      if (!Objects.equal(hostname, that.hostname)) return false;
-      if (!Objects.equal(IPAddress, that.IPAddress)) return false;
-      if (!Objects.equal(ISODisplayText, that.ISODisplayText)) return false;
-      if (!Objects.equal(ISOId, that.ISOId)) return false;
-      if (!Objects.equal(ISOName, that.ISOName)) return false;
-      if (!Objects.equal(jobId, that.jobId)) return false;
-      if (!Objects.equal(jobStatus, that.jobStatus)) return false;
-      if (!Objects.equal(memory, that.memory)) return false;
-      if (!Objects.equal(name, that.name)) return false;
-      if (!Objects.equal(networkKbsRead, that.networkKbsRead)) return false;
-      if (!Objects.equal(networkKbsWrite, that.networkKbsWrite)) return false;
-      if (!Objects.equal(password, that.password)) return false;
-      if (!Objects.equal(passwordEnabled, that.passwordEnabled)) return false;
-      if (!Objects.equal(publicIP, that.publicIP)) return false;
-      if (!Objects.equal(publicIPId, that.publicIPId)) return false;
-      if (!Objects.equal(rootDeviceId, that.rootDeviceId)) return false;
-      if (!Objects.equal(rootDeviceType, that.rootDeviceType)) return false;
-      if (!Objects.equal(securityGroups, that.securityGroups)) return false;
-      if (!Objects.equal(serviceOfferingId, that.serviceOfferingId)) return false;
-      if (!Objects.equal(serviceOfferingName, that.serviceOfferingName)) return false;
-      if (!Objects.equal(state, that.state)) return false;
-      if (!Objects.equal(templateDisplayText, that.templateDisplayText)) return false;
-      if (!Objects.equal(templateId, that.templateId)) return false;
-      if (!Objects.equal(templateName, that.templateName)) return false;
-      if (!Objects.equal(zoneId, that.zoneId)) return false;
-      if (!Objects.equal(zoneName, that.zoneName)) return false;
-      if (!Objects.equal(nics, that.nics)) return false;
-      if (!Objects.equal(hypervisor, that.hypervisor)) return false;
-
-      return true;
+      return this.securityGroups;
    }
 
    @Override
    public int hashCode() {
-       return Objects.hashCode(id, account, cpuCount, cpuSpeed, cpuUsed, displayName, created,
-                               domain, domainId, usesVirtualNetwork, group, groupId, guestOSId,
-                               HAEnabled, hostId, hostname, IPAddress, ISODisplayText, ISOId,
-                               ISOName, jobId, jobStatus, memory, name, networkKbsRead,
-                               networkKbsWrite, password, passwordEnabled, publicIP, publicIPId, rootDeviceId,
-                               rootDeviceType, securityGroups, serviceOfferingId,
-                               serviceOfferingName, state, templateDisplayText, templateId,
-                               templateName, zoneId, zoneName, nics, hypervisor);
+      return Objects.hashCode(id, account, cpuCount, cpuSpeed, cpuUsed, displayName, created, domain, domainId, usesVirtualNetwork, group, groupId, guestOSId, HAEnabled, hostId, hostname, IPAddress, ISODisplayText, ISOId, ISOName, jobId, jobStatus, memory, name, networkKbsRead, networkKbsWrite, password, passwordEnabled, publicIP, publicIPId, rootDeviceId, rootDeviceType, serviceOfferingId, serviceOfferingName, state, templateDisplayText, templateId, templateName, zoneId, zoneName, nics, hypervisor, securityGroups);
    }
 
    @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      VirtualMachine that = VirtualMachine.class.cast(obj);
+      return Objects.equal(this.id, that.id)
+               && Objects.equal(this.account, that.account)
+               && Objects.equal(this.cpuCount, that.cpuCount)
+               && Objects.equal(this.cpuSpeed, that.cpuSpeed)
+               && Objects.equal(this.cpuUsed, that.cpuUsed)
+               && Objects.equal(this.displayName, that.displayName)
+               && Objects.equal(this.created, that.created)
+               && Objects.equal(this.domain, that.domain)
+               && Objects.equal(this.domainId, that.domainId)
+               && Objects.equal(this.usesVirtualNetwork, that.usesVirtualNetwork)
+               && Objects.equal(this.group, that.group)
+               && Objects.equal(this.groupId, that.groupId)
+               && Objects.equal(this.guestOSId, that.guestOSId)
+               && Objects.equal(this.HAEnabled, that.HAEnabled)
+               && Objects.equal(this.hostId, that.hostId)
+               && Objects.equal(this.hostname, that.hostname)
+               && Objects.equal(this.IPAddress, that.IPAddress)
+               && Objects.equal(this.ISODisplayText, that.ISODisplayText)
+               && Objects.equal(this.ISOId, that.ISOId)
+               && Objects.equal(this.ISOName, that.ISOName)
+               && Objects.equal(this.jobId, that.jobId)
+               && Objects.equal(this.jobStatus, that.jobStatus)
+               && Objects.equal(this.memory, that.memory)
+               && Objects.equal(this.name, that.name)
+               && Objects.equal(this.networkKbsRead, that.networkKbsRead)
+               && Objects.equal(this.networkKbsWrite, that.networkKbsWrite)
+               && Objects.equal(this.password, that.password)
+               && Objects.equal(this.passwordEnabled, that.passwordEnabled)
+               && Objects.equal(this.publicIP, that.publicIP)
+               && Objects.equal(this.publicIPId, that.publicIPId)
+               && Objects.equal(this.rootDeviceId, that.rootDeviceId)
+               && Objects.equal(this.rootDeviceType, that.rootDeviceType)
+               && Objects.equal(this.serviceOfferingId, that.serviceOfferingId)
+               && Objects.equal(this.serviceOfferingName, that.serviceOfferingName)
+               && Objects.equal(this.state, that.state)
+               && Objects.equal(this.templateDisplayText, that.templateDisplayText)
+               && Objects.equal(this.templateId, that.templateId)
+               && Objects.equal(this.templateName, that.templateName)
+               && Objects.equal(this.zoneId, that.zoneId)
+               && Objects.equal(this.zoneName, that.zoneName)
+               && Objects.equal(this.nics, that.nics)
+               && Objects.equal(this.hypervisor, that.hypervisor)
+               && Objects.equal(this.securityGroups, that.securityGroups);
+   }
+   
+   protected ToStringHelper string() {
+      return Objects.toStringHelper(this)
+            .add("id", id).add("account", account).add("cpuCount", cpuCount).add("cpuSpeed", cpuSpeed).add("cpuUsed", cpuUsed)
+            .add("displayName", displayName).add("created", created).add("domain", domain).add("domainId", domainId)
+            .add("usesVirtualNetwork", usesVirtualNetwork).add("group", group).add("groupId", groupId).add("guestOSId", guestOSId)
+            .add("HAEnabled", HAEnabled).add("hostId", hostId).add("hostname", hostname).add("IPAddress", IPAddress)
+            .add("ISODisplayText", ISODisplayText).add("ISOId", ISOId).add("ISOName", ISOName).add("jobId", jobId)
+            .add("jobStatus", jobStatus).add("memory", memory).add("name", name).add("networkKbsRead", networkKbsRead)
+            .add("networkKbsWrite", networkKbsWrite).add("password", password).add("passwordEnabled", passwordEnabled)
+            .add("publicIP", publicIP).add("publicIPId", publicIPId).add("rootDeviceId", rootDeviceId).add("rootDeviceType", rootDeviceType)
+            .add("serviceOfferingId", serviceOfferingId).add("serviceOfferingName", serviceOfferingName).add("state", state)
+            .add("templateDisplayText", templateDisplayText).add("templateId", templateId).add("templateName", templateName)
+            .add("zoneId", zoneId).add("zoneName", zoneName).add("nics", nics).add("hypervisor", hypervisor).add("securityGroups", securityGroups);
+   }
+   
+   @Override
    public String toString() {
-      return "VirtualMachine{" +
-            "id=" + id +
-            ", account='" + account + '\'' +
-            ", cpuCount=" + cpuCount +
-            ", cpuSpeed=" + cpuSpeed +
-            ", cpuUsed='" + cpuUsed + '\'' +
-            ", displayName='" + displayName + '\'' +
-            ", created=" + created +
-            ", domain='" + domain + '\'' +
-            ", domainId=" + domainId +
-            ", usesVirtualNetwork=" + usesVirtualNetwork +
-            ", group='" + group + '\'' +
-            ", groupId=" + groupId +
-            ", guestOSId=" + guestOSId +
-            ", HAEnabled=" + HAEnabled +
-            ", hostId=" + hostId +
-            ", hostname='" + hostname + '\'' +
-            ", IPAddress='" + IPAddress + '\'' +
-            ", ISODisplayText='" + ISODisplayText + '\'' +
-            ", ISOId=" + ISOId +
-            ", ISOName='" + ISOName + '\'' +
-            ", jobId=" + jobId +
-            ", jobStatus=" + jobStatus +
-            ", memory=" + memory +
-            ", name='" + name + '\'' +
-            ", networkKbsRead=" + networkKbsRead +
-            ", networkKbsWrite=" + networkKbsWrite +
-            ", password='" + password + '\'' +
-            ", passwordEnabled=" + passwordEnabled +
-            ", publicIP='" + publicIP + '\'' +
-            ", publicIPId='" + publicIPId + '\'' +
-            ", rootDeviceId=" + rootDeviceId +
-            ", rootDeviceType='" + rootDeviceType + '\'' +
-            ", serviceOfferingId=" + serviceOfferingId +
-            ", serviceOfferingName='" + serviceOfferingName + '\'' +
-            ", state=" + state +
-            ", templateDisplayText='" + templateDisplayText + '\'' +
-            ", templateId=" + templateId +
-            ", templateName='" + templateName + '\'' +
-            ", zoneId=" + zoneId +
-            ", zoneName='" + zoneName + '\'' +
-            ", nics=" + nics +
-            ", hypervisor='" + hypervisor + '\'' +
-            ", securityGroups=" + securityGroups +
-            '}';
+      return string().toString();
    }
 
-   @Override
-   public int compareTo(VirtualMachine arg0) {
-      return id.compareTo(arg0.getId());
-   }
 }
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/VlanIPRange.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/VlanIPRange.java
index cfca734..a65f3ff 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/VlanIPRange.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/VlanIPRange.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -18,139 +18,236 @@
  */
 package org.jclouds.cloudstack.domain;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.beans.ConstructorProperties;
+
+import javax.inject.Named;
+
+import org.jclouds.javax.annotation.Nullable;
+
 import com.google.common.base.Objects;
-import com.google.gson.annotations.SerializedName;
+import com.google.common.base.Objects.ToStringHelper;
 
 /**
  * Represents the data object used in CloudStack's "Vlan" API.
- *
+ * 
  * @author Richard Downer
- */
+*/
 public class VlanIPRange implements Comparable<VlanIPRange> {
 
-   public static Builder builder() {
-      return new Builder();
+   public static Builder<?> builder() { 
+      return new ConcreteBuilder();
+   }
+   
+   public Builder<?> toBuilder() { 
+      return new ConcreteBuilder().fromVlanIPRange(this);
    }
 
-   public static class Builder {
+   public static abstract class Builder<T extends Builder<T>>  {
+      protected abstract T self();
 
-      private String id;
-      private String description;
-      private boolean forVirtualNetwork;
-      private String zoneId;
-      private String vlan;
-      private String account;
-      private String domainId;
-      private String domain;
-      private String podId;
-      private String podName;
-      private String gateway;
-      private String netmask;
-      private String startIP;
-      private String endIP;
-      private String networkId;
-
-      public Builder id(String id) {
+      protected String id;
+      protected String description;
+      protected boolean forVirtualNetwork;
+      protected String zoneId;
+      protected String vlan;
+      protected String account;
+      protected String domainId;
+      protected String domain;
+      protected String podId;
+      protected String podName;
+      protected String gateway;
+      protected String netmask;
+      protected String startIP;
+      protected String endIP;
+      protected String networkId;
+   
+      /** 
+       * @see VlanIPRange#getId()
+       */
+      public T id(String id) {
          this.id = id;
-         return this;
+         return self();
       }
 
-      public Builder description(String description) {
+      /** 
+       * @see VlanIPRange#getDescription()
+       */
+      public T description(String description) {
          this.description = description;
-         return this;
+         return self();
       }
 
-      public Builder forVirtualNetwork(boolean forVirtualNetwork) {
+      /** 
+       * @see VlanIPRange#isForVirtualNetwork()
+       */
+      public T forVirtualNetwork(boolean forVirtualNetwork) {
          this.forVirtualNetwork = forVirtualNetwork;
-         return this;
+         return self();
       }
 
-      public Builder zoneId(String zoneId) {
+      /** 
+       * @see VlanIPRange#getZoneId()
+       */
+      public T zoneId(String zoneId) {
          this.zoneId = zoneId;
-         return this;
+         return self();
       }
 
-      public Builder vlan(String vlan) {
+      /** 
+       * @see VlanIPRange#getVlan()
+       */
+      public T vlan(String vlan) {
          this.vlan = vlan;
-         return this;
+         return self();
       }
 
-      public Builder account(String account) {
+      /** 
+       * @see VlanIPRange#getAccount()
+       */
+      public T account(String account) {
          this.account = account;
-         return this;
+         return self();
       }
 
-      public Builder domainId(String domainId) {
+      /** 
+       * @see VlanIPRange#getDomainId()
+       */
+      public T domainId(String domainId) {
          this.domainId = domainId;
-         return this;
+         return self();
       }
 
-      public Builder domain(String domain) {
+      /** 
+       * @see VlanIPRange#getDomain()
+       */
+      public T domain(String domain) {
          this.domain = domain;
-         return this;
+         return self();
       }
 
-      public Builder podId(String podId) {
+      /** 
+       * @see VlanIPRange#getPodId()
+       */
+      public T podId(String podId) {
          this.podId = podId;
-         return this;
+         return self();
       }
 
-      public Builder podName(String podName) {
+      /** 
+       * @see VlanIPRange#getPodName()
+       */
+      public T podName(String podName) {
          this.podName = podName;
-         return this;
+         return self();
       }
 
-      public Builder gateway(String gateway) {
+      /** 
+       * @see VlanIPRange#getGateway()
+       */
+      public T gateway(String gateway) {
          this.gateway = gateway;
-         return this;
+         return self();
       }
 
-      public Builder netmask(String netmask) {
+      /** 
+       * @see VlanIPRange#getNetmask()
+       */
+      public T netmask(String netmask) {
          this.netmask = netmask;
-         return this;
+         return self();
       }
 
-      public Builder startIP(String startIP) {
+      /** 
+       * @see VlanIPRange#getStartIP()
+       */
+      public T startIP(String startIP) {
          this.startIP = startIP;
-         return this;
+         return self();
       }
 
-      public Builder endIP(String endIP) {
+      /** 
+       * @see VlanIPRange#getEndIP()
+       */
+      public T endIP(String endIP) {
          this.endIP = endIP;
-         return this;
+         return self();
       }
 
-      public Builder networkId(String networkId) {
+      /** 
+       * @see VlanIPRange#getNetworkId()
+       */
+      public T networkId(String networkId) {
          this.networkId = networkId;
-         return this;
+         return self();
+      }
+
+      public VlanIPRange build() {
+         return new VlanIPRange(id, description, forVirtualNetwork, zoneId, vlan, account, domainId, domain, podId,
+               podName, gateway, netmask, startIP, endIP, networkId);
       }
       
-      public VlanIPRange build() {
-         return new VlanIPRange(id, description, forVirtualNetwork, zoneId, vlan, account, domainId, domain, podId, podName, gateway, netmask, startIP, endIP, networkId);
+      public T fromVlanIPRange(VlanIPRange in) {
+         return this
+                  .id(in.getId())
+                  .description(in.getDescription())
+                  .forVirtualNetwork(in.isForVirtualNetwork())
+                  .zoneId(in.getZoneId())
+                  .vlan(in.getVlan())
+                  .account(in.getAccount())
+                  .domainId(in.getDomainId())
+                  .domain(in.getDomain())
+                  .podId(in.getPodId())
+                  .podName(in.getPodName())
+                  .gateway(in.getGateway())
+                  .netmask(in.getNetmask())
+                  .startIP(in.getStartIP())
+                  .endIP(in.getEndIP())
+                  .networkId(in.getNetworkId());
       }
    }
-   
-   private String id;
-   private String description;
-   @SerializedName("forvirtualnetwork") private boolean forVirtualNetwork;
-   @SerializedName("zoneid") private String zoneId;
-   private String vlan;
-   private String account;
-   @SerializedName("domainid") private String domainId;
-   private String domain;
-   @SerializedName("podid") private String podId;
-   @SerializedName("podname") private String podName;
-   private String gateway;
-   private String netmask;
-   @SerializedName("startip") private String startIP;
-   @SerializedName("endip") private String endIP;
-   @SerializedName("networkid") private String networkId;
 
-   /* just for the deserializer */
-   VlanIPRange() {}
-   
-   public VlanIPRange(String id, String description, boolean forVirtualNetwork, String zoneId, String vlan, String account, String domainId, String domain, String podId, String podName, String gateway, String netmask, String startIP, String endIP, String networkId) {
-      this.id = id;
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
+   }
+
+   private final String id;
+   private final String description;
+   @Named("forvirtualnetwork")
+   private final boolean forVirtualNetwork;
+   @Named("zoneid")
+   private final String zoneId;
+   private final String vlan;
+   private final String account;
+   @Named("domainid")
+   private final String domainId;
+   private final String domain;
+   @Named("podid")
+   private final String podId;
+   @Named("podname")
+   private final String podName;
+   private final String gateway;
+   private final String netmask;
+   @Named("startip")
+   private final String startIP;
+   @Named("endip")
+   private final String endIP;
+   @Named("networkid")
+   private final String networkId;
+
+   @ConstructorProperties({
+      "id", "description", "forvirtualnetwork", "zoneid", "vlan", "account", "domainid", "domain", "podid", "podname",
+         "gateway", "netmask", "startip", "endip", "networkid"
+   })
+   protected VlanIPRange(String id, @Nullable String description, boolean forVirtualNetwork, @Nullable String zoneId,
+                         @Nullable String vlan, @Nullable String account, @Nullable String domainId, @Nullable String domain,
+                         @Nullable String podId, @Nullable String podName, @Nullable String gateway, @Nullable String netmask,
+                         @Nullable String startIP, @Nullable String endIP, @Nullable String networkId) {
+      this.id = checkNotNull(id, "id");
       this.description = description;
       this.forVirtualNetwork = forVirtualNetwork;
       this.zoneId = zoneId;
@@ -168,121 +265,121 @@
    }
 
    public String getId() {
-      return id;
+      return this.id;
    }
 
+   @Nullable
    public String getDescription() {
-      return description;
+      return this.description;
    }
 
    public boolean isForVirtualNetwork() {
-      return forVirtualNetwork;
+      return this.forVirtualNetwork;
    }
 
+   @Nullable
    public String getZoneId() {
-      return zoneId;
+      return this.zoneId;
    }
 
+   @Nullable
    public String getVlan() {
-      return vlan;
+      return this.vlan;
    }
 
+   @Nullable
    public String getAccount() {
-      return account;
+      return this.account;
    }
 
+   @Nullable
    public String getDomainId() {
-      return domainId;
+      return this.domainId;
    }
 
+   @Nullable
    public String getDomain() {
-      return domain;
+      return this.domain;
    }
 
+   @Nullable
    public String getPodId() {
-      return podId;
+      return this.podId;
    }
 
+   @Nullable
    public String getPodName() {
-      return podName;
+      return this.podName;
    }
 
+   @Nullable
    public String getGateway() {
-      return gateway;
+      return this.gateway;
    }
 
+   @Nullable
    public String getNetmask() {
-      return netmask;
+      return this.netmask;
    }
 
+   @Nullable
    public String getStartIP() {
-      return startIP;
+      return this.startIP;
    }
 
+   @Nullable
    public String getEndIP() {
-      return endIP;
+      return this.endIP;
    }
 
+   @Nullable
    public String getNetworkId() {
-      return networkId;
-   }
-
-   @Override
-   public boolean equals(Object o) {
-      if (this == o) return true;
-      if (o == null || getClass() != o.getClass()) return false;
-
-      VlanIPRange that = (VlanIPRange) o;
-
-      if (!Objects.equal(domainId, that.domainId)) return false;
-      if (!Objects.equal(forVirtualNetwork, that.forVirtualNetwork)) return false;
-      if (!Objects.equal(id, that.id)) return false;
-      if (!Objects.equal(networkId, that.networkId)) return false;
-      if (!Objects.equal(podId, that.podId)) return false;
-      if (!Objects.equal(zoneId, that.zoneId)) return false;
-      if (!Objects.equal(account, that.account)) return false;
-      if (!Objects.equal(description, that.description)) return false;
-      if (!Objects.equal(domain, that.domain)) return false;
-      if (!Objects.equal(endIP, that.endIP)) return false;
-      if (!Objects.equal(gateway, that.gateway)) return false;
-      if (!Objects.equal(netmask, that.netmask)) return false;
-      if (!Objects.equal(podName, that.podName)) return false;
-      if (!Objects.equal(startIP, that.startIP)) return false;
-      if (!Objects.equal(vlan, that.vlan)) return false;
-
-      return true;
+      return this.networkId;
    }
 
    @Override
    public int hashCode() {
-       return Objects.hashCode(domainId, forVirtualNetwork, id, networkId, podId,
-                               zoneId, account, description, domain, endIP, gateway,
-                               netmask, podName, startIP, vlan);
+      return Objects.hashCode(id, description, forVirtualNetwork, zoneId, vlan, account, domainId, domain, podId, podName, gateway, netmask, startIP, endIP, networkId);
    }
 
    @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      VlanIPRange that = VlanIPRange.class.cast(obj);
+      return Objects.equal(this.id, that.id)
+               && Objects.equal(this.description, that.description)
+               && Objects.equal(this.forVirtualNetwork, that.forVirtualNetwork)
+               && Objects.equal(this.zoneId, that.zoneId)
+               && Objects.equal(this.vlan, that.vlan)
+               && Objects.equal(this.account, that.account)
+               && Objects.equal(this.domainId, that.domainId)
+               && Objects.equal(this.domain, that.domain)
+               && Objects.equal(this.podId, that.podId)
+               && Objects.equal(this.podName, that.podName)
+               && Objects.equal(this.gateway, that.gateway)
+               && Objects.equal(this.netmask, that.netmask)
+               && Objects.equal(this.startIP, that.startIP)
+               && Objects.equal(this.endIP, that.endIP)
+               && Objects.equal(this.networkId, that.networkId);
+   }
+   
+   protected ToStringHelper string() {
+      return Objects.toStringHelper(this)
+            .add("id", id).add("description", description).add("forVirtualNetwork", forVirtualNetwork).add("zoneId", zoneId)
+            .add("vlan", vlan).add("account", account).add("domainId", domainId).add("domain", domain).add("podId", podId)
+            .add("podName", podName).add("gateway", gateway).add("netmask", netmask).add("startIP", startIP).add("endIP", endIP)
+            .add("networkId", networkId);
+   }
+   
+   @Override
    public String toString() {
-      return "VlanIPRange{" +
-         "id=" + id +
-         ", description='" + description + '\'' +
-         ", forVirtualNetwork=" + forVirtualNetwork +
-         ", zoneId=" + zoneId +
-         ", vlan='" + vlan + '\'' +
-         ", account='" + account + '\'' +
-         ", domainId=" + domainId +
-         ", domain='" + domain + '\'' +
-         ", podId=" + podId +
-         ", podName='" + podName + '\'' +
-         ", gateway='" + gateway + '\'' +
-         ", netmask='" + netmask + '\'' +
-         ", startIP='" + startIP + '\'' +
-         ", endIP='" + endIP + '\'' +
-         ", networkId=" + networkId +
-         '}';
+      return string().toString();
    }
 
    @Override
    public int compareTo(VlanIPRange other) {
       return this.id.compareTo(other.id);
    }
+
 }
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Volume.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Volume.java
index 133c1ed..f9020a5 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Volume.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Volume.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -16,288 +16,508 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
 package org.jclouds.cloudstack.domain;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import java.beans.ConstructorProperties;
 import java.util.Date;
 import java.util.Map;
 
+import javax.inject.Named;
+
+import org.jclouds.javax.annotation.Nullable;
+
 import com.google.common.base.CaseFormat;
 import com.google.common.base.Function;
 import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Maps;
-import com.google.gson.annotations.SerializedName;
 
 /**
  * @author Vijay Kiran
- */
-public class Volume implements Comparable<Volume> {
+*/
+public class Volume {
 
-   public static Builder builder() {
-      return new Builder();
+   /**
+    */
+   public static enum State {
+      
+      /**
+      * indicates that the volume record is created in the DB, but not on the backend
+      */
+      ALLOCATED,
+      /**
+      * the volume is being created on the backend
+      */
+      CREATING,
+      /**
+      * the volume is ready to be used
+      */
+      READY,
+      /**
+      * the volume is destroyed (either as a result of deleteVolume command for DataDisk or as a part of destroyVm)
+      */
+      DESTROYED,
+      /**
+      * the volume has failed somehow, e.g. during creation (in cloudstack development)
+      */
+      FAILED,
+      
+      UNRECOGNIZED;
+      
+      @Override
+      public String toString() {
+      return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, name());
+      }
+      
+      public static State fromValue(String state) {
+         try {
+            return valueOf(CaseFormat.UPPER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, checkNotNull(state, "state")));
+         } catch (IllegalArgumentException e) {
+            return UNRECOGNIZED;
+         }
+      }
    }
 
+   /**
+    */
+   public static enum Type {
+      ROOT(0),
+      DATADISK(1),
+      UNRECOGNIZED(Integer.MAX_VALUE);
+      
+      private int code;
+      
+      private static final Map<Integer, Type> INDEX = Maps.uniqueIndex(ImmutableSet.copyOf(Type.values()),
+      new Function<Type, Integer>() {
+      
+      @Override
+      public Integer apply(Type input) {
+      return input.code;
+      }
+      
+      });
+      
+      Type(int code) {
+      this.code = code;
+      }
+      
+      @Override
+      public String toString() {
+      return name().toLowerCase();
+      }
+      
+      public static Type fromValue(String resourceType) {
+      Integer code = new Integer(checkNotNull(resourceType, "resourcetype"));
+      return INDEX.containsKey(code) ? INDEX.get(code) : UNRECOGNIZED;
+      }
+      
+   }
 
-   public static class Builder {
+   public static Builder<?> builder() { 
+      return new ConcreteBuilder();
+   }
+   
+   public Builder<?> toBuilder() { 
+      return new ConcreteBuilder().fromVolume(this);
+   }
 
-      private String id;
-      private String account;
-      private Date attached;
-      private Date created;
-      private boolean destroyed;
-      private String deviceId;
-      private String diskOfferingDisplayText;
-      private String diskOfferingId;
-      private String diskOfferingName;
-      private String domain;
-      private String domainId;
-      private String hypervisor;
-      private boolean isExtractable;
-      private String jobId;
-      private String jobStatus;
-      private String name;
-      private String serviceOfferingDisplayText;
-      private String serviceOfferingId;
-      private String serviceOfferingName;
-      private long size;
-      private String snapshotId;
-      private State state;
-      private String storage;
-      private String storageType;
-      private Type type;
-      private String virtualMachineId;
-      private String vmDisplayName;
-      private String vmName;
-      private VirtualMachine.State vmState;
-      private String zoneId;
-      private String zoneName;
+   public static abstract class Builder<T extends Builder<T>>  {
+      protected abstract T self();
 
-      public Builder id(String id) {
+      protected String id;
+      protected String account;
+      protected Date attached;
+      protected Date created;
+      protected boolean destroyed;
+      protected String deviceId;
+      protected String diskOfferingDisplayText;
+      protected String diskOfferingId;
+      protected String diskOfferingName;
+      protected String domain;
+      protected String domainId;
+      protected String hypervisor;
+      protected boolean isExtractable;
+      protected String jobId;
+      protected String jobStatus;
+      protected String name;
+      protected String serviceOfferingDisplayText;
+      protected String serviceOfferingId;
+      protected String serviceOfferingName;
+      protected long size;
+      protected String snapshotId;
+      protected Volume.State state;
+      protected String storage;
+      protected String storageType;
+      protected Volume.Type type;
+      protected String virtualMachineId;
+      protected String vmDisplayName;
+      protected String vmName;
+      protected VirtualMachine.State vmState;
+      protected String zoneId;
+      protected String zoneName;
+   
+      /** 
+       * @see Volume#getId()
+       */
+      public T id(String id) {
          this.id = id;
-         return this;
+         return self();
       }
 
-      public Builder account(String account) {
+      /** 
+       * @see Volume#getAccount()
+       */
+      public T account(String account) {
          this.account = account;
-         return this;
+         return self();
       }
 
-      public Builder attached(Date attached) {
+      /** 
+       * @see Volume#getAttached()
+       */
+      public T attached(Date attached) {
          this.attached = attached;
-         return this;
+         return self();
       }
 
-      public Builder created(Date created) {
+      /** 
+       * @see Volume#getCreated()
+       */
+      public T created(Date created) {
          this.created = created;
-         return this;
+         return self();
       }
 
-      public Builder destroyed(boolean destroyed) {
+      /** 
+       * @see Volume#isDestroyed()
+       */
+      public T destroyed(boolean destroyed) {
          this.destroyed = destroyed;
-         return this;
+         return self();
       }
 
-      public Builder deviceId(String deviceId) {
+      /** 
+       * @see Volume#getDeviceId()
+       */
+      public T deviceId(String deviceId) {
          this.deviceId = deviceId;
-         return this;
+         return self();
       }
 
-      public Builder diskOfferingDisplayText(String diskOfferingDisplayText) {
+      /** 
+       * @see Volume#getDiskOfferingDisplayText()
+       */
+      public T diskOfferingDisplayText(String diskOfferingDisplayText) {
          this.diskOfferingDisplayText = diskOfferingDisplayText;
-         return this;
+         return self();
       }
 
-      public Builder diskOfferingId(String diskOfferingId) {
+      /** 
+       * @see Volume#getDiskOfferingId()
+       */
+      public T diskOfferingId(String diskOfferingId) {
          this.diskOfferingId = diskOfferingId;
-         return this;
+         return self();
       }
 
-      public Builder diskOfferingName(String diskOfferingName) {
+      /** 
+       * @see Volume#getDiskOfferingName()
+       */
+      public T diskOfferingName(String diskOfferingName) {
          this.diskOfferingName = diskOfferingName;
-         return this;
+         return self();
       }
 
-      public Builder domain(String domain) {
+      /** 
+       * @see Volume#getDomain()
+       */
+      public T domain(String domain) {
          this.domain = domain;
-         return this;
+         return self();
       }
 
-      public Builder domainId(String domainId) {
+      /** 
+       * @see Volume#getDomainId()
+       */
+      public T domainId(String domainId) {
          this.domainId = domainId;
-         return this;
+         return self();
       }
 
-      public Builder hypervisor(String hypervisor) {
+      /** 
+       * @see Volume#getHypervisor()
+       */
+      public T hypervisor(String hypervisor) {
          this.hypervisor = hypervisor;
-         return this;
+         return self();
       }
 
-      public Builder isExtractable(boolean isExtractable) {
+      /** 
+       * @see Volume#isExtractable()
+       */
+      public T isExtractable(boolean isExtractable) {
          this.isExtractable = isExtractable;
-         return this;
+         return self();
       }
 
-      public Builder jobId(String jobId) {
+      /** 
+       * @see Volume#getJobId()
+       */
+      public T jobId(String jobId) {
          this.jobId = jobId;
-         return this;
+         return self();
       }
 
-      public Builder jobStatus(String jobStatus) {
+      /** 
+       * @see Volume#getJobStatus()
+       */
+      public T jobStatus(String jobStatus) {
          this.jobStatus = jobStatus;
-         return this;
+         return self();
       }
 
-      public Builder name(String name) {
+      /** 
+       * @see Volume#getName()
+       */
+      public T name(String name) {
          this.name = name;
-         return this;
+         return self();
       }
 
-      public Builder serviceOfferingDisplayText(String serviceOfferingDisplayText) {
+      /** 
+       * @see Volume#getServiceOfferingDisplayText()
+       */
+      public T serviceOfferingDisplayText(String serviceOfferingDisplayText) {
          this.serviceOfferingDisplayText = serviceOfferingDisplayText;
-         return this;
+         return self();
       }
 
-      public Builder serviceOfferingId(String serviceOfferingId) {
+      /** 
+       * @see Volume#getServiceOfferingId()
+       */
+      public T serviceOfferingId(String serviceOfferingId) {
          this.serviceOfferingId = serviceOfferingId;
-         return this;
+         return self();
       }
 
-      public Builder serviceOfferingName(String serviceOfferingName) {
+      /** 
+       * @see Volume#getServiceOfferingName()
+       */
+      public T serviceOfferingName(String serviceOfferingName) {
          this.serviceOfferingName = serviceOfferingName;
-         return this;
+         return self();
       }
 
-      public Builder size(long size) {
+      /** 
+       * @see Volume#getSize()
+       */
+      public T size(long size) {
          this.size = size;
-         return this;
+         return self();
       }
 
-      public Builder snapshotId(String snapshotId) {
+      /** 
+       * @see Volume#getSnapshotId()
+       */
+      public T snapshotId(String snapshotId) {
          this.snapshotId = snapshotId;
-         return this;
+         return self();
       }
 
-      public Builder state(State state) {
+      /** 
+       * @see Volume#getState()
+       */
+      public T state(Volume.State state) {
          this.state = state;
-         return this;
+         return self();
       }
 
-      public Builder storage(String storage) {
+      /** 
+       * @see Volume#getStorage()
+       */
+      public T storage(String storage) {
          this.storage = storage;
-         return this;
+         return self();
       }
 
-      public Builder storageType(String storageType) {
+      /** 
+       * @see Volume#getStorageType()
+       */
+      public T storageType(String storageType) {
          this.storageType = storageType;
-         return this;
+         return self();
       }
 
-      public Builder type(Type type) {
+      /** 
+       * @see Volume#getType()
+       */
+      public T type(Volume.Type type) {
          this.type = type;
-         return this;
+         return self();
       }
 
-      public Builder virtualMachineId(String virtualMachineId) {
+      /** 
+       * @see Volume#getVirtualMachineId()
+       */
+      public T virtualMachineId(String virtualMachineId) {
          this.virtualMachineId = virtualMachineId;
-         return this;
+         return self();
       }
 
-      public Builder vmDisplayName(String vmDisplayName) {
+      /** 
+       * @see Volume#getVmDisplayName()
+       */
+      public T vmDisplayName(String vmDisplayName) {
          this.vmDisplayName = vmDisplayName;
-         return this;
+         return self();
       }
 
-      public Builder vmName(String vmName) {
+      /** 
+       * @see Volume#getVmName()
+       */
+      public T vmName(String vmName) {
          this.vmName = vmName;
-         return this;
+         return self();
       }
 
-      public Builder vmState(VirtualMachine.State vmState) {
+      /** 
+       * @see Volume#getVmState()
+       */
+      public T vmState(VirtualMachine.State vmState) {
          this.vmState = vmState;
-         return this;
+         return self();
       }
 
-      public Builder zoneId(String zoneId) {
+      /** 
+       * @see Volume#getZoneId()
+       */
+      public T zoneId(String zoneId) {
          this.zoneId = zoneId;
-         return this;
+         return self();
       }
 
-      public Builder zoneName(String zoneName) {
+      /** 
+       * @see Volume#getZoneName()
+       */
+      public T zoneName(String zoneName) {
          this.zoneName = zoneName;
-         return this;
+         return self();
       }
 
       public Volume build() {
-         return new Volume(id, account, attached, created, destroyed, deviceId, diskOfferingDisplayText, diskOfferingId,
-               diskOfferingName, domain, domainId, hypervisor, isExtractable, jobId, jobStatus, name,
-               serviceOfferingDisplayText, serviceOfferingId, serviceOfferingName, size, snapshotId, state, storage,
-               storageType, type, virtualMachineId, vmDisplayName, vmName, vmState, zoneId, zoneName);
+         return new Volume(id, account, attached, created, destroyed, deviceId, diskOfferingDisplayText, diskOfferingId, diskOfferingName, domain, domainId, hypervisor, isExtractable, jobId, jobStatus, name, serviceOfferingDisplayText, serviceOfferingId, serviceOfferingName, size, snapshotId, state, storage, storageType, type, virtualMachineId, vmDisplayName, vmName, vmState, zoneId, zoneName);
+      }
+      
+      public T fromVolume(Volume in) {
+         return this
+                  .id(in.getId())
+                  .account(in.getAccount())
+                  .attached(in.getAttached())
+                  .created(in.getCreated())
+                  .destroyed(in.isDestroyed())
+                  .deviceId(in.getDeviceId())
+                  .diskOfferingDisplayText(in.getDiskOfferingDisplayText())
+                  .diskOfferingId(in.getDiskOfferingId())
+                  .diskOfferingName(in.getDiskOfferingName())
+                  .domain(in.getDomain())
+                  .domainId(in.getDomainId())
+                  .hypervisor(in.getHypervisor())
+                  .isExtractable(in.isExtractable())
+                  .jobId(in.getJobId())
+                  .jobStatus(in.getJobStatus())
+                  .name(in.getName())
+                  .serviceOfferingDisplayText(in.getServiceOfferingDisplayText())
+                  .serviceOfferingId(in.getServiceOfferingId())
+                  .serviceOfferingName(in.getServiceOfferingName())
+                  .size(in.getSize())
+                  .snapshotId(in.getSnapshotId())
+                  .state(in.getState())
+                  .storage(in.getStorage())
+                  .storageType(in.getStorageType())
+                  .type(in.getType())
+                  .virtualMachineId(in.getVirtualMachineId())
+                  .vmDisplayName(in.getVmDisplayName())
+                  .vmName(in.getVmName())
+                  .vmState(in.getVmState())
+                  .zoneId(in.getZoneId())
+                  .zoneName(in.getZoneName());
       }
    }
 
-   private String id;
-   private String account;
-   private Date attached;
-   private Date created;
-   private boolean destroyed;
-   @SerializedName("deviceid")
-   private String deviceId;
-   @SerializedName("diskofferingdisplaytext")
-   private String diskOfferingDisplayText;
-   @SerializedName("diskofferingid")
-   private String diskOfferingId;
-   @SerializedName("diskofferingname")
-   private String diskOfferingName;
-   private String domain;
-   @SerializedName("domainid")
-   private String domainId;
-   private String hypervisor;
-   @SerializedName("isextractable")
-   private boolean isExtractable;
-   @SerializedName("jobid")
-   private String jobId;
-   @SerializedName("jobstatus")
-   private String jobStatus;
-   private String name;
-   @SerializedName("serviceofferingdisplaytext")
-   private String serviceOfferingDisplayText;
-   @SerializedName("serviceofferingid")
-   private String serviceOfferingId;
-   @SerializedName("serviceofferingname")
-   private String serviceOfferingName;
-   private long size;
-   @SerializedName("snapshotid")
-   private String snapshotId;
-   private State state;
-   private String storage;
-   @SerializedName("storagetype")
-   // MAYDO: this should perhaps be an enum; only value I have seen is "shared"
-   private String storageType;
-   private Type type;
-   @SerializedName("virtualmachineid")
-   private String virtualMachineId;
-   @SerializedName("vmdisplayname")
-   private String vmDisplayName;
-   @SerializedName("vmname")
-   private String vmName;
-   @SerializedName("vmstate")
-   private VirtualMachine.State vmState;
-   @SerializedName("zoneid")
-   private String zoneId;
-   @SerializedName("zonename")
-   private String zoneName;
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
+   }
 
-   public Volume(String id,String account,  Date attached, Date created, boolean destroyed, String deviceId,
-                 String diskOfferingDisplayText, String diskOfferingId, String diskOfferingName,
-                 String domain, String domainId, String hypervisor, boolean extractable, String jobId,
-                 String jobStatus, String name, String serviceOfferingDisplayText, String serviceOfferingId,
-                 String serviceOfferingName, long size, String snapshotId, State state, String storage,
-                 String storageType, Type type, String virtualMachineId, String vmDisplayName, String vmName,
-                 VirtualMachine.State vmState, String zoneId, String zoneName) {
-      this.id = id;
+   private final String id;
+   private final String account;
+   private final Date attached;
+   private final Date created;
+   private final boolean destroyed;
+   @Named("deviceid")
+   private final String deviceId;
+   @Named("diskofferingdisplaytext")
+   private final String diskOfferingDisplayText;
+   @Named("diskofferingid")
+   private final String diskOfferingId;
+   @Named("diskofferingname")
+   private final String diskOfferingName;
+   private final String domain;
+   @Named("domainid")
+   private final String domainId;
+   private final String hypervisor;
+   @Named("isextractable")
+   private final boolean isExtractable;
+   @Named("jobid")
+   private final String jobId;
+   @Named("jobstatus")
+   private final String jobStatus;
+   private final String name;
+   @Named("serviceofferingdisplaytext")
+   private final String serviceOfferingDisplayText;
+   @Named("serviceofferingid")
+   private final String serviceOfferingId;
+   @Named("serviceofferingname")
+   private final String serviceOfferingName;
+   private final long size;
+   @Named("snapshotid")
+   private final String snapshotId;
+   private final Volume.State state;
+   private final String storage;
+   @Named("storagetype")
+   private final String storageType;
+   private final Volume.Type type;
+   @Named("virtualmachineid")
+   private final String virtualMachineId;
+   @Named("vmdisplayname")
+   private final String vmDisplayName;
+   @Named("vmname")
+   private final String vmName;
+   @Named("vmstate")
+   private final VirtualMachine.State vmState;
+   @Named("zoneid")
+   private final String zoneId;
+   @Named("zonename")
+   private final String zoneName;
+
+   @ConstructorProperties({
+      "id", "account", "attached", "created", "destroyed", "deviceid", "diskofferingdisplaytext", "diskofferingid", "diskofferingname", "domain", "domainid", "hypervisor", "isextractable", "jobid", "jobstatus", "name", "serviceofferingdisplaytext", "serviceofferingid", "serviceofferingname", "size", "snapshotid", "state", "storage", "storagetype", "type", "virtualmachineid", "vmdisplayname", "vmname", "vmstate", "zoneid", "zonename"
+   })
+   protected Volume(String id, @Nullable String account, @Nullable Date attached, @Nullable Date created, boolean destroyed,
+                    @Nullable String deviceId, @Nullable String diskOfferingDisplayText, @Nullable String diskOfferingId,
+                    @Nullable String diskOfferingName, @Nullable String domain, @Nullable String domainId, @Nullable String hypervisor,
+                    boolean isExtractable, @Nullable String jobId, @Nullable String jobStatus, @Nullable String name,
+                    @Nullable String serviceOfferingDisplayText, @Nullable String serviceOfferingId, @Nullable String serviceOfferingName,
+                    long size, @Nullable String snapshotId, @Nullable Volume.State state, @Nullable String storage,
+                    @Nullable String storageType, @Nullable Volume.Type type, @Nullable String virtualMachineId,
+                    @Nullable String vmDisplayName, @Nullable String vmName, @Nullable VirtualMachine.State vmState,
+                    @Nullable String zoneId, @Nullable String zoneName) {
+      this.id = checkNotNull(id, "id");
       this.account = account;
       this.attached = attached;
       this.created = created;
@@ -309,7 +529,7 @@
       this.domain = domain;
       this.domainId = domainId;
       this.hypervisor = hypervisor;
-      isExtractable = extractable;
+      this.isExtractable = isExtractable;
       this.jobId = jobId;
       this.jobStatus = jobStatus;
       this.name = name;
@@ -330,299 +550,215 @@
       this.zoneName = zoneName;
    }
 
-   // for deserialization
-   Volume() {
-   }
-
    public String getId() {
-      return id;
+      return this.id;
    }
 
+   @Nullable
+   public String getAccount() {
+      return this.account;
+   }
+
+   @Nullable
    public Date getAttached() {
-      return attached;
+      return this.attached;
    }
 
+   @Nullable
    public Date getCreated() {
-      return created;
+      return this.created;
    }
 
    public boolean isDestroyed() {
-      return destroyed;
+      return this.destroyed;
    }
 
+   @Nullable
    public String getDeviceId() {
-      return deviceId;
+      return this.deviceId;
    }
 
+   @Nullable
    public String getDiskOfferingDisplayText() {
-      return diskOfferingDisplayText;
+      return this.diskOfferingDisplayText;
    }
 
+   @Nullable
    public String getDiskOfferingId() {
-      return diskOfferingId;
+      return this.diskOfferingId;
    }
 
+   @Nullable
    public String getDiskOfferingName() {
-      return diskOfferingName;
+      return this.diskOfferingName;
    }
 
+   @Nullable
    public String getDomain() {
-      return domain;
+      return this.domain;
    }
 
+   @Nullable
    public String getDomainId() {
-      return domainId;
+      return this.domainId;
    }
 
+   @Nullable
    public String getHypervisor() {
-      return hypervisor;
+      return this.hypervisor;
    }
 
    public boolean isExtractable() {
-      return isExtractable;
+      return this.isExtractable;
    }
 
+   @Nullable
    public String getJobId() {
-      return jobId;
+      return this.jobId;
    }
 
+   @Nullable
    public String getJobStatus() {
-      return jobStatus;
+      return this.jobStatus;
    }
 
+   @Nullable
    public String getName() {
-      return name;
+      return this.name;
    }
 
+   @Nullable
    public String getServiceOfferingDisplayText() {
-      return serviceOfferingDisplayText;
+      return this.serviceOfferingDisplayText;
    }
 
+   @Nullable
    public String getServiceOfferingId() {
-      return serviceOfferingId;
+      return this.serviceOfferingId;
    }
 
+   @Nullable
    public String getServiceOfferingName() {
-      return serviceOfferingName;
+      return this.serviceOfferingName;
    }
 
    public long getSize() {
-      return size;
+      return this.size;
    }
 
+   @Nullable
    public String getSnapshotId() {
-      return snapshotId;
+      return this.snapshotId;
    }
 
-   public State getState() {
-      return state;
+   @Nullable
+   public Volume.State getState() {
+      return this.state;
    }
 
+   @Nullable
    public String getStorage() {
-      return storage;
+      return this.storage;
    }
 
+   @Nullable
    public String getStorageType() {
-      return storageType;
+      return this.storageType;
    }
 
-   public Type getType() {
-      return type;
+   @Nullable
+   public Volume.Type getType() {
+      return this.type;
    }
 
+   @Nullable
    public String getVirtualMachineId() {
-      return virtualMachineId;
+      return this.virtualMachineId;
    }
 
+   @Nullable
    public String getVmDisplayName() {
-      return vmDisplayName;
+      return this.vmDisplayName;
    }
 
+   @Nullable
    public String getVmName() {
-      return vmName;
+      return this.vmName;
    }
 
+   @Nullable
    public VirtualMachine.State getVmState() {
-      return vmState;
+      return this.vmState;
    }
 
+   @Nullable
    public String getZoneId() {
-      return zoneId;
+      return this.zoneId;
    }
 
+   @Nullable
    public String getZoneName() {
-      return zoneName;
-   }
-
-   public String getAccount() {
-      return account;
-   }
-
-   @Override
-   public int compareTo(Volume volume) {
-      return this.id.compareTo(volume.id);
-   }
-
-
-   @Override
-   public boolean equals(Object o) {
-      if (this == o) return true;
-      if (o == null || getClass() != o.getClass()) return false;
-
-      Volume that = (Volume) o;
-
-      if (!Objects.equal(id, that.id)) return false;
-      if (!Objects.equal(account, that.account)) return false;
-      if (!Objects.equal(attached, that.attached)) return false;
-      if (!Objects.equal(created, that.created)) return false;
-      if (!Objects.equal(destroyed, that.destroyed)) return false;
-      if (!Objects.equal(deviceId, that.deviceId)) return false;
-      if (!Objects.equal(diskOfferingDisplayText, that.diskOfferingDisplayText)) return false;
-      if (!Objects.equal(diskOfferingId, that.diskOfferingId)) return false;
-      if (!Objects.equal(diskOfferingName, that.diskOfferingName)) return false;
-      if (!Objects.equal(domain, that.domain)) return false;
-      if (!Objects.equal(domainId, that.domainId)) return false;
-      if (!Objects.equal(hypervisor, that.hypervisor)) return false;
-      if (!Objects.equal(isExtractable, that.isExtractable)) return false;
-      if (!Objects.equal(jobId, that.jobId)) return false;
-      if (!Objects.equal(jobStatus, that.jobStatus)) return false;
-      if (!Objects.equal(name, that.name)) return false;
-      if (!Objects.equal(serviceOfferingDisplayText, that.serviceOfferingDisplayText)) return false;
-      if (!Objects.equal(serviceOfferingId, that.serviceOfferingId)) return false;
-      if (!Objects.equal(serviceOfferingName, that.serviceOfferingName)) return false;
-      if (!Objects.equal(size, that.size)) return false;
-      if (!Objects.equal(snapshotId, that.snapshotId)) return false;
-      if (!Objects.equal(state, that.state)) return false;
-      if (!Objects.equal(storage, that.storage)) return false;
-      if (!Objects.equal(storageType, that.storageType)) return false;
-      if (!Objects.equal(type, that.type)) return false;
-      if (!Objects.equal(virtualMachineId, that.virtualMachineId)) return false;
-      if (!Objects.equal(vmDisplayName, that.vmDisplayName)) return false;
-      if (!Objects.equal(vmName, that.vmName)) return false;
-      if (!Objects.equal(vmState, that.vmState)) return false;
-      if (!Objects.equal(zoneId, that.zoneId)) return false;
-      if (!Objects.equal(zoneName, that.zoneName)) return false;
-
-      return true;
+      return this.zoneName;
    }
 
    @Override
    public int hashCode() {
-       return Objects.hashCode(id, account, attached, created, destroyed, deviceId, diskOfferingDisplayText,
-                               diskOfferingId, diskOfferingName, domain, domainId, hypervisor,
-                               isExtractable, jobId, jobStatus, name, serviceOfferingDisplayText,
-                               serviceOfferingId, serviceOfferingName, size, snapshotId, state, storage,
-                               storageType, type, virtualMachineId, vmDisplayName, vmName, vmState, zoneId,
-                               zoneName);
+      return Objects.hashCode(id, account, attached, created, destroyed, deviceId, diskOfferingDisplayText, diskOfferingId, diskOfferingName, domain, domainId, hypervisor, isExtractable, jobId, jobStatus, name, serviceOfferingDisplayText, serviceOfferingId, serviceOfferingName, size, snapshotId, state, storage, storageType, type, virtualMachineId, vmDisplayName, vmName, vmState, zoneId, zoneName);
    }
 
    @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      Volume that = Volume.class.cast(obj);
+      return Objects.equal(this.id, that.id)
+               && Objects.equal(this.account, that.account)
+               && Objects.equal(this.attached, that.attached)
+               && Objects.equal(this.created, that.created)
+               && Objects.equal(this.destroyed, that.destroyed)
+               && Objects.equal(this.deviceId, that.deviceId)
+               && Objects.equal(this.diskOfferingDisplayText, that.diskOfferingDisplayText)
+               && Objects.equal(this.diskOfferingId, that.diskOfferingId)
+               && Objects.equal(this.diskOfferingName, that.diskOfferingName)
+               && Objects.equal(this.domain, that.domain)
+               && Objects.equal(this.domainId, that.domainId)
+               && Objects.equal(this.hypervisor, that.hypervisor)
+               && Objects.equal(this.isExtractable, that.isExtractable)
+               && Objects.equal(this.jobId, that.jobId)
+               && Objects.equal(this.jobStatus, that.jobStatus)
+               && Objects.equal(this.name, that.name)
+               && Objects.equal(this.serviceOfferingDisplayText, that.serviceOfferingDisplayText)
+               && Objects.equal(this.serviceOfferingId, that.serviceOfferingId)
+               && Objects.equal(this.serviceOfferingName, that.serviceOfferingName)
+               && Objects.equal(this.size, that.size)
+               && Objects.equal(this.snapshotId, that.snapshotId)
+               && Objects.equal(this.state, that.state)
+               && Objects.equal(this.storage, that.storage)
+               && Objects.equal(this.storageType, that.storageType)
+               && Objects.equal(this.type, that.type)
+               && Objects.equal(this.virtualMachineId, that.virtualMachineId)
+               && Objects.equal(this.vmDisplayName, that.vmDisplayName)
+               && Objects.equal(this.vmName, that.vmName)
+               && Objects.equal(this.vmState, that.vmState)
+               && Objects.equal(this.zoneId, that.zoneId)
+               && Objects.equal(this.zoneName, that.zoneName);
+   }
+   
+   protected ToStringHelper string() {
+      return Objects.toStringHelper(this)
+            .add("id", id).add("account", account).add("attached", attached).add("created", created).add("destroyed", destroyed)
+            .add("deviceId", deviceId).add("diskOfferingDisplayText", diskOfferingDisplayText).add("diskOfferingId", diskOfferingId)
+            .add("diskOfferingName", diskOfferingName).add("domain", domain).add("domainId", domainId).add("hypervisor", hypervisor)
+            .add("isExtractable", isExtractable).add("jobId", jobId).add("jobStatus", jobStatus).add("name", name)
+            .add("serviceOfferingDisplayText", serviceOfferingDisplayText).add("serviceOfferingId", serviceOfferingId)
+            .add("serviceOfferingName", serviceOfferingName).add("size", size).add("snapshotId", snapshotId).add("state", state)
+            .add("storage", storage).add("storageType", storageType).add("type", type).add("virtualMachineId", virtualMachineId)
+            .add("vmDisplayName", vmDisplayName).add("vmName", vmName).add("vmState", vmState).add("zoneId", zoneId).add("zoneName", zoneName);
+   }
+   
+   @Override
    public String toString() {
-      return "Volume{" +
-            "id=" + id +
-            ", account='" + account + '\'' +
-            ", attached=" + attached +
-            ", created=" + created +
-            ", destroyed=" + destroyed +
-            ", deviceId=" + deviceId +
-            ", diskOfferingDisplayText='" + diskOfferingDisplayText + '\'' +
-            ", diskOfferingId=" + diskOfferingId +
-            ", diskOfferingName='" + diskOfferingName + '\'' +
-            ", domain='" + domain + '\'' +
-            ", domainId=" + domainId +
-            ", hypervisor='" + hypervisor + '\'' +
-            ", isExtractable=" + isExtractable +
-            ", jobId=" + jobId +
-            ", jobStatus='" + jobStatus + '\'' +
-            ", name='" + name + '\'' +
-            ", serviceOfferingDisplayText='" + serviceOfferingDisplayText + '\'' +
-            ", serviceOfferingId=" + serviceOfferingId +
-            ", serviceOfferingName='" + serviceOfferingName + '\'' +
-            ", size=" + size +
-            ", snapshotId=" + snapshotId +
-            ", state=" + state +
-            ", storage='" + storage + '\'' +
-            ", storageType='" + storageType + '\'' +
-            ", type=" + type +
-            ", virtualMachineId=" + virtualMachineId +
-            ", vmDisplayName='" + vmDisplayName + '\'' +
-            ", vmName='" + vmName + '\'' +
-            ", vmState=" + vmState +
-            ", zoneId=" + zoneId +
-            ", zoneName='" + zoneName + '\'' +
-            '}';
-   }
-
-   public enum State {
-
-      /**
-       * indicates that the volume record is created in the DB, but not on the backend
-       */
-      ALLOCATED,
-      /**
-       * the volume is being created on the backend
-       */
-      CREATING,
-      /**
-       * the volume is ready to be used
-       */
-      READY,
-      /**
-       * the volume is destroyed (either as a result of deleteVolume command for DataDisk or as a part of destroyVm)
-       */
-      DESTROYED,
-      /**
-       * the volume has failed somehow, e.g. during creation (in cloudstack development)
-       */
-      FAILED,
-
-      UNRECOGNIZED;
-
-      @Override
-      public String toString() {
-         return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, name());
-      }
-
-      public static State fromValue(String state) {
-         try {
-            return valueOf(CaseFormat.UPPER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, checkNotNull(state, "state")));
-         } catch (IllegalArgumentException e) {
-            return UNRECOGNIZED;
-         }
-      }
-   }
-
-   public enum Type {
-      ROOT(0),
-      DATADISK(1),
-      UNRECOGNIZED(Integer.MAX_VALUE);
-
-      private int code;
-
-      private static final Map<Integer, Type> INDEX = Maps.uniqueIndex(ImmutableSet.copyOf(Type.values()),
-            new Function<Type, Integer>() {
-
-               @Override
-               public Integer apply(Type input) {
-                  return input.code;
-               }
-
-            });
-
-      Type(int code) {
-         this.code = code;
-      }
-
-      @Override
-      public String toString() {
-         return name().toLowerCase();
-      }
-
-      public static Type fromValue(String resourceType) {
-         Integer code = new Integer(checkNotNull(resourceType, "resourcetype"));
-         return INDEX.containsKey(code) ? INDEX.get(code) : UNRECOGNIZED;
-      }
-
+      return string().toString();
    }
 
 }
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Zone.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Zone.java
index 26fe614..0a4dda0 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Zone.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/domain/Zone.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -20,177 +20,257 @@
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import java.beans.ConstructorProperties;
 import java.util.List;
 
-import javax.annotation.Nullable;
+import javax.inject.Named;
+
+import org.jclouds.javax.annotation.Nullable;
 
 import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
 import com.google.common.collect.ImmutableList;
-import com.google.gson.annotations.SerializedName;
 
 /**
  * @author Adrian Cole, Andrei Savu
- */
+*/
 public class Zone implements Comparable<Zone> {
 
-   public static Builder builder() {
-      return new Builder();
+   public static Builder<?> builder() { 
+      return new ConcreteBuilder();
+   }
+   
+   public Builder<?> toBuilder() { 
+      return new ConcreteBuilder().fromZone(this);
    }
 
-   public static class Builder {
-      private String id;
-      private String description;
-      private String displayText;
-      private List<String> DNS = ImmutableList.of();
-      private String domain;
-      private String domainId;
-      private String guestCIDRAddress;
-      private List<String> internalDNS = ImmutableList.of();
-      private String name;
-      private NetworkType networkType;
-      private String VLAN;
-      private boolean securityGroupsEnabled;
-      private AllocationState allocationState;
-      private String dhcpProvider;
-      private String zoneToken;
+   public static abstract class Builder<T extends Builder<T>>  {
+      protected abstract T self();
 
-      public Builder id(String id) {
+      protected String id;
+      protected String description;
+      protected String displayText;
+      protected String DNS1;
+      protected String DNS2;
+      protected String domain;
+      protected String domainId;
+      protected String guestCIDRAddress;
+      protected String internalDNS1;
+      protected String internalDNS2;
+      protected String name;
+      protected NetworkType networkType;
+      protected String VLAN;
+      protected boolean securityGroupsEnabled;
+      protected AllocationState allocationState;
+      protected String dhcpProvider;
+      protected String zoneToken;
+   
+      /** 
+       * @see Zone#getId()
+       */
+      public T id(String id) {
          this.id = id;
-         return this;
+         return self();
       }
 
-      public Builder description(String description) {
+      /** 
+       * @see Zone#getDescription()
+       */
+      public T description(String description) {
          this.description = description;
-         return this;
+         return self();
       }
 
-      public Builder displayText(String displayText) {
+      /** 
+       * @see Zone#getDisplayText()
+       */
+      public T displayText(String displayText) {
          this.displayText = displayText;
-         return this;
+         return self();
       }
 
-      public Builder DNS(List<String> DNS) {
-         this.DNS = ImmutableList.copyOf(checkNotNull(DNS, "DNS"));
-         return this;
+      /** 
+       * @see Zone#getDNS()
+       */
+      public T DNS(List<String> DNS) {
+         if (!DNS.isEmpty()) this.DNS1 = DNS.get(0);
+         if (DNS.size() > 1) this.DNS2 = DNS.get(1);
+         return self();
       }
 
-      public Builder domain(String domain) {
+      /** 
+       * @see Zone#getDomain()
+       */
+      public T domain(String domain) {
          this.domain = domain;
-         return this;
+         return self();
       }
 
-      public Builder domainId(String domainId) {
+      /** 
+       * @see Zone#getDomainId()
+       */
+      public T domainId(String domainId) {
          this.domainId = domainId;
-         return this;
+         return self();
       }
 
-      public Builder guestCIDRAddress(String guestCIDRAddress) {
+      /** 
+       * @see Zone#getGuestCIDRAddress()
+       */
+      public T guestCIDRAddress(String guestCIDRAddress) {
          this.guestCIDRAddress = guestCIDRAddress;
-         return this;
+         return self();
       }
 
-      public Builder internalDNS(List<String> internalDNS) {
-         this.internalDNS = ImmutableList.copyOf(checkNotNull(internalDNS, "internalDNS"));
-         return this;
+      /** 
+       * @see Zone#getInternalDNS()
+       */
+      public T internalDNS(List<String> DNS) {
+         if (!DNS.isEmpty()) this.internalDNS1 = DNS.get(0);
+         if (DNS.size() > 1) this.internalDNS2 = DNS.get(1);
+         return self();
       }
 
-      public Builder name(String name) {
+      /** 
+       * @see Zone#getName()
+       */
+      public T name(String name) {
          this.name = name;
-         return this;
+         return self();
       }
 
-      public Builder networkType(NetworkType networkType) {
+      /** 
+       * @see Zone#getNetworkType()
+       */
+      public T networkType(NetworkType networkType) {
          this.networkType = networkType;
-         return this;
+         return self();
       }
 
-      public Builder VLAN(String VLAN) {
+      /** 
+       * @see Zone#getVLAN()
+       */
+      public T VLAN(String VLAN) {
          this.VLAN = VLAN;
-         return this;
+         return self();
       }
 
-      public Builder securityGroupsEnabled(boolean securityGroupsEnabled) {
+      /** 
+       * @see Zone#isSecurityGroupsEnabled()
+       */
+      public T securityGroupsEnabled(boolean securityGroupsEnabled) {
          this.securityGroupsEnabled = securityGroupsEnabled;
-         return this;
+         return self();
       }
 
-
-      public Builder allocationState(AllocationState allocationState) {
+      /** 
+       * @see Zone#getAllocationState()
+       */
+      public T allocationState(AllocationState allocationState) {
          this.allocationState = allocationState;
-         return this;
+         return self();
       }
 
-      public Builder dhcpProvider(String dhcpProvider) {
+      /** 
+       * @see Zone#getDhcpProvider()
+       */
+      public T dhcpProvider(String dhcpProvider) {
          this.dhcpProvider = dhcpProvider;
-         return this;
+         return self();
       }
 
-      public Builder zoneToken(String zoneToken) {
+      /** 
+       * @see Zone#getZoneToken()
+       */
+      public T zoneToken(String zoneToken) {
          this.zoneToken = zoneToken;
-         return this;
+         return self();
       }
 
       public Zone build() {
-         return new Zone(id, description, displayText, DNS, domain, domainId, guestCIDRAddress, internalDNS, name,
-               networkType, VLAN, securityGroupsEnabled, allocationState, dhcpProvider, zoneToken);
+         return new Zone(id, description, displayText, DNS1, DNS2, domain, domainId, guestCIDRAddress, internalDNS1, internalDNS2,
+               name, networkType, VLAN, securityGroupsEnabled, allocationState, dhcpProvider, zoneToken);
+      }
+      
+      public T fromZone(Zone in) {
+         return this
+                  .id(in.getId())
+                  .description(in.getDescription())
+                  .displayText(in.getDisplayText())
+                  .DNS(in.getDNS())
+                  .domain(in.getDomain())
+                  .domainId(in.getDomainId())
+                  .guestCIDRAddress(in.getGuestCIDRAddress())
+                  .internalDNS(in.getInternalDNS())
+                  .name(in.getName())
+                  .networkType(in.getNetworkType())
+                  .VLAN(in.getVLAN())
+                  .securityGroupsEnabled(in.isSecurityGroupsEnabled())
+                  .allocationState(in.getAllocationState())
+                  .dhcpProvider(in.getDhcpProvider())
+                  .zoneToken(in.getZoneToken());
       }
    }
 
-   private String id;
-   private String description;
-   @SerializedName("displaytext")
-   private String displayText;
-   @SerializedName("dns1")
-   private String DNS1;
-   @SerializedName("dns2")
-   private String DNS2;
-   private String domain;
-   @Nullable
-   @SerializedName("domainid")
-   private String domainId;
-   @SerializedName("guestcidraddress")
-   private String guestCIDRAddress;
-   @SerializedName("internaldns1")
-   private String internalDNS1;
-   @SerializedName("internaldns2")
-   private String internalDNS2;
-   private String name;
-   @SerializedName("networktype")
-   private NetworkType networkType;
-   @SerializedName("vlan")
-   private String VLAN;
-   @SerializedName("securitygroupsenabled")
-   private boolean securityGroupsEnabled;
-   @SerializedName("allocationstate")
-   private AllocationState allocationState;
-   @SerializedName("dhcpprovider")
-   private String dhcpProvider;
-   @SerializedName("zonetoken")
-   private String zoneToken;
-
-   /**
-    * present only for serializer
-    */
-   Zone() {
-
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
    }
 
-   public Zone(String id, String description, String displayText, List<String> DNS, String domain, String domainId,
-               String guestCIDRAddress, List<String> internalDNS, String name, NetworkType networkType,
-               String vLAN, boolean securityGroupsEnabled, AllocationState allocationState, String dhcpProvider, String zoneToken) {
-      this.id = id;
+   private final String id;
+   private final String description;
+   @Named("displaytext")
+   private final String displayText;
+   @Named("dns1")
+   private final String DNS1;
+   @Named("dns2")
+   private final String DNS2;
+   private final String domain;
+   @Named("domainid")
+   private final String domainId;
+   @Named("guestcidraddress")
+   private final String guestCIDRAddress;
+   @Named("internaldns1")
+   private final String internalDNS1;
+   @Named("internaldns2")
+   private final String internalDNS2;
+   private final String name;
+   @Named("networktype")
+   private final NetworkType networkType;
+   @Named("vlan")
+   private final String VLAN;
+   @Named("securitygroupsenabled")
+   private final boolean securityGroupsEnabled;
+   @Named("allocationstate")
+   private final AllocationState allocationState;
+   @Named("dhcpprovider")
+   private final String dhcpProvider;
+   @Named("zonetoken")
+   private final String zoneToken;
+
+   @ConstructorProperties({
+      "id", "description", "displaytext", "dns1", "dns2", "domain", "domainid", "guestcidraddress", "internaldns1", "internaldns2", "name", "networktype", "vlan", "securitygroupsenabled", "allocationstate", "dhcpprovider", "zonetoken"
+   })
+   protected Zone(String id, @Nullable String description, @Nullable String displayText, @Nullable String DNS1, @Nullable String DNS2,
+                  @Nullable String domain, @Nullable String domainId, @Nullable String guestCIDRAddress, @Nullable String internalDNS1,
+                  @Nullable String internalDNS2, @Nullable String name, @Nullable NetworkType networkType, @Nullable String VLAN, 
+                  boolean securityGroupsEnabled, @Nullable AllocationState allocationState, @Nullable String dhcpProvider,
+                  @Nullable String zoneToken) {
+      this.id = checkNotNull(id, "id");
       this.description = description;
       this.displayText = displayText;
-      this.DNS1 = checkNotNull(DNS, "DNS").size() > 0 ? DNS.get(0) : null;
-      this.DNS2 = DNS.size() > 1 ? DNS.get(1) : null;
+      this.DNS1 = DNS1;
+      this.DNS2 = DNS2;
       this.domain = domain;
       this.domainId = domainId;
       this.guestCIDRAddress = guestCIDRAddress;
-      this.internalDNS1 = checkNotNull(internalDNS, "internalDNS").size() > 0 ? internalDNS.get(0) : null;
-      this.internalDNS2 = internalDNS.size() > 1 ? internalDNS.get(1) : null;
+      this.internalDNS1 = internalDNS1;
+      this.internalDNS2 = internalDNS2;
       this.name = name;
       this.networkType = networkType;
-      this.VLAN = vLAN;
+      this.VLAN = VLAN;
       this.securityGroupsEnabled = securityGroupsEnabled;
       this.allocationState = allocationState;
       this.dhcpProvider = dhcpProvider;
@@ -201,21 +281,23 @@
     * @return Zone id
     */
    public String getId() {
-      return id;
+      return this.id;
    }
 
    /**
     * @return Zone description
     */
+   @Nullable
    public String getDescription() {
-      return description;
+      return this.description;
    }
 
    /**
     * @return the display text of the zone
     */
+   @Nullable
    public String getDisplayText() {
-      return displayText;
+      return this.displayText;
    }
 
    /**
@@ -233,8 +315,9 @@
    /**
     * @return Domain name for the Vms in the zone
     */
+   @Nullable
    public String getDomain() {
-      return domain;
+      return this.domain;
    }
 
    /**
@@ -242,14 +325,15 @@
     */
    @Nullable
    public String getDomainId() {
-      return domainId;
+      return this.domainId;
    }
 
    /**
     * @return the guest CIDR address for the Zone
     */
+   @Nullable
    public String getGuestCIDRAddress() {
-      return guestCIDRAddress;
+      return this.guestCIDRAddress;
    }
 
    /**
@@ -267,113 +351,104 @@
    /**
     * @return Zone name
     */
+   @Nullable
    public String getName() {
-      return name;
+      return this.name;
    }
 
    /**
     * @return the network type of the zone; can be Basic or Advanced
     */
+   @Nullable
    public NetworkType getNetworkType() {
-      return networkType;
+      return this.networkType;
    }
 
    /**
     * @return the vlan range of the zone
     */
+   @Nullable
    public String getVLAN() {
-      return VLAN;
+      return this.VLAN;
    }
 
    /**
     * @return true if this zone has security groups enabled
     */
    public boolean isSecurityGroupsEnabled() {
-      return securityGroupsEnabled;
+      return this.securityGroupsEnabled;
    }
 
    /**
     * @return the allocation state of the cluster
     */
+   @Nullable
    public AllocationState getAllocationState() {
-      return allocationState;
+      return this.allocationState;
    }
 
    /**
     * @return the dhcp Provider for the Zone
     */
+   @Nullable
    public String getDhcpProvider() {
-      return dhcpProvider;
+      return this.dhcpProvider;
    }
 
    /**
     * @return Zone Token
     */
+   @Nullable
    public String getZoneToken() {
-      return zoneToken;
-   }
-
-   @Override
-   public boolean equals(Object o) {
-      if (this == o) return true;
-      if (o == null || getClass() != o.getClass()) return false;
-
-      Zone that = (Zone) o;
-
-      if (!Objects.equal(id, that.id)) return false;
-      if (!Objects.equal(description, that.description)) return false;
-      if (!Objects.equal(displayText, that.displayText)) return false;
-      if (!Objects.equal(DNS1, that.DNS1)) return false;
-      if (!Objects.equal(DNS2, that.DNS2)) return false;
-      if (!Objects.equal(domain, that.domain)) return false;
-      if (!Objects.equal(domainId, that.domainId)) return false;
-      if (!Objects.equal(guestCIDRAddress, that.guestCIDRAddress)) return false;
-      if (!Objects.equal(internalDNS1, that.internalDNS1)) return false;
-      if (!Objects.equal(internalDNS2, that.internalDNS2)) return false;
-      if (!Objects.equal(name, that.name)) return false;
-      if (!Objects.equal(networkType, that.networkType)) return false;
-      if (!Objects.equal(VLAN, that.VLAN)) return false;
-      if (!Objects.equal(securityGroupsEnabled, that.securityGroupsEnabled)) return false;
-      if (!Objects.equal(allocationState, that.allocationState)) return false;
-      if (!Objects.equal(dhcpProvider, that.dhcpProvider)) return false;
-      if (!Objects.equal(zoneToken, that.zoneToken)) return false;
-
-      return true;
+      return this.zoneToken;
    }
 
    @Override
    public int hashCode() {
-       return Objects.hashCode(id, description, displayText, DNS1, DNS2, domain, domainId,
-                               guestCIDRAddress, internalDNS1, internalDNS2, name, networkType, VLAN,
-                               securityGroupsEnabled, allocationState, dhcpProvider,
-                               zoneToken);
+      return Objects.hashCode(id, description, displayText, DNS1, DNS2, domain, domainId, guestCIDRAddress, internalDNS1,
+            internalDNS2, name, networkType, VLAN, securityGroupsEnabled, allocationState, dhcpProvider, zoneToken);
    }
 
    @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      Zone that = Zone.class.cast(obj);
+      return Objects.equal(this.id, that.id)
+               && Objects.equal(this.description, that.description)
+               && Objects.equal(this.displayText, that.displayText)
+               && Objects.equal(this.DNS1, that.DNS1)
+               && Objects.equal(this.DNS2, that.DNS2)
+               && Objects.equal(this.domain, that.domain)
+               && Objects.equal(this.domainId, that.domainId)
+               && Objects.equal(this.guestCIDRAddress, that.guestCIDRAddress)
+               && Objects.equal(this.internalDNS1, that.internalDNS1)
+               && Objects.equal(this.internalDNS2, that.internalDNS2)
+               && Objects.equal(this.name, that.name)
+               && Objects.equal(this.networkType, that.networkType)
+               && Objects.equal(this.VLAN, that.VLAN)
+               && Objects.equal(this.securityGroupsEnabled, that.securityGroupsEnabled)
+               && Objects.equal(this.allocationState, that.allocationState)
+               && Objects.equal(this.dhcpProvider, that.dhcpProvider)
+               && Objects.equal(this.zoneToken, that.zoneToken);
+   }
+   
+   protected ToStringHelper string() {
+      return Objects.toStringHelper(this)
+            .add("id", id).add("description", description).add("displayText", displayText).add("DNS1", DNS1).add("DNS2", DNS2)
+            .add("domain", domain).add("domainId", domainId).add("guestCIDRAddress", guestCIDRAddress).add("internalDNS1", internalDNS1)
+            .add("internalDNS2", internalDNS2).add("name", name).add("networkType", networkType).add("VLAN", VLAN)
+            .add("securityGroupsEnabled", securityGroupsEnabled).add("allocationState", allocationState).add("dhcpProvider", dhcpProvider)
+            .add("zoneToken", zoneToken);
+   }
+   
+   @Override
    public String toString() {
-      return "Zone{" +
-            "id=" + id +
-            ", description='" + description + '\'' +
-            ", displayText='" + displayText + '\'' +
-            ", DNS1='" + DNS1 + '\'' +
-            ", DNS2='" + DNS2 + '\'' +
-            ", domain='" + domain + '\'' +
-            ", domainId=" + domainId +
-            ", guestCIDRAddress='" + guestCIDRAddress + '\'' +
-            ", internalDNS1='" + internalDNS1 + '\'' +
-            ", internalDNS2='" + internalDNS2 + '\'' +
-            ", name='" + name + '\'' +
-            ", networkType=" + networkType +
-            ", VLAN='" + VLAN + '\'' +
-            ", securityGroupsEnabled=" + securityGroupsEnabled +
-            ", allocationState='" + allocationState + '\'' +
-            ", dhcpProvider='" + dhcpProvider + '\'' +
-            ", zoneToken='" + zoneToken + '\'' +
-            '}';
+      return string().toString();
    }
 
    @Override
-   public int compareTo(Zone arg0) {
-      return id.compareTo(arg0.getId());
+   public int compareTo(Zone o) {
+      return id.compareTo(o.getId());
    }
 }
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/functions/ParseLoginResponseFromHttpResponse.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/functions/ParseLoginResponseFromHttpResponse.java
index 41f9635..62e89c7 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/functions/ParseLoginResponseFromHttpResponse.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/functions/ParseLoginResponseFromHttpResponse.java
@@ -37,6 +37,6 @@
       String jSessionId = get(Splitter.on("=").split(get(Splitter.on(";").trimResults().split(
          getOnlyElement(response.getHeaders().get("Set-Cookie"))), 0)), 1);
       
-      return LoginResponse.builder().copyOf(login).jSessionId(jSessionId).build();
+      return LoginResponse.builder().fromLoginResponse(login).jSessionId(jSessionId).build();
    }
 }
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/functions/ParseTypedAsyncJob.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/functions/ParseTypedAsyncJob.java
index 50e63eb..33a262b 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/functions/ParseTypedAsyncJob.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/functions/ParseTypedAsyncJob.java
@@ -93,7 +93,7 @@
       if (toParse.getResult() != null) {
          if (toParse.getResult().size() == 1) {
             @SuppressWarnings({"unchecked", "rawtypes"})
-            Builder<Object> builder = AsyncJob.Builder.fromAsyncJobUntyped((AsyncJob) toParse);
+            Builder<?,Object> builder = AsyncJob.Builder.fromAsyncJobUntyped((AsyncJob) toParse);
             if (toParse.getResult().containsKey("success")) {
                builder.result(null);
             } else {
@@ -118,11 +118,11 @@
             result = builder.build();
          } else if (toParse.getResult().containsKey("errorcode")) {
             @SuppressWarnings({"unchecked", "rawtypes"})
-            Builder<Object> builder = AsyncJob.Builder.fromAsyncJobUntyped((AsyncJob) toParse);
+            Builder<?, Object> builder = AsyncJob.Builder.fromAsyncJobUntyped((AsyncJob) toParse);
             builder.result(null);// avoid classcastexceptions
-            builder.error(new AsyncJobError(ErrorCode.fromValue(toParse.getResult().get("errorcode").toString()), toParse
-               .getResult().containsKey("errortext") ? toParse.getResult().get("errortext").toString()
-               .replace("\"", "") : null));
+            builder.error(AsyncJobError.builder().errorCode(ErrorCode.fromValue(toParse.getResult().get("errorcode").toString()))
+                  .errorText(toParse.getResult().containsKey("errortext") ? toParse.getResult().get("errortext").toString().replace("\"", "") : null)
+                  .build());
             result = builder.build();
          } else if (toParse.getResult().size() > 1) {
             logger.warn("unexpected size of async job result; expecting a map with a single element",
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/DeployVirtualMachineOptions.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/DeployVirtualMachineOptions.java
index f992bf7..c8b13d4 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/DeployVirtualMachineOptions.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/DeployVirtualMachineOptions.java
@@ -129,8 +129,8 @@
    public DeployVirtualMachineOptions ipsToNetworks(Map<String, String> ipsToNetworks) {
       int count = 0;
       for (String ip : ipsToNetworks.keySet()) {
-         this.queryParameters.replaceValues(String.format("ipnetworklist[%d].ip", count), ImmutableSet.of(ip));
-         this.queryParameters.replaceValues(String.format("ipnetworklist[%d].networkid", count),
+         this.queryParameters.replaceValues(String.format("iptonetworklist[%d].ip", count), ImmutableSet.of(ip));
+         this.queryParameters.replaceValues(String.format("iptonetworklist[%d].networkid", count),
                ImmutableSet.of("" + ipsToNetworks.get(ip)));
          count += 1;
       }
diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/util/SnapshotPolicySchedules.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/util/SnapshotPolicySchedules.java
index 44b6f24..3244fbb 100644
--- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/util/SnapshotPolicySchedules.java
+++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/util/SnapshotPolicySchedules.java
@@ -28,18 +28,18 @@
  */
 public class SnapshotPolicySchedules {
    public static SnapshotPolicySchedule hourly(int minute) {
-      return new SnapshotPolicySchedule(Snapshot.Interval.HOURLY, String.format("%02d", minute));
+      return SnapshotPolicySchedule.builder().interval(Snapshot.Interval.HOURLY).time(String.format("%02d", minute)).build();
    }
 
    public static SnapshotPolicySchedule daily(int hour, int minute) {
-      return new SnapshotPolicySchedule(Snapshot.Interval.DAILY, String.format("%02d:%02d", minute, hour));
+      return SnapshotPolicySchedule.builder().interval(Snapshot.Interval.DAILY).time(String.format("%02d:%02d", minute, hour)).build();
    }
 
    public static SnapshotPolicySchedule weekly(int day, int hour, int minute) {
-      return new SnapshotPolicySchedule(Snapshot.Interval.WEEKLY, String.format("%02d:%02d:%02d", minute, hour, day));
+      return SnapshotPolicySchedule.builder().interval(Snapshot.Interval.WEEKLY).time(String.format("%02d:%02d:%02d", minute, hour, day)).build();
    }
 
    public static SnapshotPolicySchedule monthly(int day, int hour, int minute) {
-      return new SnapshotPolicySchedule(Snapshot.Interval.MONTHLY, String.format("%02d:%02d:%02d", minute, hour, day));
+      return SnapshotPolicySchedule.builder().interval(Snapshot.Interval.MONTHLY).time(String.format("%02d:%02d:%02d", minute, hour, day)).build();
    }
 }
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/CloudStackAsyncClientTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/CloudStackAsyncClientTest.java
index 86bffbf..6416caf 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/CloudStackAsyncClientTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/CloudStackAsyncClientTest.java
@@ -21,7 +21,7 @@
 import java.io.IOException;
 import java.util.concurrent.ExecutionException;
 
-import org.jclouds.cloudstack.features.BaseCloudStackAsyncClientTest;
+import org.jclouds.cloudstack.internal.BaseCloudStackAsyncClientTest;
 import org.jclouds.http.HttpRequest;
 import org.jclouds.rest.internal.RestAnnotationProcessor;
 import org.testng.annotations.BeforeClass;
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/PasswordAuthenticationExpectTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/PasswordAuthenticationExpectTest.java
index 28c38f1..bf87bb0 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/PasswordAuthenticationExpectTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/PasswordAuthenticationExpectTest.java
@@ -26,7 +26,7 @@
 
 import org.jclouds.cloudstack.config.CloudStackProperties;
 import org.jclouds.cloudstack.features.AccountClient;
-import org.jclouds.cloudstack.features.BaseCloudStackRestClientExpectTest;
+import org.jclouds.cloudstack.internal.BaseCloudStackRestClientExpectTest;
 import org.jclouds.http.HttpRequest;
 import org.jclouds.http.HttpResponse;
 import org.testng.annotations.Test;
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/CloudStackComputeServiceAdapterLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/CloudStackComputeServiceAdapterLiveTest.java
index 7b0c17a..6ea8147 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/CloudStackComputeServiceAdapterLiveTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/CloudStackComputeServiceAdapterLiveTest.java
@@ -46,7 +46,7 @@
 import org.jclouds.cloudstack.domain.User;
 import org.jclouds.cloudstack.domain.VirtualMachine;
 import org.jclouds.cloudstack.domain.Zone;
-import org.jclouds.cloudstack.features.BaseCloudStackClientLiveTest;
+import org.jclouds.cloudstack.internal.BaseCloudStackClientLiveTest;
 import org.jclouds.cloudstack.functions.StaticNATVirtualMachineInNetwork;
 import org.jclouds.cloudstack.functions.ZoneIdToZone;
 import org.jclouds.cloudstack.predicates.JobComplete;
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/CloudStackExperimentLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/CloudStackExperimentLiveTest.java
index c41929c..a5bb0ee 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/CloudStackExperimentLiveTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/CloudStackExperimentLiveTest.java
@@ -38,7 +38,7 @@
 import org.jclouds.cloudstack.domain.Network;
 import org.jclouds.cloudstack.domain.SshKeyPair;
 import org.jclouds.cloudstack.domain.TrafficType;
-import org.jclouds.cloudstack.features.BaseCloudStackClientLiveTest;
+import org.jclouds.cloudstack.internal.BaseCloudStackClientLiveTest;
 import org.jclouds.cloudstack.functions.WindowsLoginCredentialsFromEncryptedData;
 import org.jclouds.cloudstack.options.ListNetworksOptions;
 import org.jclouds.compute.RunNodesException;
@@ -160,8 +160,8 @@
          WindowsLoginCredentialsFromEncryptedData passwordDecrypt = new WindowsLoginCredentialsFromEncryptedData(crypto);
 
          assertEquals(passwordDecrypt.apply(
-            new EncryptedPasswordAndPrivateKey(encryptedPassword, keyPair.getPrivateKey())).getPassword(),
-            "bX7vvptvw");
+            EncryptedPasswordAndPrivateKey.builder().encryptedPassword(encryptedPassword).privateKey(keyPair.getPrivateKey()).build())
+               .getPassword(), "bX7vvptvw");
 
       } finally {
          if (node != null) {
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/strategy/OptionsConverterTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/strategy/OptionsConverterTest.java
index 683dec1d..72da01f 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/strategy/OptionsConverterTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/strategy/OptionsConverterTest.java
@@ -39,7 +39,7 @@
    private static final Map<String,Network> EMPTY_NETWORKS_MAP = Collections.<String, Network>emptyMap();
    private static final String ZONE_ID = "2";
    private final NetworkService firewallServiceWithStaticNat
-      = new NetworkService("Firewall", ImmutableMap.of("StaticNat", "true"));
+      = NetworkService.builder().name("Firewall").capabilities(ImmutableMap.of("StaticNat", "true")).build();
 
    @Test
    public void testBasicNetworkOptionsConverter() {
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/domain/VirtualMachineTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/domain/VirtualMachineTest.java
index ecc7e4c..c7c4b4d 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/domain/VirtualMachineTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/domain/VirtualMachineTest.java
@@ -21,6 +21,7 @@
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertFalse;
 import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
 
 import org.testng.annotations.Test;
 
@@ -35,17 +36,18 @@
    @Test(groups = "unit", enabled = true)
    public void testCpuUsed() {
       // Class under test should detect if the % is missing off the end
-      boolean caught = false;
-      try { VirtualMachine.builder().cpuUsed("23.4").build(); } catch (Exception e) { caught = true; }
-      assertTrue(caught);
+      try { 
+         VirtualMachine.builder().id("1").cpuUsed("23.4").build();
+         fail("Should have thrown an exception due to % being missing!");
+      } catch (Exception e) { 
+      }
 
       // If CpuUsed is not specified at all, that's OK
-      caught = false;
-      try { VirtualMachine.builder().build(); } catch (Exception e) { caught = true; }
-      assertFalse(caught);
+      VirtualMachine vm = VirtualMachine.builder().id("2").build();
+      assertEquals(vm.getCpuUsed(), 0.0f);
 
       // Retrieving CpuUsed should just give us a straightforward float
-      VirtualMachine vm = VirtualMachine.builder().cpuUsed("23.4%").build();
+      vm = VirtualMachine.builder().id("3").cpuUsed("23.4%").build();
       assertEquals(vm.getCpuUsed(), 23.4, 0.01);
    }
 
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/AccountAsyncClientTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/AccountAsyncClientTest.java
index bd3b897..6b54f2a 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/AccountAsyncClientTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/AccountAsyncClientTest.java
@@ -21,6 +21,7 @@
 import java.io.IOException;
 import java.lang.reflect.Method;
 
+import org.jclouds.cloudstack.internal.BaseCloudStackAsyncClientTest;
 import org.jclouds.cloudstack.options.ListAccountsOptions;
 import org.jclouds.functions.IdentityFunction;
 import org.jclouds.http.HttpRequest;
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/AccountClientExpectTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/AccountClientExpectTest.java
index 2660a11..86a08bc 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/AccountClientExpectTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/AccountClientExpectTest.java
@@ -26,6 +26,7 @@
 import org.jclouds.cloudstack.CloudStackContext;
 import org.jclouds.cloudstack.domain.Account;
 import org.jclouds.cloudstack.domain.User;
+import org.jclouds.cloudstack.internal.BaseCloudStackRestClientExpectTest;
 import org.jclouds.date.internal.SimpleDateFormatDateService;
 import org.jclouds.http.HttpRequest;
 import org.jclouds.http.HttpResponse;
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/AccountClientLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/AccountClientLiveTest.java
index 3e1ea30..8b77349 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/AccountClientLiveTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/AccountClientLiveTest.java
@@ -22,6 +22,7 @@
 
 import org.jclouds.cloudstack.domain.Account;
 import org.jclouds.cloudstack.domain.User;
+import org.jclouds.cloudstack.internal.BaseCloudStackClientLiveTest;
 import org.testng.annotations.Test;
 
 /**
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/AddressAsyncClientTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/AddressAsyncClientTest.java
index 7bde347..18d2c4d 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/AddressAsyncClientTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/AddressAsyncClientTest.java
@@ -22,6 +22,7 @@
 import java.lang.reflect.Method;
 
 import org.jclouds.cloudstack.functions.ReturnVoidOnNotFoundOr404OrUnableToFindAccountOwner;
+import org.jclouds.cloudstack.internal.BaseCloudStackAsyncClientTest;
 import org.jclouds.cloudstack.options.AssociateIPAddressOptions;
 import org.jclouds.cloudstack.options.ListPublicIPAddressesOptions;
 import org.jclouds.functions.IdentityFunction;
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/AddressClientLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/AddressClientLiveTest.java
index a572f73..1b43da7 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/AddressClientLiveTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/AddressClientLiveTest.java
@@ -27,6 +27,7 @@
 
 import org.jclouds.cloudstack.domain.AsyncCreateResponse;
 import org.jclouds.cloudstack.domain.PublicIPAddress;
+import org.jclouds.cloudstack.internal.BaseCloudStackClientLiveTest;
 import org.jclouds.cloudstack.options.ListPublicIPAddressesOptions;
 import org.testng.annotations.AfterGroups;
 import org.testng.annotations.BeforeGroups;
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/AsyncJobAsyncClientTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/AsyncJobAsyncClientTest.java
index f6b1448..a60b19d 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/AsyncJobAsyncClientTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/AsyncJobAsyncClientTest.java
@@ -23,6 +23,7 @@
 
 import org.jclouds.cloudstack.functions.ParseAsyncJobFromHttpResponse;
 import org.jclouds.cloudstack.functions.ParseAsyncJobsFromHttpResponse;
+import org.jclouds.cloudstack.internal.BaseCloudStackAsyncClientTest;
 import org.jclouds.cloudstack.options.ListAsyncJobsOptions;
 import org.jclouds.http.HttpRequest;
 import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404;
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/AsyncJobClientLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/AsyncJobClientLiveTest.java
index ba21eca..2ef2b4e 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/AsyncJobClientLiveTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/AsyncJobClientLiveTest.java
@@ -25,6 +25,7 @@
 
 import org.jclouds.cloudstack.domain.AsyncJob;
 import org.jclouds.cloudstack.domain.AsyncJob.ResultCode;
+import org.jclouds.cloudstack.internal.BaseCloudStackClientLiveTest;
 import org.testng.annotations.Test;
 
 /**
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/ConfigurationAsyncClientTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/ConfigurationAsyncClientTest.java
index 5d86e0a..110ea1f 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/ConfigurationAsyncClientTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/ConfigurationAsyncClientTest.java
@@ -21,6 +21,7 @@
 import java.io.IOException;
 import java.lang.reflect.Method;
 
+import org.jclouds.cloudstack.internal.BaseCloudStackAsyncClientTest;
 import org.jclouds.http.HttpRequest;
 import org.jclouds.http.functions.ParseFirstJsonValueNamed;
 import org.jclouds.rest.functions.MapHttp4xxCodesToExceptions;
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/ConfigurationClientLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/ConfigurationClientLiveTest.java
index ff9ebd2..89bae33 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/ConfigurationClientLiveTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/ConfigurationClientLiveTest.java
@@ -19,6 +19,7 @@
 package org.jclouds.cloudstack.features;
 
 import org.jclouds.cloudstack.domain.Capabilities;
+import org.jclouds.cloudstack.internal.BaseCloudStackClientLiveTest;
 import org.testng.annotations.Test;
 
 /**
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/DomainAccountAsyncClientTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/DomainAccountAsyncClientTest.java
index f4c68dd..502f2eb 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/DomainAccountAsyncClientTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/DomainAccountAsyncClientTest.java
@@ -20,6 +20,7 @@
 
 import java.lang.reflect.Method;
 
+import org.jclouds.cloudstack.internal.BaseCloudStackAsyncClientTest;
 import org.jclouds.http.HttpRequest;
 import org.jclouds.http.functions.ParseFirstJsonValueNamed;
 import org.jclouds.http.functions.UnwrapOnlyJsonValue;
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/DomainAccountClientLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/DomainAccountClientLiveTest.java
index 7d433392..688138d 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/DomainAccountClientLiveTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/DomainAccountClientLiveTest.java
@@ -26,6 +26,7 @@
 import org.jclouds.cloudstack.domain.Account;
 import org.jclouds.cloudstack.domain.AsyncCreateResponse;
 import org.jclouds.cloudstack.domain.AsyncJob;
+import org.jclouds.cloudstack.internal.BaseCloudStackClientLiveTest;
 import org.testng.annotations.Test;
 
 /**
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/DomainDomainClientExpectTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/DomainDomainClientExpectTest.java
index 2813050..263426c 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/DomainDomainClientExpectTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/DomainDomainClientExpectTest.java
@@ -26,6 +26,7 @@
 
 import org.jclouds.cloudstack.CloudStackContext;
 import org.jclouds.cloudstack.domain.Domain;
+import org.jclouds.cloudstack.internal.BaseCloudStackRestClientExpectTest;
 import org.jclouds.http.HttpRequest;
 import org.jclouds.http.HttpResponse;
 import org.testng.annotations.Test;
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/DomainDomainClientLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/DomainDomainClientLiveTest.java
index cb3ca36..d100da9 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/DomainDomainClientLiveTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/DomainDomainClientLiveTest.java
@@ -24,7 +24,7 @@
 import com.google.common.collect.Sets;
 import static com.google.common.collect.Sets.newHashSet;
 import java.util.NoSuchElementException;
-import static org.jclouds.cloudstack.options.ListDomainChildrenOptions.Builder.name;
+
 import static org.jclouds.cloudstack.options.ListDomainChildrenOptions.Builder.parentDomainId;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotNull;
@@ -33,13 +33,10 @@
 
 import java.util.Set;
 
-import javax.annotation.Nullable;
-
 import org.jclouds.cloudstack.domain.Domain;
+import org.jclouds.cloudstack.internal.BaseCloudStackClientLiveTest;
 import org.testng.annotations.Test;
 
-import com.google.common.base.Predicate;
-
 /**
  * Tests behavior of {@code DomainDomainClient}
  *
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/DomainLimitAsyncClientTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/DomainLimitAsyncClientTest.java
index b3baaca..cab9d0a 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/DomainLimitAsyncClientTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/DomainLimitAsyncClientTest.java
@@ -23,6 +23,7 @@
 
 import org.jclouds.cloudstack.domain.ResourceLimit;
 import org.jclouds.cloudstack.domain.ResourceLimit.ResourceType;
+import org.jclouds.cloudstack.internal.BaseCloudStackAsyncClientTest;
 import org.jclouds.http.HttpRequest;
 import org.jclouds.http.functions.ParseFirstJsonValueNamed;
 import org.jclouds.rest.functions.MapHttp4xxCodesToExceptions;
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/DomainLimitClientLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/DomainLimitClientLiveTest.java
index 6f3496c..2a8598c 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/DomainLimitClientLiveTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/DomainLimitClientLiveTest.java
@@ -18,6 +18,7 @@
  */
 package org.jclouds.cloudstack.features;
 
+import org.jclouds.cloudstack.internal.BaseCloudStackClientLiveTest;
 import org.testng.annotations.Test;
 
 /**
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/DomainUserAsyncClientTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/DomainUserAsyncClientTest.java
index da177e6..e70a129 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/DomainUserAsyncClientTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/DomainUserAsyncClientTest.java
@@ -18,6 +18,7 @@
  */
 package org.jclouds.cloudstack.features;
 
+import org.jclouds.cloudstack.internal.BaseCloudStackAsyncClientTest;
 import org.jclouds.rest.internal.RestAnnotationProcessor;
 import org.testng.annotations.Test;
 
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/DomainUserClientLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/DomainUserClientLiveTest.java
index aa5dfbd..43ce1a8 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/DomainUserClientLiveTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/DomainUserClientLiveTest.java
@@ -30,6 +30,7 @@
 import org.jclouds.cloudstack.domain.AsyncCreateResponse;
 import org.jclouds.cloudstack.domain.AsyncJob;
 import org.jclouds.cloudstack.domain.User;
+import org.jclouds.cloudstack.internal.BaseCloudStackClientLiveTest;
 import org.testng.annotations.Test;
 
 /**
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/EventAsyncClientTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/EventAsyncClientTest.java
index 243de7a..7f3d185 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/EventAsyncClientTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/EventAsyncClientTest.java
@@ -22,6 +22,7 @@
 import java.lang.reflect.Method;
 
 import org.jclouds.cloudstack.functions.ParseEventTypesFromHttpResponse;
+import org.jclouds.cloudstack.internal.BaseCloudStackAsyncClientTest;
 import org.jclouds.cloudstack.options.ListEventsOptions;
 import org.jclouds.http.HttpRequest;
 import org.jclouds.http.functions.ParseFirstJsonValueNamed;
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/EventClientLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/EventClientLiveTest.java
index 19c4afa..f64ef20 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/EventClientLiveTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/EventClientLiveTest.java
@@ -23,6 +23,7 @@
 import java.util.Set;
 
 import org.jclouds.cloudstack.domain.Event;
+import org.jclouds.cloudstack.internal.BaseCloudStackClientLiveTest;
 import org.testng.annotations.Test;
 
 /**
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/FirewallAsyncClientTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/FirewallAsyncClientTest.java
index 572266f..08349a9 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/FirewallAsyncClientTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/FirewallAsyncClientTest.java
@@ -22,6 +22,7 @@
 import java.lang.reflect.Method;
 
 import org.jclouds.cloudstack.domain.PortForwardingRule;
+import org.jclouds.cloudstack.internal.BaseCloudStackAsyncClientTest;
 import org.jclouds.cloudstack.options.ListPortForwardingRulesOptions;
 import org.jclouds.http.HttpRequest;
 import org.jclouds.http.functions.ParseFirstJsonValueNamed;
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/FirewallClientExpectTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/FirewallClientExpectTest.java
index 88e0acd..6492c48 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/FirewallClientExpectTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/FirewallClientExpectTest.java
@@ -28,6 +28,7 @@
 import org.jclouds.cloudstack.domain.AsyncCreateResponse;
 import org.jclouds.cloudstack.domain.FirewallRule;
 import org.jclouds.cloudstack.domain.PortForwardingRule;
+import org.jclouds.cloudstack.internal.BaseCloudStackRestClientExpectTest;
 import org.jclouds.http.HttpRequest;
 import org.jclouds.http.HttpResponse;
 import org.testng.annotations.Test;
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/FirewallClientLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/FirewallClientLiveTest.java
index 73e4a61..8b190a0 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/FirewallClientLiveTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/FirewallClientLiveTest.java
@@ -34,6 +34,7 @@
 import org.jclouds.cloudstack.domain.PortForwardingRule;
 import org.jclouds.cloudstack.domain.PublicIPAddress;
 import org.jclouds.cloudstack.domain.VirtualMachine;
+import org.jclouds.cloudstack.internal.BaseCloudStackClientLiveTest;
 import org.jclouds.cloudstack.options.CreateFirewallRuleOptions;
 import org.jclouds.logging.Logger;
 import org.testng.annotations.AfterGroups;
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalAccountAsyncClientTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalAccountAsyncClientTest.java
index e91fa3c..59e5875 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalAccountAsyncClientTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalAccountAsyncClientTest.java
@@ -21,6 +21,7 @@
 import java.lang.reflect.Method;
 
 import org.jclouds.cloudstack.domain.Account;
+import org.jclouds.cloudstack.internal.BaseCloudStackAsyncClientTest;
 import org.jclouds.cloudstack.options.CreateAccountOptions;
 import org.jclouds.cloudstack.options.UpdateAccountOptions;
 import org.jclouds.http.HttpRequest;
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalAccountClientLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalAccountClientLiveTest.java
index fca83ac..c316400 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalAccountClientLiveTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalAccountClientLiveTest.java
@@ -23,6 +23,7 @@
 
 import org.jclouds.cloudstack.CloudStackGlobalClient;
 import org.jclouds.cloudstack.domain.Account;
+import org.jclouds.cloudstack.internal.BaseCloudStackClientLiveTest;
 import org.jclouds.crypto.CryptoStreams;
 import org.testng.annotations.Test;
 
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalAlertAsyncClientTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalAlertAsyncClientTest.java
index aefe6fb..c33fd0a 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalAlertAsyncClientTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalAlertAsyncClientTest.java
@@ -21,6 +21,7 @@
 import java.io.IOException;
 import java.lang.reflect.Method;
 
+import org.jclouds.cloudstack.internal.BaseCloudStackAsyncClientTest;
 import org.jclouds.cloudstack.options.ListAlertsOptions;
 import org.jclouds.http.HttpRequest;
 import org.jclouds.http.functions.ParseFirstJsonValueNamed;
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalAlertClientLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalAlertClientLiveTest.java
index a51d983..bacc313 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalAlertClientLiveTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalAlertClientLiveTest.java
@@ -25,6 +25,7 @@
 import java.util.Set;
 
 import org.jclouds.cloudstack.domain.Alert;
+import org.jclouds.cloudstack.internal.BaseCloudStackClientLiveTest;
 import org.jclouds.cloudstack.options.ListAlertsOptions;
 import org.testng.annotations.Test;
 
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalCapacityAsyncClientTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalCapacityAsyncClientTest.java
index f2f93ea..69a472b 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalCapacityAsyncClientTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalCapacityAsyncClientTest.java
@@ -22,6 +22,7 @@
 import java.lang.reflect.Method;
 
 import org.jclouds.cloudstack.domain.Capacity;
+import org.jclouds.cloudstack.internal.BaseCloudStackAsyncClientTest;
 import org.jclouds.cloudstack.options.ListCapacityOptions;
 import org.jclouds.http.HttpRequest;
 import org.jclouds.http.functions.ParseFirstJsonValueNamed;
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalCapacityClientLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalCapacityClientLiveTest.java
index 3b72681..4b67535 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalCapacityClientLiveTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalCapacityClientLiveTest.java
@@ -24,6 +24,7 @@
 import java.util.Set;
 
 import org.jclouds.cloudstack.domain.Capacity;
+import org.jclouds.cloudstack.internal.BaseCloudStackClientLiveTest;
 import org.testng.annotations.Test;
 
 /**
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalConfigurationClientExpectTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalConfigurationClientExpectTest.java
index a096441..37f6ec2 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalConfigurationClientExpectTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalConfigurationClientExpectTest.java
@@ -25,6 +25,7 @@
 
 import org.jclouds.cloudstack.CloudStackContext;
 import org.jclouds.cloudstack.domain.ConfigurationEntry;
+import org.jclouds.cloudstack.internal.BaseCloudStackRestClientExpectTest;
 import org.jclouds.http.HttpRequest;
 import org.jclouds.http.HttpResponse;
 import org.testng.annotations.Test;
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalConfigurationClientLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalConfigurationClientLiveTest.java
index 64b578d..1d75e21 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalConfigurationClientLiveTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalConfigurationClientLiveTest.java
@@ -27,6 +27,7 @@
 import javax.annotation.Nullable;
 
 import org.jclouds.cloudstack.domain.ConfigurationEntry;
+import org.jclouds.cloudstack.internal.BaseCloudStackClientLiveTest;
 import org.testng.annotations.Test;
 import org.testng.collections.Sets;
 
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalDomainClientExpectTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalDomainClientExpectTest.java
index 9b547c9..4fdf96e 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalDomainClientExpectTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalDomainClientExpectTest.java
@@ -26,6 +26,7 @@
 
 import org.jclouds.cloudstack.CloudStackContext;
 import org.jclouds.cloudstack.domain.Domain;
+import org.jclouds.cloudstack.internal.BaseCloudStackRestClientExpectTest;
 import org.jclouds.http.HttpRequest;
 import org.jclouds.http.HttpResponse;
 import org.testng.annotations.Test;
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalDomainClientLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalDomainClientLiveTest.java
index ae84834..08581e7 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalDomainClientLiveTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalDomainClientLiveTest.java
@@ -26,6 +26,7 @@
 import javax.annotation.Nullable;
 
 import org.jclouds.cloudstack.domain.Domain;
+import org.jclouds.cloudstack.internal.BaseCloudStackClientLiveTest;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalHostAsyncClientTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalHostAsyncClientTest.java
index 33cece3..1951ae5 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalHostAsyncClientTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalHostAsyncClientTest.java
@@ -20,6 +20,7 @@
 
 import java.lang.reflect.Method;
 
+import org.jclouds.cloudstack.internal.BaseCloudStackAsyncClientTest;
 import org.jclouds.cloudstack.options.ListHostsOptions;
 import org.jclouds.http.HttpRequest;
 import org.jclouds.http.functions.ParseFirstJsonValueNamed;
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalHostClientExpectTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalHostClientExpectTest.java
index b4631f1..25ab1d5 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalHostClientExpectTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalHostClientExpectTest.java
@@ -31,6 +31,7 @@
 import org.jclouds.cloudstack.domain.AllocationState;
 import org.jclouds.cloudstack.domain.Cluster;
 import org.jclouds.cloudstack.domain.Host;
+import org.jclouds.cloudstack.internal.BaseCloudStackRestClientExpectTest;
 import org.jclouds.cloudstack.options.AddClusterOptions;
 import org.jclouds.cloudstack.options.AddHostOptions;
 import org.jclouds.cloudstack.options.AddSecondaryStorageOptions;
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalHostClientLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalHostClientLiveTest.java
index 6db8524..c4ba3e0 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalHostClientLiveTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalHostClientLiveTest.java
@@ -26,6 +26,7 @@
 import org.jclouds.cloudstack.domain.AllocationState;
 import org.jclouds.cloudstack.domain.Cluster;
 import org.jclouds.cloudstack.domain.Host;
+import org.jclouds.cloudstack.internal.BaseCloudStackClientLiveTest;
 import org.testng.annotations.Test;
 
 import com.google.common.base.Strings;
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalOfferingAsyncClientTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalOfferingAsyncClientTest.java
index 78eecfd..1c028a0 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalOfferingAsyncClientTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalOfferingAsyncClientTest.java
@@ -20,6 +20,7 @@
 
 import java.lang.reflect.Method;
 
+import org.jclouds.cloudstack.internal.BaseCloudStackAsyncClientTest;
 import org.jclouds.cloudstack.options.CreateDiskOfferingOptions;
 import org.jclouds.cloudstack.options.CreateServiceOfferingOptions;
 import org.jclouds.cloudstack.options.UpdateDiskOfferingOptions;
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalOfferingClientLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalOfferingClientLiveTest.java
index f3eb1b5..a6530ec 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalOfferingClientLiveTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalOfferingClientLiveTest.java
@@ -32,6 +32,7 @@
 import org.jclouds.cloudstack.domain.NetworkOfferingAvailabilityType;
 import org.jclouds.cloudstack.domain.ServiceOffering;
 import org.jclouds.cloudstack.domain.StorageType;
+import org.jclouds.cloudstack.internal.BaseCloudStackClientLiveTest;
 import org.jclouds.cloudstack.options.UpdateDiskOfferingOptions;
 import org.jclouds.cloudstack.options.UpdateNetworkOfferingOptions;
 import org.jclouds.cloudstack.options.UpdateServiceOfferingOptions;
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalPodClientExpectTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalPodClientExpectTest.java
index 4c28b5e..18e7d0d 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalPodClientExpectTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalPodClientExpectTest.java
@@ -25,6 +25,7 @@
 import org.jclouds.cloudstack.CloudStackContext;
 import org.jclouds.cloudstack.domain.AllocationState;
 import org.jclouds.cloudstack.domain.Pod;
+import org.jclouds.cloudstack.internal.BaseCloudStackRestClientExpectTest;
 import org.jclouds.cloudstack.options.CreatePodOptions;
 import org.jclouds.cloudstack.options.UpdatePodOptions;
 import org.jclouds.http.HttpRequest;
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalPodClientLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalPodClientLiveTest.java
index 9cf1cdf..b7e070b 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalPodClientLiveTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalPodClientLiveTest.java
@@ -30,6 +30,7 @@
 import org.jclouds.cloudstack.domain.NetworkType;
 import org.jclouds.cloudstack.domain.Pod;
 import org.jclouds.cloudstack.domain.Zone;
+import org.jclouds.cloudstack.internal.BaseCloudStackClientLiveTest;
 import org.jclouds.cloudstack.options.CreatePodOptions;
 import org.jclouds.cloudstack.options.ListPodsOptions;
 import org.jclouds.cloudstack.options.UpdatePodOptions;
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalStoragePoolAsyncClientTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalStoragePoolAsyncClientTest.java
index db8d9db..c301e24 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalStoragePoolAsyncClientTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalStoragePoolAsyncClientTest.java
@@ -20,6 +20,7 @@
 
 import java.lang.reflect.Method;
 
+import org.jclouds.cloudstack.internal.BaseCloudStackAsyncClientTest;
 import org.jclouds.cloudstack.options.ListStoragePoolsOptions;
 import org.jclouds.http.HttpRequest;
 import org.jclouds.http.functions.ParseFirstJsonValueNamed;
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalStoragePoolClientLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalStoragePoolClientLiveTest.java
index c1ba2c0..e467d74 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalStoragePoolClientLiveTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalStoragePoolClientLiveTest.java
@@ -25,6 +25,7 @@
 import java.util.Set;
 
 import org.jclouds.cloudstack.domain.StoragePool;
+import org.jclouds.cloudstack.internal.BaseCloudStackClientLiveTest;
 import org.testng.annotations.Test;
 
 import com.google.common.base.Strings;
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalUsageAsyncClientTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalUsageAsyncClientTest.java
index 5cbaea4..1c4ac99 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalUsageAsyncClientTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalUsageAsyncClientTest.java
@@ -22,6 +22,7 @@
 import java.util.Calendar;
 import java.util.Date;
 
+import org.jclouds.cloudstack.internal.BaseCloudStackAsyncClientTest;
 import org.jclouds.cloudstack.options.GenerateUsageRecordsOptions;
 import org.jclouds.cloudstack.options.ListUsageRecordsOptions;
 import org.jclouds.http.HttpRequest;
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalUsageClientLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalUsageClientLiveTest.java
index c283646..af45157 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalUsageClientLiveTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalUsageClientLiveTest.java
@@ -28,6 +28,7 @@
 
 import org.jclouds.cloudstack.domain.JobResult;
 import org.jclouds.cloudstack.domain.UsageRecord;
+import org.jclouds.cloudstack.internal.BaseCloudStackClientLiveTest;
 import org.jclouds.cloudstack.options.GenerateUsageRecordsOptions;
 import org.jclouds.cloudstack.options.ListUsageRecordsOptions;
 import org.testng.annotations.Test;
@@ -51,7 +52,7 @@
 
       JobResult result = globalAdminClient.getUsageClient().generateUsageRecords(start, end, GenerateUsageRecordsOptions.NONE);
       assertNotNull(result);
-      assertTrue(result.getSuccess(), result.getDisplayText());
+      assertTrue(result.isSuccess(), result.getDisplayText());
 
       Set<UsageRecord> records = globalAdminClient.getUsageClient().listUsageRecords(start, end, ListUsageRecordsOptions.NONE);
       assertNotNull(records);
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalUserAsyncClientTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalUserAsyncClientTest.java
index 81b570e..a1e75e5 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalUserAsyncClientTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalUserAsyncClientTest.java
@@ -20,6 +20,7 @@
 
 import java.lang.reflect.Method;
 
+import org.jclouds.cloudstack.internal.BaseCloudStackAsyncClientTest;
 import org.jclouds.cloudstack.options.CreateUserOptions;
 import org.jclouds.cloudstack.options.UpdateUserOptions;
 import org.jclouds.http.HttpRequest;
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalUserClientLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalUserClientLiveTest.java
index 988d0d1..b984be2 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalUserClientLiveTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalUserClientLiveTest.java
@@ -33,6 +33,7 @@
 import org.jclouds.cloudstack.domain.Account;
 import org.jclouds.cloudstack.domain.ApiKeyPair;
 import org.jclouds.cloudstack.domain.User;
+import org.jclouds.cloudstack.internal.BaseCloudStackClientLiveTest;
 import org.jclouds.compute.ComputeServiceContext;
 import org.jclouds.crypto.CryptoStreams;
 import org.testng.annotations.Test;
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalVlanClientExpectTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalVlanClientExpectTest.java
index 79d722d..b50298d 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalVlanClientExpectTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalVlanClientExpectTest.java
@@ -24,6 +24,7 @@
 
 import org.jclouds.cloudstack.CloudStackContext;
 import org.jclouds.cloudstack.domain.VlanIPRange;
+import org.jclouds.cloudstack.internal.BaseCloudStackRestClientExpectTest;
 import org.jclouds.cloudstack.options.CreateVlanIPRangeOptions;
 import org.jclouds.http.HttpRequest;
 import org.jclouds.http.HttpResponse;
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalVlanClientLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalVlanClientLiveTest.java
index 0b2fda7..26835e2 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalVlanClientLiveTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalVlanClientLiveTest.java
@@ -31,6 +31,7 @@
 import org.jclouds.cloudstack.domain.TrafficType;
 import org.jclouds.cloudstack.domain.VlanIPRange;
 import org.jclouds.cloudstack.domain.Zone;
+import org.jclouds.cloudstack.internal.BaseCloudStackClientLiveTest;
 import org.jclouds.cloudstack.options.CreateVlanIPRangeOptions;
 import org.jclouds.cloudstack.options.ListVlanIPRangesOptions;
 import org.jclouds.cloudstack.predicates.NetworkOfferingPredicates;
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalZoneClientExpectTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalZoneClientExpectTest.java
index 7d895eb..14b2e35 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalZoneClientExpectTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalZoneClientExpectTest.java
@@ -28,6 +28,7 @@
 import org.jclouds.cloudstack.domain.AllocationState;
 import org.jclouds.cloudstack.domain.NetworkType;
 import org.jclouds.cloudstack.domain.Zone;
+import org.jclouds.cloudstack.internal.BaseCloudStackRestClientExpectTest;
 import org.jclouds.http.HttpRequest;
 import org.jclouds.http.HttpResponse;
 import org.testng.annotations.Test;
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalZoneClientLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalZoneClientLiveTest.java
index c5a1438..d6366f7 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalZoneClientLiveTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalZoneClientLiveTest.java
@@ -24,6 +24,7 @@
 
 import org.jclouds.cloudstack.domain.NetworkType;
 import org.jclouds.cloudstack.domain.Zone;
+import org.jclouds.cloudstack.internal.BaseCloudStackClientLiveTest;
 import org.testng.annotations.Test;
 
 import com.google.common.collect.ImmutableList;
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GuestOSAsyncClientTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GuestOSAsyncClientTest.java
index 3cb6cf2..51774dd 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GuestOSAsyncClientTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GuestOSAsyncClientTest.java
@@ -23,6 +23,7 @@
 
 import org.jclouds.cloudstack.functions.ParseIdToNameEntryFromHttpResponse;
 import org.jclouds.cloudstack.functions.ParseIdToNameFromHttpResponse;
+import org.jclouds.cloudstack.internal.BaseCloudStackAsyncClientTest;
 import org.jclouds.cloudstack.options.ListOSTypesOptions;
 import org.jclouds.functions.IdentityFunction;
 import org.jclouds.http.HttpRequest;
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GuestOSClientLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GuestOSClientLiveTest.java
index 6e0108d..d2ec922 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GuestOSClientLiveTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GuestOSClientLiveTest.java
@@ -27,6 +27,7 @@
 import java.util.Map.Entry;
 
 import org.jclouds.cloudstack.domain.OSType;
+import org.jclouds.cloudstack.internal.BaseCloudStackClientLiveTest;
 import org.jclouds.cloudstack.options.ListOSTypesOptions;
 import org.testng.annotations.Test;
 
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/HypervisorAsyncClientTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/HypervisorAsyncClientTest.java
index 2811a12..c8d0967 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/HypervisorAsyncClientTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/HypervisorAsyncClientTest.java
@@ -22,6 +22,7 @@
 import java.lang.reflect.Method;
 
 import org.jclouds.cloudstack.functions.ParseNamesFromHttpResponse;
+import org.jclouds.cloudstack.internal.BaseCloudStackAsyncClientTest;
 import org.jclouds.http.HttpRequest;
 import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404;
 import org.jclouds.rest.internal.RestAnnotationProcessor;
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/HypervisorClientLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/HypervisorClientLiveTest.java
index a4f19dc..06b6b1d 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/HypervisorClientLiveTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/HypervisorClientLiveTest.java
@@ -23,6 +23,7 @@
 import java.util.Set;
 
 import org.jclouds.cloudstack.domain.Zone;
+import org.jclouds.cloudstack.internal.BaseCloudStackClientLiveTest;
 import org.testng.annotations.Test;
 
 /**
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/ISOAsyncClientTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/ISOAsyncClientTest.java
index 1eca10a..af78a51 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/ISOAsyncClientTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/ISOAsyncClientTest.java
@@ -23,6 +23,7 @@
 import org.jclouds.cloudstack.domain.ExtractMode;
 import org.jclouds.cloudstack.domain.ISO;
 import org.jclouds.cloudstack.domain.PermissionOperation;
+import org.jclouds.cloudstack.internal.BaseCloudStackAsyncClientTest;
 import org.jclouds.cloudstack.options.AccountInDomainOptions;
 import org.jclouds.cloudstack.options.DeleteISOOptions;
 import org.jclouds.cloudstack.options.ExtractISOOptions;
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/ISOClientLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/ISOClientLiveTest.java
index 409be7f..46f1b5c 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/ISOClientLiveTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/ISOClientLiveTest.java
@@ -27,6 +27,7 @@
 
 import org.jclouds.cloudstack.domain.ISO;
 import org.jclouds.cloudstack.domain.ISOPermissions;
+import org.jclouds.cloudstack.internal.BaseCloudStackClientLiveTest;
 import org.jclouds.cloudstack.options.ListISOsOptions;
 import org.testng.annotations.Test;
 
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/LimitAsyncClientTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/LimitAsyncClientTest.java
index 06d8348..1dffc95 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/LimitAsyncClientTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/LimitAsyncClientTest.java
@@ -21,6 +21,7 @@
 import java.io.IOException;
 import java.lang.reflect.Method;
 
+import org.jclouds.cloudstack.internal.BaseCloudStackAsyncClientTest;
 import org.jclouds.cloudstack.options.ListResourceLimitsOptions;
 import org.jclouds.http.HttpRequest;
 import org.jclouds.http.functions.ParseFirstJsonValueNamed;
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/LimitClientLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/LimitClientLiveTest.java
index 4854dbb..c0366a4 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/LimitClientLiveTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/LimitClientLiveTest.java
@@ -21,6 +21,7 @@
 import java.util.Set;
 
 import org.jclouds.cloudstack.domain.ResourceLimit;
+import org.jclouds.cloudstack.internal.BaseCloudStackClientLiveTest;
 import org.testng.annotations.Test;
 
 /**
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/LoadBalancerAsyncClientTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/LoadBalancerAsyncClientTest.java
index e96cb02..dc6c2bf 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/LoadBalancerAsyncClientTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/LoadBalancerAsyncClientTest.java
@@ -22,6 +22,7 @@
 import java.lang.reflect.Method;
 
 import org.jclouds.cloudstack.domain.LoadBalancerRule.Algorithm;
+import org.jclouds.cloudstack.internal.BaseCloudStackAsyncClientTest;
 import org.jclouds.cloudstack.options.CreateLoadBalancerRuleOptions;
 import org.jclouds.cloudstack.options.ListLoadBalancerRulesOptions;
 import org.jclouds.cloudstack.options.UpdateLoadBalancerRuleOptions;
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/LoadBalancerClientLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/LoadBalancerClientLiveTest.java
index 3fa074e..c7b4c9d 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/LoadBalancerClientLiveTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/LoadBalancerClientLiveTest.java
@@ -40,6 +40,7 @@
 import org.jclouds.cloudstack.domain.Network;
 import org.jclouds.cloudstack.domain.PublicIPAddress;
 import org.jclouds.cloudstack.domain.VirtualMachine;
+import org.jclouds.cloudstack.internal.BaseCloudStackClientLiveTest;
 import org.jclouds.cloudstack.predicates.LoadBalancerRuleActive;
 import org.jclouds.predicates.RetryablePredicate;
 import org.jclouds.ssh.SshException;
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/NATAsyncClientTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/NATAsyncClientTest.java
index 9c5abd3..4846483 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/NATAsyncClientTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/NATAsyncClientTest.java
@@ -21,6 +21,7 @@
 import java.io.IOException;
 import java.lang.reflect.Method;
 
+import org.jclouds.cloudstack.internal.BaseCloudStackAsyncClientTest;
 import org.jclouds.cloudstack.options.CreateIPForwardingRuleOptions;
 import org.jclouds.cloudstack.options.ListIPForwardingRulesOptions;
 import org.jclouds.http.HttpRequest;
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/NATClientLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/NATClientLiveTest.java
index d85a5d4..0d79751 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/NATClientLiveTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/NATClientLiveTest.java
@@ -25,6 +25,7 @@
 import java.util.Set;
 
 import org.jclouds.cloudstack.domain.IPForwardingRule;
+import org.jclouds.cloudstack.internal.BaseCloudStackClientLiveTest;
 import org.jclouds.cloudstack.options.ListIPForwardingRulesOptions;
 import org.testng.annotations.Test;
 
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/NetworkAsyncClientTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/NetworkAsyncClientTest.java
index f691807..1df27e1 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/NetworkAsyncClientTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/NetworkAsyncClientTest.java
@@ -22,6 +22,7 @@
 import java.lang.reflect.Method;
 
 import org.jclouds.cloudstack.domain.NetworkType;
+import org.jclouds.cloudstack.internal.BaseCloudStackAsyncClientTest;
 import org.jclouds.cloudstack.options.CreateNetworkOptions;
 import org.jclouds.cloudstack.options.ListNetworksOptions;
 import org.jclouds.functions.IdentityFunction;
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/NetworkClientLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/NetworkClientLiveTest.java
index a122ce1..2387eab 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/NetworkClientLiveTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/NetworkClientLiveTest.java
@@ -38,6 +38,7 @@
 import org.jclouds.cloudstack.domain.Network;
 import org.jclouds.cloudstack.domain.NetworkOffering;
 import org.jclouds.cloudstack.domain.Zone;
+import org.jclouds.cloudstack.internal.BaseCloudStackClientLiveTest;
 import org.jclouds.cloudstack.predicates.NetworkOfferingPredicates;
 import org.jclouds.cloudstack.predicates.ZonePredicates;
 import org.testng.annotations.BeforeGroups;
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/OfferingAsyncClientTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/OfferingAsyncClientTest.java
index 8b73e3f..154f078 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/OfferingAsyncClientTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/OfferingAsyncClientTest.java
@@ -23,6 +23,7 @@
 import java.io.IOException;
 import java.lang.reflect.Method;
 
+import org.jclouds.cloudstack.internal.BaseCloudStackAsyncClientTest;
 import org.jclouds.cloudstack.options.ListDiskOfferingsOptions;
 import org.jclouds.cloudstack.options.ListNetworkOfferingsOptions;
 import org.jclouds.cloudstack.options.ListServiceOfferingsOptions;
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/OfferingClientLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/OfferingClientLiveTest.java
index 44818f7..72ccc3c 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/OfferingClientLiveTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/OfferingClientLiveTest.java
@@ -29,6 +29,7 @@
 import org.jclouds.cloudstack.domain.ServiceOffering;
 import org.jclouds.cloudstack.domain.StorageType;
 import org.jclouds.cloudstack.domain.TrafficType;
+import org.jclouds.cloudstack.internal.BaseCloudStackClientLiveTest;
 import org.jclouds.cloudstack.options.ListDiskOfferingsOptions;
 import org.jclouds.cloudstack.options.ListNetworkOfferingsOptions;
 import org.jclouds.cloudstack.options.ListServiceOfferingsOptions;
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/SSHKeyPairAsyncClientTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/SSHKeyPairAsyncClientTest.java
index c9b6cf9..d34739e 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/SSHKeyPairAsyncClientTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/SSHKeyPairAsyncClientTest.java
@@ -25,6 +25,7 @@
 import java.net.URLEncoder;
 
 import org.jclouds.cloudstack.filters.QuerySigner;
+import org.jclouds.cloudstack.internal.BaseCloudStackAsyncClientTest;
 import org.jclouds.cloudstack.options.ListSSHKeyPairsOptions;
 import org.jclouds.crypto.SshKeys;
 import org.jclouds.functions.IdentityFunction;
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/SSHKeyPairClientExpectTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/SSHKeyPairClientExpectTest.java
index 63149d6..b266252 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/SSHKeyPairClientExpectTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/SSHKeyPairClientExpectTest.java
@@ -25,6 +25,7 @@
 
 import org.jclouds.cloudstack.CloudStackContext;
 import org.jclouds.cloudstack.domain.SshKeyPair;
+import org.jclouds.cloudstack.internal.BaseCloudStackRestClientExpectTest;
 import org.jclouds.crypto.SshKeys;
 import org.jclouds.http.HttpRequest;
 import org.jclouds.http.HttpResponse;
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/SSHKeyPairClientLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/SSHKeyPairClientLiveTest.java
index 52e0687..edc1111 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/SSHKeyPairClientLiveTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/SSHKeyPairClientLiveTest.java
@@ -26,6 +26,7 @@
 import java.util.Set;
 
 import org.jclouds.cloudstack.domain.SshKeyPair;
+import org.jclouds.cloudstack.internal.BaseCloudStackClientLiveTest;
 import org.jclouds.crypto.SshKeys;
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/SecurityGroupAsyncClientTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/SecurityGroupAsyncClientTest.java
index 19e1498..3652a18 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/SecurityGroupAsyncClientTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/SecurityGroupAsyncClientTest.java
@@ -21,6 +21,7 @@
 import java.io.IOException;
 import java.lang.reflect.Method;
 
+import org.jclouds.cloudstack.internal.BaseCloudStackAsyncClientTest;
 import org.jclouds.cloudstack.options.AccountInDomainOptions;
 import org.jclouds.cloudstack.options.ListSecurityGroupsOptions;
 import org.jclouds.functions.IdentityFunction;
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/SecurityGroupClientLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/SecurityGroupClientLiveTest.java
index 4320390..5b33cac 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/SecurityGroupClientLiveTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/SecurityGroupClientLiveTest.java
@@ -30,6 +30,7 @@
 import org.jclouds.cloudstack.domain.SecurityGroup;
 import org.jclouds.cloudstack.domain.VirtualMachine;
 import org.jclouds.cloudstack.domain.Zone;
+import org.jclouds.cloudstack.internal.BaseCloudStackClientLiveTest;
 import org.jclouds.cloudstack.options.AccountInDomainOptions;
 import org.jclouds.cloudstack.options.DeployVirtualMachineOptions;
 import org.jclouds.cloudstack.options.ListSecurityGroupsOptions;
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/SessionClientExpectTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/SessionClientExpectTest.java
index 434cd2b..8216a74 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/SessionClientExpectTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/SessionClientExpectTest.java
@@ -28,6 +28,7 @@
 import org.jclouds.cloudstack.CloudStackContext;
 import org.jclouds.cloudstack.domain.Account;
 import org.jclouds.cloudstack.domain.LoginResponse;
+import org.jclouds.cloudstack.internal.BaseCloudStackRestClientExpectTest;
 import org.jclouds.http.HttpRequest;
 import org.jclouds.http.HttpResponse;
 import org.testng.annotations.Test;
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/SessionClientLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/SessionClientLiveTest.java
index 0ffc155..f79c571 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/SessionClientLiveTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/SessionClientLiveTest.java
@@ -29,6 +29,7 @@
 import org.jclouds.cloudstack.domain.ApiKeyPair;
 import org.jclouds.cloudstack.domain.LoginResponse;
 import org.jclouds.cloudstack.domain.User;
+import org.jclouds.cloudstack.internal.BaseCloudStackClientLiveTest;
 import org.jclouds.cloudstack.util.ApiKeyPairs;
 import org.jclouds.crypto.CryptoStreams;
 import org.jclouds.rest.AuthorizationException;
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/SnapshotAsyncClientTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/SnapshotAsyncClientTest.java
index 353cc90..f3df9a8 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/SnapshotAsyncClientTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/SnapshotAsyncClientTest.java
@@ -22,6 +22,7 @@
 
 import org.jclouds.cloudstack.domain.Snapshot;
 import org.jclouds.cloudstack.domain.SnapshotPolicySchedule;
+import org.jclouds.cloudstack.internal.BaseCloudStackAsyncClientTest;
 import org.jclouds.cloudstack.options.CreateSnapshotOptions;
 import org.jclouds.cloudstack.options.ListSnapshotPoliciesOptions;
 import org.jclouds.cloudstack.options.ListSnapshotsOptions;
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/SnapshotClientLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/SnapshotClientLiveTest.java
index 5a3d427..84f3723 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/SnapshotClientLiveTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/SnapshotClientLiveTest.java
@@ -33,6 +33,7 @@
 import org.jclouds.cloudstack.domain.AsyncCreateResponse;
 import org.jclouds.cloudstack.domain.Snapshot;
 import org.jclouds.cloudstack.domain.Volume;
+import org.jclouds.cloudstack.internal.BaseCloudStackClientLiveTest;
 import org.jclouds.cloudstack.options.ListSnapshotsOptions;
 import org.jclouds.logging.Logger;
 import org.jclouds.predicates.PredicateCallable;
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/TemplateAsyncClientTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/TemplateAsyncClientTest.java
index 00abea4..f7e038f 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/TemplateAsyncClientTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/TemplateAsyncClientTest.java
@@ -25,6 +25,7 @@
 import org.jclouds.cloudstack.domain.Template;
 import org.jclouds.cloudstack.domain.TemplateFilter;
 import org.jclouds.cloudstack.domain.TemplateMetadata;
+import org.jclouds.cloudstack.internal.BaseCloudStackAsyncClientTest;
 import org.jclouds.cloudstack.options.AccountInDomainOptions;
 import org.jclouds.cloudstack.options.CreateTemplateOptions;
 import org.jclouds.cloudstack.options.DeleteTemplateOptions;
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/TemplateClientLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/TemplateClientLiveTest.java
index f2a9fd5..e9a8ce0 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/TemplateClientLiveTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/TemplateClientLiveTest.java
@@ -40,6 +40,7 @@
 import org.jclouds.cloudstack.domain.VirtualMachine;
 import org.jclouds.cloudstack.domain.Volume;
 import org.jclouds.cloudstack.domain.Zone;
+import org.jclouds.cloudstack.internal.BaseCloudStackClientLiveTest;
 import org.jclouds.cloudstack.options.CreateTemplateOptions;
 import org.jclouds.cloudstack.options.ListNetworksOptions;
 import org.jclouds.cloudstack.options.ListVolumesOptions;
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/VMGroupAsyncClientTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/VMGroupAsyncClientTest.java
index fe369c2..e6153d2 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/VMGroupAsyncClientTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/VMGroupAsyncClientTest.java
@@ -21,6 +21,7 @@
 import java.io.IOException;
 import java.lang.reflect.Method;
 
+import org.jclouds.cloudstack.internal.BaseCloudStackAsyncClientTest;
 import org.jclouds.cloudstack.options.CreateVMGroupOptions;
 import org.jclouds.cloudstack.options.ListVMGroupsOptions;
 import org.jclouds.cloudstack.options.UpdateVMGroupOptions;
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/VMGroupClientLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/VMGroupClientLiveTest.java
index e8cb96e..63f7ed6 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/VMGroupClientLiveTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/VMGroupClientLiveTest.java
@@ -24,6 +24,7 @@
 import java.util.Set;
 
 import org.jclouds.cloudstack.domain.VMGroup;
+import org.jclouds.cloudstack.internal.BaseCloudStackClientLiveTest;
 import org.jclouds.cloudstack.options.ListVMGroupsOptions;
 import org.testng.annotations.AfterGroups;
 import org.testng.annotations.Test;
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/VirtualMachineAsyncClientTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/VirtualMachineAsyncClientTest.java
index 5609e54..94f069d 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/VirtualMachineAsyncClientTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/VirtualMachineAsyncClientTest.java
@@ -21,6 +21,7 @@
 import java.io.IOException;
 import java.lang.reflect.Method;
 
+import org.jclouds.cloudstack.internal.BaseCloudStackAsyncClientTest;
 import org.jclouds.cloudstack.options.DeployVirtualMachineOptions;
 import org.jclouds.cloudstack.options.ListVirtualMachinesOptions;
 import org.jclouds.functions.IdentityFunction;
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/VirtualMachineClientExpectTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/VirtualMachineClientExpectTest.java
index bc64a64..bd9a8bc 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/VirtualMachineClientExpectTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/VirtualMachineClientExpectTest.java
@@ -27,6 +27,7 @@
 import org.jclouds.cloudstack.CloudStackContext;
 import org.jclouds.cloudstack.domain.EncryptedPasswordAndPrivateKey;
 import org.jclouds.cloudstack.functions.WindowsLoginCredentialsFromEncryptedData;
+import org.jclouds.cloudstack.internal.BaseCloudStackRestClientExpectTest;
 import org.jclouds.crypto.Crypto;
 import org.jclouds.encryption.bouncycastle.BouncyCastleCrypto;
 import org.jclouds.http.HttpRequest;
@@ -87,7 +88,7 @@
       WindowsLoginCredentialsFromEncryptedData passwordDecrypt = new WindowsLoginCredentialsFromEncryptedData(crypto);
 
       assertEquals(passwordDecrypt.apply(
-         new EncryptedPasswordAndPrivateKey(actual, privateKey)).getPassword(), "bX7vvptvw");
+         EncryptedPasswordAndPrivateKey.builder().encryptedPassword(actual).privateKey(privateKey).build()).getPassword(), "bX7vvptvw");
    }
 
    @Override
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/VirtualMachineClientLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/VirtualMachineClientLiveTest.java
index dc0b4fa..3bf7bd4 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/VirtualMachineClientLiveTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/VirtualMachineClientLiveTest.java
@@ -47,6 +47,7 @@
 import org.jclouds.cloudstack.domain.Template;
 import org.jclouds.cloudstack.domain.VirtualMachine;
 import org.jclouds.cloudstack.domain.Zone;
+import org.jclouds.cloudstack.internal.BaseCloudStackClientLiveTest;
 import org.jclouds.cloudstack.options.CreateNetworkOptions;
 import org.jclouds.cloudstack.options.DeployVirtualMachineOptions;
 import org.jclouds.cloudstack.options.ListNetworkOfferingsOptions;
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/VolumeAsyncClientTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/VolumeAsyncClientTest.java
index 6d6bd29..8bbbaaf 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/VolumeAsyncClientTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/VolumeAsyncClientTest.java
@@ -21,6 +21,7 @@
 import java.io.IOException;
 import java.lang.reflect.Method;
 
+import org.jclouds.cloudstack.internal.BaseCloudStackAsyncClientTest;
 import org.jclouds.cloudstack.options.ListVolumesOptions;
 import org.jclouds.http.HttpRequest;
 import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404;
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/VolumeClientLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/VolumeClientLiveTest.java
index 1e42261..7c33222 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/VolumeClientLiveTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/VolumeClientLiveTest.java
@@ -37,6 +37,7 @@
 import org.jclouds.cloudstack.domain.VirtualMachine;
 import org.jclouds.cloudstack.domain.Volume;
 import org.jclouds.cloudstack.domain.Zone;
+import org.jclouds.cloudstack.internal.BaseCloudStackClientLiveTest;
 import org.jclouds.cloudstack.options.ListVolumesOptions;
 import org.jclouds.logging.Logger;
 import org.jclouds.predicates.PredicateCallable;
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/ZoneAsyncClientTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/ZoneAsyncClientTest.java
index d52e002..99e94c6 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/ZoneAsyncClientTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/ZoneAsyncClientTest.java
@@ -21,6 +21,7 @@
 import java.io.IOException;
 import java.lang.reflect.Method;
 
+import org.jclouds.cloudstack.internal.BaseCloudStackAsyncClientTest;
 import org.jclouds.cloudstack.options.ListZonesOptions;
 import org.jclouds.functions.IdentityFunction;
 import org.jclouds.http.HttpRequest;
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/ZoneClientExpectTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/ZoneClientExpectTest.java
index c75af2d..22c7910 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/ZoneClientExpectTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/ZoneClientExpectTest.java
@@ -25,6 +25,7 @@
 import org.jclouds.cloudstack.CloudStackContext;
 import org.jclouds.cloudstack.domain.NetworkType;
 import org.jclouds.cloudstack.domain.Zone;
+import org.jclouds.cloudstack.internal.BaseCloudStackRestClientExpectTest;
 import org.jclouds.http.HttpRequest;
 import org.jclouds.http.HttpResponse;
 import org.testng.annotations.Test;
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/ZoneClientLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/ZoneClientLiveTest.java
index eac5350..890d4ba 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/ZoneClientLiveTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/ZoneClientLiveTest.java
@@ -25,6 +25,7 @@
 
 import org.jclouds.cloudstack.domain.NetworkType;
 import org.jclouds.cloudstack.domain.Zone;
+import org.jclouds.cloudstack.internal.BaseCloudStackClientLiveTest;
 import org.jclouds.cloudstack.options.ListZonesOptions;
 import org.testng.annotations.Test;
 
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/functions/BlockUntilJobCompletesAndReturnResultTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/functions/BlockUntilJobCompletesAndReturnResultTest.java
index 83d91fa..b4dce6c 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/functions/BlockUntilJobCompletesAndReturnResultTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/functions/BlockUntilJobCompletesAndReturnResultTest.java
@@ -58,8 +58,8 @@
       replay(jobClient);
 
       assertEquals(
-            new BlockUntilJobCompletesAndReturnResult(client, jobComplete).<String> apply(new AsyncCreateResponse(id,
-                  jobId)), "foo");
+            new BlockUntilJobCompletesAndReturnResult(client, jobComplete).<String>apply(AsyncCreateResponse.builder().id(id).jobId(
+                  jobId).build()), "foo");
 
       verify(client);
       verify(jobClient);
@@ -83,8 +83,8 @@
       replay(jobClient);
 
       assertEquals(
-            new BlockUntilJobCompletesAndReturnResult(client, jobComplete).<String> apply(new AsyncCreateResponse(id,
-                  jobId)), "foo");
+            new BlockUntilJobCompletesAndReturnResult(client, jobComplete).<String>apply(
+                  AsyncCreateResponse.builder().id(id).jobId(jobId).build()), "foo");
 
       verify(client);
       verify(jobClient);
@@ -102,15 +102,17 @@
 
       expect(client.getAsyncJobClient()).andReturn(jobClient).atLeastOnce();
       expect(jobClient.getAsyncJob(jobId)).andReturn(
-            AsyncJob.builder().id(jobId).error(
-               new AsyncJobError(ErrorCode.INTERNAL_ERROR, "ERRROR")).result("foo").build()).atLeastOnce();
+            AsyncJob.builder().id(jobId)
+                  .error(AsyncJobError.builder().errorCode(ErrorCode.INTERNAL_ERROR).errorText("ERRROR").build())
+                  .result("foo").build())
+            .atLeastOnce();
 
       replay(client);
       replay(jobClient);
 
       assertEquals(
-            new BlockUntilJobCompletesAndReturnResult(client, jobComplete).<String> apply(new AsyncCreateResponse(id,
-                  jobId)), "foo");
+            new BlockUntilJobCompletesAndReturnResult(client, jobComplete).<String>apply(
+                  AsyncCreateResponse.builder().id(id).jobId(jobId).build()), "foo");
 
       verify(client);
       verify(jobClient);
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/functions/ParseAsyncJobFromHttpResponseTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/functions/ParseAsyncJobFromHttpResponseTest.java
index c1d37f9..733aeb0 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/functions/ParseAsyncJobFromHttpResponseTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/functions/ParseAsyncJobFromHttpResponseTest.java
@@ -103,8 +103,8 @@
          .status(Status.FAILED)
          .progress(0)
          .resultType("object")
-         .error(new AsyncJobError(ErrorCode.INTERNAL_ERROR, "Internal error executing " +
-            "command, please contact your system administrator"))
+         .error(AsyncJobError.builder().errorCode(ErrorCode.INTERNAL_ERROR).errorText("Internal error executing " +
+            "command, please contact your system administrator").build())
          .resultCode(ResultCode.FAIL).build();
 
       ParseAsyncJobFromHttpResponse parser = i.getInstance(ParseAsyncJobFromHttpResponse.class);
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/functions/ReuseOrAssociateNewPublicIPAddressTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/functions/ReuseOrAssociateNewPublicIPAddressTest.java
index 442fd3f..85a1079 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/functions/ReuseOrAssociateNewPublicIPAddressTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/functions/ReuseOrAssociateNewPublicIPAddressTest.java
@@ -85,7 +85,7 @@
       expect(addressClient.listPublicIPAddresses(allocatedOnly(true).networkId(networkId))).andReturn(
             ImmutableSet.<PublicIPAddress> of());
 
-      AsyncCreateResponse job = new AsyncCreateResponse("1", "2");
+      AsyncCreateResponse job = AsyncCreateResponse.builder().id("1").jobId("2").build();
       // make sure we created the job relating to a new ip
       expect(addressClient.associateIPAddressInZone(zoneId, networkId(networkId))).andReturn(job);
 
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/BaseCloudStackAsyncClientTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/internal/BaseCloudStackAsyncClientTest.java
similarity index 97%
rename from apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/BaseCloudStackAsyncClientTest.java
rename to apis/cloudstack/src/test/java/org/jclouds/cloudstack/internal/BaseCloudStackAsyncClientTest.java
index 6be2724..fae7502 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/BaseCloudStackAsyncClientTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/internal/BaseCloudStackAsyncClientTest.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.cloudstack.features;
+package org.jclouds.cloudstack.internal;
 
 import static org.testng.Assert.assertEquals;
 
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/BaseCloudStackClientLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/internal/BaseCloudStackClientLiveTest.java
similarity index 99%
rename from apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/BaseCloudStackClientLiveTest.java
rename to apis/cloudstack/src/test/java/org/jclouds/cloudstack/internal/BaseCloudStackClientLiveTest.java
index 16218b0..99c8b69 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/BaseCloudStackClientLiveTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/internal/BaseCloudStackClientLiveTest.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.cloudstack.features;
+package org.jclouds.cloudstack.internal;
 
 import static com.google.common.collect.Iterables.filter;
 import static com.google.common.collect.Iterables.get;
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/BaseCloudStackRestClientExpectTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/internal/BaseCloudStackRestClientExpectTest.java
similarity index 98%
rename from apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/BaseCloudStackRestClientExpectTest.java
rename to apis/cloudstack/src/test/java/org/jclouds/cloudstack/internal/BaseCloudStackRestClientExpectTest.java
index a2e027d..d397436 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/BaseCloudStackRestClientExpectTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/internal/BaseCloudStackRestClientExpectTest.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.jclouds.cloudstack.features;
+package org.jclouds.cloudstack.internal;
 
 import static org.jclouds.crypto.CryptoStreams.md5Hex;
 
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/parse/JobResultResponseTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/parse/JobResultResponseTest.java
index b89ad08..60fe2f2 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/parse/JobResultResponseTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/parse/JobResultResponseTest.java
@@ -38,7 +38,7 @@
    @Override
    @SelectJson("jobresult")
    public JobResult expected() {
-      return new JobResult(true, null);
+      return JobResult.builder().success(true).build();
    }
 
 }
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/parse/ListCapabilitiesResponseTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/parse/ListCapabilitiesResponseTest.java
index c1a7641..7dcaa3c 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/parse/ListCapabilitiesResponseTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/parse/ListCapabilitiesResponseTest.java
@@ -38,7 +38,7 @@
    @Override
    @SelectJson("capability")
    public Capabilities expected() {
-      return Capabilities.builder().securityGroupsEnabled(true).sharedTemplatesEnabled(true).cloudStackVersion("2.2")
+      return Capabilities.builder().securityGroupsEnabled(true).canShareTemplates(true).cloudStackVersion("2.2")
             .build();
    }
 
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/parse/ListHostsResponseTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/parse/ListHostsResponseTest.java
index 953ad4d..65f5a57 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/parse/ListHostsResponseTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/parse/ListHostsResponseTest.java
@@ -18,18 +18,22 @@
  */
 package org.jclouds.cloudstack.parse;
 
+import static org.testng.Assert.assertEquals;
+
 import java.util.Set;
 
 import org.jclouds.cloudstack.config.CloudStackParserModule;
 import org.jclouds.cloudstack.domain.AllocationState;
 import org.jclouds.cloudstack.domain.Host;
 import org.jclouds.date.internal.SimpleDateFormatDateService;
+import org.jclouds.json.BaseParserTest;
 import org.jclouds.json.BaseSetParserTest;
 import org.jclouds.json.config.GsonModule;
 import org.jclouds.rest.annotations.SelectJson;
 import org.testng.annotations.Test;
 
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSortedSet;
 import com.google.inject.Guice;
 import com.google.inject.Injector;
 
@@ -37,7 +41,11 @@
  * @author Andrei Savu
  */
 @Test(groups = "unit")
-public class ListHostsResponseTest extends BaseSetParserTest<Host> {
+public class ListHostsResponseTest extends BaseParserTest<Set<Host>, Set<Host>> {
+
+   public void compare(Set<Host> expects, Set<Host> response) {
+      assertEquals(response.toString(), expects.toString());
+   }
 
    @Override
    protected Injector injector() {
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/parse/ListNetworksResponseTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/parse/ListNetworksResponseTest.java
index c5e7678..742afec 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/parse/ListNetworksResponseTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/parse/ListNetworksResponseTest.java
@@ -77,17 +77,17 @@
                   .isDefault(true)
                   .services(
                         ImmutableSortedSet.of(
-                              new NetworkService("Vpn", ImmutableMap.of("SupportedVpnTypes", "pptp,l2tp,ipsec")),
-                              new NetworkService("Gateway"),
-                              new NetworkService("UserData"),
-                              new NetworkService("Dhcp"),
-                              new NetworkService("Firewall", ImmutableSortedMap.<String, String> naturalOrder()
+                              NetworkService.builder().name("Vpn").capabilities(ImmutableMap.of("SupportedVpnTypes", "pptp,l2tp,ipsec")).build(),
+                              NetworkService.builder().name("Gateway").build(),
+                              NetworkService.builder().name("UserData").build(),
+                              NetworkService.builder().name("Dhcp").build(),
+                              NetworkService.builder().name("Firewall").capabilities(ImmutableSortedMap.<String, String> naturalOrder()
                                     .put("SupportedSourceNatTypes", "per account").put("StaticNat", "true")
                                     .put("TrafficStatistics", "per public ip").put("PortForwarding", "true")
-                                    .put("MultipleIps", "true").put("SupportedProtocols", "tcp,udp").build()),
-                              new NetworkService("Dns"),
-                              new NetworkService("Lb", ImmutableMap.of("SupportedLbAlgorithms",
-                                    "roundrobin,leastconn,source", "SupportedProtocols", "tcp, udp"))))
+                                    .put("MultipleIps", "true").put("SupportedProtocols", "tcp,udp").build()).build(),
+                              NetworkService.builder().name("Dns").build(),
+                              NetworkService.builder().name("Lb").capabilities(ImmutableMap.of("SupportedLbAlgorithms",
+                                    "roundrobin,leastconn,source", "SupportedProtocols", "tcp, udp")).build()))
                   .networkDomain("cs3cloud.internal").build());
    }
 }
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/parse/ListServiceOfferingsResponseTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/parse/ListServiceOfferingsResponseTest.java
index acf864f..195558f 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/parse/ListServiceOfferingsResponseTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/parse/ListServiceOfferingsResponseTest.java
@@ -49,12 +49,12 @@
                   .displayText("Small Instance - 500 MhZ CPU, 512 MB RAM - $0.05 per hour").cpuNumber(1).cpuSpeed(500)
                   .memory(512)
                   .created(new SimpleDateFormatDateService().iso8601SecondsDateParse("2011-02-11T15:22:32-0800"))
-                  .storageType(StorageType.SHARED).haSupport(false).build(),
+                  .storageType(StorageType.SHARED).supportsHA(false).build(),
             ServiceOffering.builder().id("2").name("Medium Instance")
                   .displayText("Medium Instance, 1 GhZ CPU,  1 GB RAM - $0.10 per hour").cpuNumber(1).cpuSpeed(1000)
                   .memory(1024)
                   .created(new SimpleDateFormatDateService().iso8601SecondsDateParse("2011-02-11T15:22:32-0800"))
-                  .storageType(StorageType.SHARED).haSupport(false).build());
+                  .storageType(StorageType.SHARED).supportsHA(false).build());
    }
 
 }
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/predicates/JobCompleteTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/predicates/JobCompleteTest.java
index 4dfa049..980bbee 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/predicates/JobCompleteTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/predicates/JobCompleteTest.java
@@ -68,7 +68,7 @@
    public void testFailedJobComplete() {
       AsyncJob<?> job = AsyncJob.builder().id("100")
          .status(Status.FAILED).resultCode(ResultCode.FAIL)
-         .error(new AsyncJobError(ErrorCode.INTERNAL_ERROR, "Dummy test error")).build();
+         .error(AsyncJobError.builder().errorCode(ErrorCode.INTERNAL_ERROR).errorText("Dummy test error").build()).build();
       expect((Object) asyncJobClient.getAsyncJob(job.getId())).andReturn(job);
 
       replay(client, asyncJobClient);
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/predicates/NetworkPredicatesTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/predicates/NetworkPredicatesTest.java
index 2e39312..a2923d4 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/predicates/NetworkPredicatesTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/predicates/NetworkPredicatesTest.java
@@ -42,7 +42,7 @@
 public class NetworkPredicatesTest {
 
    public void testHasLoadBalancerService() {
-      Network network = Network.builder().id("204").services(ImmutableSet.of(new NetworkService("Lb"))).build();
+      Network network = Network.builder().id("204").services(ImmutableSet.of(NetworkService.builder().name("Lb").build())).build();
 
       assert hasLoadBalancerService().apply(network);
       assert !supportsStaticNAT().apply(network);
@@ -55,7 +55,8 @@
             .builder()
             .id("204")
             .services(
-                  ImmutableSet.of(new NetworkService("Firewall", ImmutableMap.<String, String> of("StaticNat", "true"))))
+                  ImmutableSet.of(NetworkService.builder().name("Firewall").capabilities(
+                        ImmutableMap.<String, String> of("StaticNat", "true")).build()))
             .build();
 
       assert !hasLoadBalancerService().apply(network);
@@ -65,7 +66,8 @@
 
    public void testNoSupport() {
       Network network = Network.builder().id("204")
-            .services(ImmutableSet.of(new NetworkService("Firewall", ImmutableMap.<String, String> of()))).build();
+            .services(ImmutableSet.of(NetworkService.builder().name("Firewall").capabilities(
+                  ImmutableMap.<String, String> of()).build())).build();
 
       assert !hasLoadBalancerService().apply(network);
       assert !supportsStaticNAT().apply(network);
@@ -77,8 +79,8 @@
             .builder()
             .id("204")
             .services(
-                  ImmutableSet.of(new NetworkService("Firewall", ImmutableMap.<String, String> of("PortForwarding",
-                        "true")))).build();
+                  ImmutableSet.of(NetworkService.builder().name("Firewall").capabilities(
+                        ImmutableMap.<String, String> of("PortForwarding", "true")).build())).build();
 
       assert !hasLoadBalancerService().apply(network);
       assert !supportsStaticNAT().apply(network);
@@ -90,8 +92,8 @@
             .builder()
             .id("204")
             .services(
-                  ImmutableSet.of(new NetworkService("Firewall", ImmutableMap.<String, String> of("StaticNat", "true",
-                        "PortForwarding", "true")))).build();
+                  ImmutableSet.of(NetworkService.builder().name("Firewall").capabilities(
+                        ImmutableMap.<String, String> of("StaticNat", "true", "PortForwarding", "true")).build())).build();
 
       assert Predicates.and(supportsPortForwarding(), supportsStaticNAT()).apply(network);
       assert !hasLoadBalancerService().apply(network);
@@ -99,10 +101,10 @@
    }
 
    public void testDefaultNetworkInZone() {
-      Network defaultInZone = Network.builder().isDefault(true).zoneId("42").build();
-      Network defaultNotInZone = Network.builder().isDefault(true).zoneId("200").build();
-      Network notDefaultInZone = Network.builder().isDefault(false).zoneId("42").build();
-      Network notDefaultNotInZone = Network.builder().isDefault(false).zoneId("200").build();
+      Network defaultInZone = Network.builder().id("42-1").isDefault(true).zoneId("42").build();
+      Network defaultNotInZone = Network.builder().id("200-1").isDefault(true).zoneId("200").build();
+      Network notDefaultInZone = Network.builder().id("42-2").isDefault(false).zoneId("42").build();
+      Network notDefaultNotInZone = Network.builder().id("200-2").isDefault(false).zoneId("200").build();
 
       Predicate<Network> predicate = defaultNetworkInZone("42");
       assertTrue(predicate.apply(defaultInZone));
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/predicates/OSCategoryInTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/predicates/OSCategoryInTest.java
index e994c58..c6f205c 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/predicates/OSCategoryInTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/predicates/OSCategoryInTest.java
@@ -76,7 +76,7 @@
    @Test
    public void testTemplateInAcceptableCategory() {
       assertTrue(new OSCategoryIn(client).apply(acceptableCategories).apply(
-         Template.builder().OSTypeId("10").build()
+         Template.builder().id("1").OSTypeId("10").build()
       ));
       verify(client, guestOSClient);
    }
@@ -84,7 +84,7 @@
    @Test
    public void testTemplateNotInAcceptableCategory() {
       assertFalse(new OSCategoryIn(client).apply(acceptableCategories).apply(
-         Template.builder().OSTypeId("30").build()
+         Template.builder().id("2").OSTypeId("30").build()
       ));
       verify(client, guestOSClient);
    }
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/predicates/TemplatePredicatesTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/predicates/TemplatePredicatesTest.java
index 451c5b2..46c25b7 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/predicates/TemplatePredicatesTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/predicates/TemplatePredicatesTest.java
@@ -35,20 +35,20 @@
    @Test
    public void testTemplateIsReady() {
       assertTrue(isReady().apply(
-         Template.builder().ready(true).build()
+         Template.builder().id("a").ready(true).build()
       ));
       assertFalse(isReady().apply(
-         Template.builder().ready(false).build()
+         Template.builder().id("b").ready(false).build()
       ));
    }
 
    @Test
    public void testTemplateIsPasswordEnabled() {
       assertTrue(isPasswordEnabled().apply(
-         Template.builder().passwordEnabled(true).build()
+         Template.builder().id("anid").passwordEnabled(true).build()
       ));
       assertFalse(isPasswordEnabled().apply(
-         Template.builder().passwordEnabled(false).build()
+         Template.builder().id("someid").passwordEnabled(false).build()
       ));
    }
 }
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/predicates/UserPredicatesTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/predicates/UserPredicatesTest.java
index 7e3f3ed..eb61014 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/predicates/UserPredicatesTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/predicates/UserPredicatesTest.java
@@ -40,10 +40,10 @@
    @Test
    public void testMatchApiKey() {
       assertTrue(apiKeyEquals("random-text").apply(
-         User.builder().apiKey("random-text").build()
+         User.builder().id("random-id").apiKey("random-text").build()
       ));
       assertFalse(apiKeyEquals("something-different").apply(
-         User.builder().apiKey("random-text").build()
+         User.builder().id("randome-id").apiKey("random-text").build()
       ));
    }
 
@@ -60,7 +60,7 @@
 
    @Test(dataProvider = "accountType")
    public void testAccountType(Account.Type type, boolean isUser, boolean isDomainAdmin, boolean isAdmin) {
-      User testUser = User.builder().accountType(type).build();
+      User testUser = User.builder().id("someid").accountType(type).build();
       assertEquals(isUserAccount().apply(testUser), isUser);
       assertEquals(isDomainAdminAccount().apply(testUser), isDomainAdmin);
       assertEquals(isAdminAccount().apply(testUser), isAdmin);
diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/predicates/ZonePredicatesTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/predicates/ZonePredicatesTest.java
index eb135bb..cb19474 100644
--- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/predicates/ZonePredicatesTest.java
+++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/predicates/ZonePredicatesTest.java
@@ -36,20 +36,20 @@
    @Test
    public void testSupportsAdvancedNetworks() {
       assertTrue(supportsAdvancedNetworks().apply(
-         Zone.builder().networkType(NetworkType.ADVANCED).build()
+         Zone.builder().id("41").networkType(NetworkType.ADVANCED).build()
       ));
       assertFalse(supportsAdvancedNetworks().apply(
-         Zone.builder().networkType(NetworkType.BASIC).build()
+         Zone.builder().id("42").networkType(NetworkType.BASIC).build()
       ));
    }
 
    @Test
    public void testSupportsSecurityGroups() {
       assertTrue(supportsSecurityGroups().apply(
-         Zone.builder().securityGroupsEnabled(true).build()
+         Zone.builder().id("43").securityGroupsEnabled(true).build()
       ));
       assertFalse(supportsSecurityGroups().apply(
-         Zone.builder().securityGroupsEnabled(false).build()
+         Zone.builder().id("44").securityGroupsEnabled(false).build()
       ));
    }
 
diff --git a/apis/deltacloud/src/main/java/org/jclouds/deltacloud/config/DeltacloudRestClientModule.java b/apis/deltacloud/src/main/java/org/jclouds/deltacloud/config/DeltacloudRestClientModule.java
index 73974a0..0bff28c 100644
--- a/apis/deltacloud/src/main/java/org/jclouds/deltacloud/config/DeltacloudRestClientModule.java
+++ b/apis/deltacloud/src/main/java/org/jclouds/deltacloud/config/DeltacloudRestClientModule.java
@@ -23,6 +23,7 @@
 import java.net.URI;
 import java.util.NoSuchElementException;
 import java.util.Set;
+import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicReference;
 
 import javax.inject.Named;
@@ -47,11 +48,12 @@
 import org.jclouds.rest.ConfiguresRestClient;
 import org.jclouds.rest.config.RestClientModule;
 import org.jclouds.rest.suppliers.MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier;
+import org.jclouds.util.Suppliers2;
 
 import com.google.common.base.Function;
+import com.google.common.base.Objects;
 import com.google.common.base.Predicate;
 import com.google.common.base.Supplier;
-import com.google.common.base.Suppliers;
 import com.google.common.collect.Iterables;
 import com.google.inject.Provides;
 
@@ -81,13 +83,17 @@
    @Singleton
    protected Supplier<Set<? extends DeltacloudCollection>> provideCollections(
          @Named(PROPERTY_SESSION_INTERVAL) long seconds, final DeltacloudClient client) {
-      return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier<Set<? extends DeltacloudCollection>>(
-            authException, seconds, new Supplier<Set<? extends DeltacloudCollection>>() {
+      return MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(
+            authException, new Supplier<Set<? extends DeltacloudCollection>>() {
                @Override
                public Set<? extends DeltacloudCollection> get() {
                   return client.getCollections();
                }
-            });
+               @Override
+               public String toString() {
+                  return Objects.toStringHelper(client).add("method", "getCollections").toString();
+               }
+            }, seconds, TimeUnit.SECONDS);
    }
 
    /**
@@ -97,7 +103,7 @@
    @Provides
    @Images
    protected Supplier<URI> provideImageCollection(Supplier<Set<? extends DeltacloudCollection>> collectionSupplier) {
-      return Suppliers.compose(new FindCollectionWithRelAndReturnHref("images"), collectionSupplier);
+      return Suppliers2.compose(new FindCollectionWithRelAndReturnHref("images"), collectionSupplier);
    }
 
    public static class FindCollectionWithRelAndReturnHref implements Function<Set<? extends DeltacloudCollection>, URI> {
@@ -128,24 +134,24 @@
    @Provides
    @HardwareProfiles
    protected Supplier<URI> provideHardwareProfileCollection(Supplier<Set<? extends DeltacloudCollection>> collectionSupplier) {
-      return Suppliers.compose(new FindCollectionWithRelAndReturnHref("hardware_profiles"), collectionSupplier);
+      return Suppliers2.compose(new FindCollectionWithRelAndReturnHref("hardware_profiles"), collectionSupplier);
    }
 
    @Provides
    @Instances
    protected Supplier<URI> provideInstanceCollection(Supplier<Set<? extends DeltacloudCollection>> collectionSupplier) {
-      return Suppliers.compose(new FindCollectionWithRelAndReturnHref("instances"), collectionSupplier);
+      return Suppliers2.compose(new FindCollectionWithRelAndReturnHref("instances"), collectionSupplier);
    }
 
    @Provides
    @Realms
    protected Supplier<URI> provideRealmCollection(Supplier<Set<? extends DeltacloudCollection>> collectionSupplier) {
-      return Suppliers.compose(new FindCollectionWithRelAndReturnHref("realms"), collectionSupplier);
+      return Suppliers2.compose(new FindCollectionWithRelAndReturnHref("realms"), collectionSupplier);
    }
 
    @Provides
    @InstanceStates
    protected Supplier<URI> provideInstanceStateCollection(Supplier<Set<? extends DeltacloudCollection>> collectionSupplier) {
-      return Suppliers.compose(new FindCollectionWithRelAndReturnHref("instance_states"), collectionSupplier);
+      return Suppliers2.compose(new FindCollectionWithRelAndReturnHref("instance_states"), collectionSupplier);
    }
 }
diff --git a/apis/ec2/src/main/clojure/org/jclouds/ec2/ami2.clj b/apis/ec2/src/main/clojure/org/jclouds/ec2/ami2.clj
new file mode 100644
index 0000000..0bb0b5b
--- /dev/null
+++ b/apis/ec2/src/main/clojure/org/jclouds/ec2/ami2.clj
@@ -0,0 +1,86 @@
+;
+; Licensed to jclouds, Inc. (jclouds) under one or more
+; contributor license agreements.  See the NOTICE file
+; distributed with this work for additional information
+; regarding copyright ownership.  jclouds licenses this file
+; to you under the Apache License, Version 2.0 (the
+; "License"); you may not use this file except in compliance
+; with the License.  You may obtain a copy of the License at
+;
+;   http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing,
+; software distributed under the License is distributed on an
+; "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+; KIND, either express or implied.  See the License for the
+; specific language governing permissions and limitations
+; under the License.
+;
+
+(ns
+  #^{:author "Hunter Hutchinson, hunter.hutchinson@gmail.com"
+     :doc "A clojure binding to the jclouds AMI service interface."}
+  org.jclouds.ec2.ami2
+  (:use org.jclouds.compute2 [clojure.core.incubator :only (-?>)])
+  (:import org.jclouds.aws.domain.Region
+    org.jclouds.ec2.services.AMIClient
+    org.jclouds.ec2.options.CreateImageOptions
+    org.jclouds.compute.domain.NodeMetadata
+    (org.jclouds.ec2.domain Volume Volume$Status Snapshot Snapshot$Status AvailabilityZoneInfo)))
+
+(defn ^org.jclouds.ec2.services.AMIClient
+  ami-service
+  ""
+  [compute]
+  (-> compute
+    .getContext
+    .getProviderSpecificContext
+    .getApi
+    .getAMIServices))
+
+(defn get-region
+  "Coerces the first parameter into a Region string; strings, keywords, and
+   NodeMetadata instances are acceptable arguments. An optional second argument
+   is returned if the first cannot be coerced into a region string.
+   Returns nil otherwise."
+  ([v] (get-region v nil))
+  ([v default-region]
+    (cond
+      (string? v) v
+      (keyword? v) (name v)
+      (instance? NodeMetadata v) (let [zone (location v)]
+      ; no easier way to go from zone -> region?
+      (if (> (.indexOf zone "-") -1)
+        (subs zone 0 (-> zone count dec))
+        zone))
+      :else default-region)))
+
+(defn- as-string
+  [v]
+  (cond
+    (string? v) v
+    (keyword? v) (name v)
+    :else v))
+
+(defn- get-string
+  [map key]
+  (as-string (get map key)))
+
+(defn- as-int
+  [v]
+  (cond
+    (number? v) (int v)
+    (string? v) (Integer/parseInt v)
+    :else (throw (IllegalArgumentException.
+    (str "Don't know how to convert object of type " (class v) " to a string")))))
+
+(defn create-image-in-region
+  ([compute region name node-id description]
+     (.createImageInRegion (ami-service compute)
+                              (get-region region)
+                              (as-string name)
+                              (as-string node-id)
+                              (into-array CreateImageOptions
+                                          (when description
+                                            [(.withDescription (CreateImageOptions.) description)])))))
+
diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/ImagesToRegionAndIdMap.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/ImagesToRegionAndIdMap.java
index 012b557..36f93ef 100644
--- a/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/ImagesToRegionAndIdMap.java
+++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/ImagesToRegionAndIdMap.java
@@ -18,6 +18,7 @@
  */
 package org.jclouds.ec2.compute.functions;
 
+import static com.google.common.base.Preconditions.checkState;
 import static com.google.common.collect.Maps.uniqueIndex;
 
 import java.util.Map;
@@ -38,18 +39,19 @@
    public static Map<RegionAndName, ? extends Image> imagesToMap(Iterable<? extends Image> input) {
       return new ImagesToRegionAndIdMap().apply(input);
    }
-   
+
    @Override
    public Map<RegionAndName, ? extends Image> apply(Iterable<? extends Image> input) {
       return uniqueIndex(input, new Function<Image, RegionAndName>() {
-         
+
          @Override
          public RegionAndName apply(Image from) {
+            checkState(from.getLocation() != null,
+                     "in ec2, image locations cannot be null; typically, they are Region-scoped");
             return new RegionAndName(from.getLocation().getId(), from.getProviderId());
          }
-         
+
       });
    }
 
-
 }
diff --git a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/KeystoneApiMetadata.java b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/KeystoneApiMetadata.java
index c15f193..03e4b07 100644
--- a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/KeystoneApiMetadata.java
+++ b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/KeystoneApiMetadata.java
@@ -26,8 +26,11 @@
 
 import org.jclouds.apis.ApiMetadata;
 import org.jclouds.openstack.keystone.v2_0.config.CredentialTypes;
+import org.jclouds.openstack.keystone.v2_0.config.KeystoneAuthenticationModule;
+import org.jclouds.openstack.keystone.v2_0.config.KeystoneParserModule;
 import org.jclouds.openstack.keystone.v2_0.config.KeystoneProperties;
 import org.jclouds.openstack.keystone.v2_0.config.KeystoneRestClientModule;
+import org.jclouds.openstack.keystone.v2_0.config.KeystoneRestClientModule.KeystoneAdminURLModule;
 import org.jclouds.openstack.v2_0.ServiceType;
 import org.jclouds.rest.RestContext;
 import org.jclouds.rest.internal.BaseRestApiMetadata;
@@ -85,7 +88,11 @@
          .version("2.0")
          .defaultEndpoint("http://localhost:5000")
          .defaultProperties(KeystoneApiMetadata.defaultProperties())
-         .defaultModules(ImmutableSet.<Class<? extends Module>>of(KeystoneRestClientModule.class));
+         .defaultModules(ImmutableSet.<Class<? extends Module>>builder()
+                                     .add(KeystoneAuthenticationModule.class)
+                                     .add(KeystoneAdminURLModule.class)
+                                     .add(KeystoneParserModule.class)
+                                     .add(KeystoneRestClientModule.class).build());
       }
       
       @Override
diff --git a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/config/KeystoneAuthenticationModule.java b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/config/KeystoneAuthenticationModule.java
index c556487..e89b9d8 100644
--- a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/config/KeystoneAuthenticationModule.java
+++ b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/config/KeystoneAuthenticationModule.java
@@ -35,12 +35,18 @@
 import org.jclouds.http.HttpRetryHandler;
 import org.jclouds.http.annotation.ClientError;
 import org.jclouds.location.Provider;
+import org.jclouds.location.suppliers.ImplicitLocationSupplier;
+import org.jclouds.location.suppliers.LocationsSupplier;
 import org.jclouds.location.suppliers.RegionIdToURISupplier;
 import org.jclouds.location.suppliers.RegionIdsSupplier;
 import org.jclouds.location.suppliers.ZoneIdToURISupplier;
 import org.jclouds.location.suppliers.ZoneIdsSupplier;
+import org.jclouds.location.suppliers.all.RegionToProvider;
+import org.jclouds.location.suppliers.all.ZoneToProvider;
 import org.jclouds.location.suppliers.derived.RegionIdsFromRegionIdToURIKeySet;
 import org.jclouds.location.suppliers.derived.ZoneIdsFromZoneIdToURIKeySet;
+import org.jclouds.location.suppliers.implicit.FirstRegion;
+import org.jclouds.location.suppliers.implicit.FirstZone;
 import org.jclouds.openstack.keystone.v2_0.AuthenticationAsyncClient;
 import org.jclouds.openstack.keystone.v2_0.AuthenticationClient;
 import org.jclouds.openstack.keystone.v2_0.domain.Access;
@@ -62,8 +68,8 @@
 import com.google.common.collect.ImmutableSet.Builder;
 import com.google.inject.AbstractModule;
 import com.google.inject.Injector;
-import com.google.inject.Module;
 import com.google.inject.Provides;
+import com.google.inject.Scopes;
 import com.google.inject.assistedinject.FactoryModuleBuilder;
 
 /**
@@ -71,25 +77,6 @@
  * @author Adrian Cole
  */
 public class KeystoneAuthenticationModule extends AbstractModule {
-   private final Module locationModule;
-
-   public KeystoneAuthenticationModule() {
-      this(new RegionModule());
-   }
-
-   protected KeystoneAuthenticationModule(Module locationModule) {
-      this.locationModule = locationModule;
-   }
-   
-   public static class KeystoneAuthenticationModuleForRegions extends KeystoneAuthenticationModule {
-      public KeystoneAuthenticationModuleForRegions() {
-         super(new RegionModule());
-      }
-   }
-
-   public static Module forRegions() {
-      return new KeystoneAuthenticationModuleForRegions();
-   }
    
    public static class RegionModule extends AbstractModule {
       @Override
@@ -100,6 +87,8 @@
                   RegionIdToAdminURIFromAccessForTypeAndVersion.class).build(RegionIdToAdminURISupplier.Factory.class));
          // dynamically build the region list as opposed to from properties
          bind(RegionIdsSupplier.class).to(RegionIdsFromRegionIdToURIKeySet.class);
+         bind(ImplicitLocationSupplier.class).to(FirstRegion.class).in(Scopes.SINGLETON);
+         bind(LocationsSupplier.class).to(RegionToProvider.class).in(Scopes.SINGLETON);
       }
 
       // supply the region to id map from keystone, based on the servicetype and api version in
@@ -132,6 +121,8 @@
                   ZoneIdToURIFromAccessForTypeAndVersion.class).build(ZoneIdToURISupplier.Factory.class));
          // dynamically build the zone list as opposed to from properties
          bind(ZoneIdsSupplier.class).to(ZoneIdsFromZoneIdToURIKeySet.class);
+         bind(ImplicitLocationSupplier.class).to(FirstZone.class).in(Scopes.SINGLETON);
+         bind(LocationsSupplier.class).to(ZoneToProvider.class).in(Scopes.SINGLETON);
       }
 
       // supply the zone to id map from keystone, based on the servicetype and api version in
@@ -146,21 +137,10 @@
 
    }
 
-   public static class KeystoneAuthenticationModuleForZones extends KeystoneAuthenticationModule {
-      public KeystoneAuthenticationModuleForZones() {
-         super(new ZoneModule());
-      }
-   }
-
-   public static Module forZones() {
-      return new KeystoneAuthenticationModuleForZones();
-   }
-
    @Override
    protected void configure() {
       bind(HttpRetryHandler.class).annotatedWith(ClientError.class).to(RetryOnRenew.class);
       bindAuthenticationClient();
-      install(locationModule);
    }
 
    protected void bindAuthenticationClient() {
diff --git a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/config/KeystoneParserModule.java b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/config/KeystoneParserModule.java
index 51e2927..55b2508 100644
--- a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/config/KeystoneParserModule.java
+++ b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/config/KeystoneParserModule.java
@@ -61,6 +61,7 @@
     * Treats [A,B,C] and {"values"=[A,B,C], "someotherstuff"=...} as the same Set
     */
    public static class SetTypeAdapterFactory implements TypeAdapterFactory {
+      @SuppressWarnings("unchecked")
       public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
          Type type = typeToken.getType();
          if (typeToken.getRawType() != Set.class || !(type instanceof ParameterizedType)) {
@@ -69,7 +70,7 @@
 
          Type elementType = ((ParameterizedType) type).getActualTypeArguments()[0];
          TypeAdapter<?> elementAdapter = gson.getAdapter(TypeToken.get(elementType));
-         return (TypeAdapter<T>) newSetAdapter(elementAdapter);
+         return TypeAdapter.class.cast(newSetAdapter(elementAdapter));
       }
 
       private <E> TypeAdapter<Set<E>> newSetAdapter(final TypeAdapter<E> elementAdapter) {
diff --git a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/config/KeystoneRestClientModule.java b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/config/KeystoneRestClientModule.java
index 88cf19e..62263f6 100644
--- a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/config/KeystoneRestClientModule.java
+++ b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/config/KeystoneRestClientModule.java
@@ -67,15 +67,13 @@
          RestClientModule<S, A> {
 
    public static final Map<Class<?>, Class<?>> DELEGATE_MAP = ImmutableMap.<Class<?>, Class<?>> builder()
-            .put(ServiceClient.class, ServiceAsyncClient.class)
-            .put(TokenClient.class, TokenAsyncClient.class)
-            .put(UserClient.class, UserAsyncClient.class)
-            .put(TenantClient.class, TenantAsyncClient.class).build();
+            .put(ServiceClient.class, ServiceAsyncClient.class).put(TokenClient.class, TokenAsyncClient.class)
+            .put(UserClient.class, UserAsyncClient.class).put(TenantClient.class, TenantAsyncClient.class).build();
 
    @SuppressWarnings("unchecked")
    public KeystoneRestClientModule() {
-      super((TypeToken) TypeToken.of(KeystoneClient.class), (TypeToken) TypeToken.of(KeystoneAsyncClient.class),
-               DELEGATE_MAP);
+      super(TypeToken.class.cast(TypeToken.of(KeystoneClient.class)), TypeToken.class.cast(TypeToken
+               .of(KeystoneAsyncClient.class)), DELEGATE_MAP);
    }
 
    protected KeystoneRestClientModule(TypeToken<S> syncClientType, TypeToken<A> asyncClientType,
@@ -83,23 +81,11 @@
       super(syncClientType, asyncClientType, sync2Async);
    }
 
-   @Override
-   protected void configure() {
-      install(new KeystoneParserModule());
-      super.configure();
-   }
-
-   @Override
-   protected void installLocations() {
-      install(new KeystoneAuthenticationModule(new KeystoneAdminURLModule()));
-      super.installLocations();
-   }
-
    public static class KeystoneAdminURLModule extends AbstractModule {
 
       @Override
       protected void configure() {
-         bind(ImplicitOptionalConverter.class).to(PresentWhenAdminURLExistsForIdentityService.class); 
+         bind(ImplicitOptionalConverter.class).to(PresentWhenAdminURLExistsForIdentityService.class);
          install(new FactoryModuleBuilder().implement(RegionIdToAdminURISupplier.class,
                   RegionIdToAdminURIFromAccessForTypeAndVersion.class).build(RegionIdToAdminURISupplier.Factory.class));
       }
diff --git a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Access.java b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Access.java
index b6e9e57..f8a821a 100644
--- a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Access.java
+++ b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Access.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
  * regarding copyright ownership.  jclouds licenses this file
- * to you under the Apache License, User 2.0 (the
+ * 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
  *
@@ -18,33 +18,35 @@
  */
 package org.jclouds.openstack.keystone.v2_0.domain;
 
-import static com.google.common.base.Objects.equal;
-import static com.google.common.base.Objects.toStringHelper;
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import java.beans.ConstructorProperties;
 import java.util.Set;
 
 import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
 import com.google.common.collect.ImmutableSet;
 
 /**
  * TODO
- * 
+ *
  * @author Adrian Cole
  * @see <a href="http://docs.openstack.org/api/openstack-identity-service/2.0/content/Service_API_Client_Operations.html"
- *      />
+/>
  */
 public class Access implements Comparable<Access> {
 
-   public static Builder builder() {
-      return new Builder();
+   public static Builder<?> builder() {
+      return new ConcreteBuilder();
    }
 
-   public Builder toBuilder() {
-      return builder().fromAccess(this);
+   public Builder<?> toBuilder() {
+      return new ConcreteBuilder().fromAccess(this);
    }
 
-   public static class Builder {
+   public static abstract class Builder<T extends Builder<T>>  {
+      protected abstract T self();
+
       protected Token token;
       protected User user;
       protected Set<Service> serviceCatalog = ImmutableSet.of();
@@ -52,55 +54,58 @@
       /**
        * @see Access#getToken()
        */
-      public Builder token(Token token) {
-         this.token = checkNotNull(token, "token");
-         return this;
+      public T token(Token token) {
+         this.token = token;
+         return self();
       }
 
       /**
        * @see Access#getUser()
        */
-      public Builder user(User user) {
-         this.user = checkNotNull(user, "user");
-         return this;
+      public T user(User user) {
+         this.user = user;
+         return self();
       }
 
       /**
        * @see Access#getServiceCatalog()
        */
-      public Builder serviceCatalog(Service... serviceCatalog) {
-         return serviceCatalog(ImmutableSet.copyOf(checkNotNull(serviceCatalog, "serviceCatalog")));
-      }
-
-      /**
-       * @see Access#getServiceCatalog()
-       */
-      public Builder serviceCatalog(Set<Service> serviceCatalog) {
+      public T serviceCatalog(Set<Service> serviceCatalog) {
          this.serviceCatalog = ImmutableSet.copyOf(checkNotNull(serviceCatalog, "serviceCatalog"));
-         return this;
+         return self();
+      }
+
+      public T serviceCatalog(Service... in) {
+         return serviceCatalog(ImmutableSet.copyOf(in));
       }
 
       public Access build() {
          return new Access(token, user, serviceCatalog);
       }
 
-      public Builder fromAccess(Access from) {
-         return token(from.getToken()).user(from.getUser()).serviceCatalog(from.getServiceCatalog());
+      public T fromAccess(Access in) {
+         return this
+               .token(in.getToken())
+               .user(in.getUser())
+               .serviceCatalog(in.getServiceCatalog());
       }
    }
 
-
-   protected Access() {
-      // we want serializers like Gson to work w/o using sun.misc.Unsafe,
-      // prohibited in GAE. This also implies fields are not final.
-      // see http://code.google.com/p/jclouds/issues/detail?id=925
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
    }
 
-   protected Token token;
-   protected User user;
-   protected Set<Service> serviceCatalog = ImmutableSet.of();
+   private final Token token;
+   private final User user;
+   private final Set<Service> serviceCatalog;
 
-   public Access(Token token, User user, Set<Service> serviceCatalog) {
+   @ConstructorProperties({
+         "token", "user", "serviceCatalog"
+   })
+   protected Access(Token token, User user, Set<Service> serviceCatalog) {
       this.token = checkNotNull(token, "token");
       this.user = checkNotNull(user, "user");
       this.serviceCatalog = ImmutableSet.copyOf(checkNotNull(serviceCatalog, "serviceCatalog"));
@@ -110,34 +115,21 @@
     * TODO
     */
    public Token getToken() {
-      return token;
+      return this.token;
    }
 
    /**
     * TODO
     */
    public User getUser() {
-      return user;
+      return this.user;
    }
 
    /**
     * TODO
     */
    public Set<Service> getServiceCatalog() {
-      return serviceCatalog;
-   }
-
-   @Override
-   public boolean equals(Object object) {
-      if (this == object) {
-         return true;
-      }
-      if (object instanceof Access) {
-         final Access other = Access.class.cast(object);
-         return equal(token, other.token) && equal(user, other.user) && equal(serviceCatalog, other.serviceCatalog);
-      } else {
-         return false;
-      }
+      return this.serviceCatalog;
    }
 
    @Override
@@ -146,10 +138,25 @@
    }
 
    @Override
-   public String toString() {
-      return toStringHelper("").add("token", token).add("user", user).add("serviceCatalog", serviceCatalog).toString();
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      Access that = Access.class.cast(obj);
+      return Objects.equal(this.token, that.token)
+            && Objects.equal(this.user, that.user)
+            && Objects.equal(this.serviceCatalog, that.serviceCatalog);
    }
 
+   protected ToStringHelper string() {
+      return Objects.toStringHelper(this)
+            .add("token", token).add("user", user).add("serviceCatalog", serviceCatalog);
+   }
+
+   @Override
+   public String toString() {
+      return string().toString();
+   }
+   
    @Override
    public int compareTo(Access that) {
       if (that == null)
diff --git a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/ApiAccessKeyCredentials.java b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/ApiAccessKeyCredentials.java
index f530389..2a06bad 100644
--- a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/ApiAccessKeyCredentials.java
+++ b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/ApiAccessKeyCredentials.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -16,71 +16,86 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
 package org.jclouds.openstack.keystone.v2_0.domain;
 
-import static com.google.common.base.Objects.equal;
-import static com.google.common.base.Objects.toStringHelper;
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import java.beans.ConstructorProperties;
+
 import org.jclouds.openstack.keystone.v2_0.config.CredentialType;
 import org.jclouds.openstack.keystone.v2_0.config.CredentialTypes;
 
 import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
 
 /**
  * Api AccessKey Credentials
- * 
+ *
  * @see <a href="http://docs.openstack.org/api/openstack-identity-service/2.0/content/POST_authenticate_v2.0_tokens_Service_API_Client_Operations.html#d662e583"
- *      />
+/>
  * @author Adrian Cole
  */
 @CredentialType(CredentialTypes.API_ACCESS_KEY_CREDENTIALS)
 public class ApiAccessKeyCredentials {
-   public static Builder builder() {
-      return new Builder();
+
+   public static Builder<?> builder() {
+      return new ConcreteBuilder();
    }
 
-   public Builder toBuilder() {
-      return builder().fromSecretKeyCredentials(this);
+   public Builder<?> toBuilder() {
+      return new ConcreteBuilder().fromApiAccessKeyCredentials(this);
    }
 
    public static ApiAccessKeyCredentials createWithAccessKeyAndSecretKey(String accessKey, String secretKey) {
-      return builder().secretKey(secretKey).accessKey(accessKey).build();
+      return new ApiAccessKeyCredentials(accessKey, secretKey);
    }
 
-   public static class Builder {
+   public static abstract class Builder<T extends Builder<T>>  {
+      protected abstract T self();
+
       protected String accessKey;
       protected String secretKey;
 
       /**
        * @see ApiAccessKeyCredentials#getAccessKey()
        */
-      protected Builder secretKey(String secretKey) {
-         this.secretKey = secretKey;
-         return this;
+      public T accessKey(String accessKey) {
+         this.accessKey = accessKey;
+         return self();
       }
 
       /**
        * @see ApiAccessKeyCredentials#getSecretKey()
        */
-      public Builder accessKey(String accessKey) {
-         this.accessKey = accessKey;
-         return this;
+      public T secretKey(String secretKey) {
+         this.secretKey = secretKey;
+         return self();
       }
 
       public ApiAccessKeyCredentials build() {
          return new ApiAccessKeyCredentials(accessKey, secretKey);
       }
 
-      public Builder fromSecretKeyCredentials(ApiAccessKeyCredentials from) {
-         return accessKey(from.getAccessKey()).secretKey(from.getSecretKey());
+      public T fromApiAccessKeyCredentials(ApiAccessKeyCredentials in) {
+         return this
+               .accessKey(in.getAccessKey())
+               .secretKey(in.getSecretKey());
       }
    }
 
-   protected final String accessKey;
-   protected final String secretKey;
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
+   }
 
+   private final String accessKey;
+   private final String secretKey;
+
+   @ConstructorProperties({
+         "accessKey", "secretKey"
+   })
    protected ApiAccessKeyCredentials(String accessKey, String secretKey) {
       this.accessKey = checkNotNull(accessKey, "accessKey");
       this.secretKey = checkNotNull(secretKey, "secretKey");
@@ -90,27 +105,14 @@
     * @return the accessKey
     */
    public String getAccessKey() {
-      return accessKey;
+      return this.accessKey;
    }
 
    /**
     * @return the secretKey
     */
    public String getSecretKey() {
-      return secretKey;
-   }
-
-   @Override
-   public boolean equals(Object object) {
-      if (this == object) {
-         return true;
-      }
-      if (object instanceof ApiAccessKeyCredentials) {
-         final ApiAccessKeyCredentials other = ApiAccessKeyCredentials.class.cast(object);
-         return equal(accessKey, other.accessKey) && equal(secretKey, other.secretKey);
-      } else {
-         return false;
-      }
+      return this.secretKey;
    }
 
    @Override
@@ -119,8 +121,22 @@
    }
 
    @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      ApiAccessKeyCredentials that = ApiAccessKeyCredentials.class.cast(obj);
+      return Objects.equal(this.accessKey, that.accessKey)
+            && Objects.equal(this.secretKey, that.secretKey);
+   }
+
+   protected ToStringHelper string() {
+      return Objects.toStringHelper(this)
+            .add("accessKey", accessKey).add("secretKey", secretKey);
+   }
+
+   @Override
    public String toString() {
-      return toStringHelper("").add("accessKey", accessKey).add("secretKey", secretKey).toString();
+      return string().toString();
    }
 
 }
diff --git a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/ApiMetadata.java b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/ApiMetadata.java
index 24ce002..766cae5 100644
--- a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/ApiMetadata.java
+++ b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/ApiMetadata.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -20,20 +20,23 @@
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
-import java.util.Collections;
+import java.beans.ConstructorProperties;
 import java.util.Date;
 import java.util.Set;
 
+import javax.inject.Named;
+
 import org.jclouds.javax.annotation.Nullable;
+import org.jclouds.openstack.v2_0.domain.Link;
 import org.jclouds.openstack.v2_0.domain.Resource;
 
 import com.google.common.base.Objects;
 import com.google.common.base.Objects.ToStringHelper;
 import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Sets;
-import com.google.gson.annotations.SerializedName;
 
 /**
+ * Class ApiMetadata
+ *
  * @author Adam Lowe
  */
 public class ApiMetadata extends Resource {
@@ -47,9 +50,9 @@
    }
 
    public static abstract class Builder<T extends Builder<T>> extends Resource.Builder<T>  {
-      private String status;
-      private Date updated;
-      private Set<MediaType> mediaTypes = Sets.newLinkedHashSet();
+      protected String status;
+      protected Date updated;
+      protected Set<MediaType> mediaTypes = ImmutableSet.of();
 
       /**
        * @see ApiMetadata#getStatus()
@@ -71,12 +74,16 @@
        * @see ApiMetadata#getMediaTypes()
        */
       public T mediaTypes(Set<MediaType> mediaTypes) {
-         this.mediaTypes = mediaTypes;
+         this.mediaTypes = ImmutableSet.copyOf(checkNotNull(mediaTypes, "mediaTypes"));
          return self();
       }
 
+      public T mediaTypes(MediaType... in) {
+         return mediaTypes(ImmutableSet.copyOf(in));
+      }
+
       public ApiMetadata build() {
-         return new ApiMetadata(this);
+         return new ApiMetadata(id, name, links, status, updated, mediaTypes);
       }
 
       public T fromApiMetadata(ApiMetadata in) {
@@ -85,7 +92,6 @@
                .updated(in.getUpdated())
                .mediaTypes(in.getMediaTypes());
       }
-
    }
 
    private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
@@ -94,44 +100,34 @@
          return this;
       }
    }
-   
-   protected ApiMetadata() {
-      // we want serializers like Gson to work w/o using sun.misc.Unsafe,
-      // prohibited in GAE. This also implies fields are not final.
-      // see http://code.google.com/p/jclouds/issues/detail?id=925
+
+   private final String status;
+   private final Date updated;
+   @Named("media-types")
+   private final Set<MediaType> mediaTypes;
+
+   @ConstructorProperties({
+         "id", "name", "links", "status", "updated", "media-types"
+   })
+   protected ApiMetadata(String id, @Nullable String name, java.util.Set<Link> links, @Nullable String status, @Nullable Date updated, Set<MediaType> mediaTypes) {
+      super(id, name, links);
+      this.status = status;
+      this.updated = updated;
+      this.mediaTypes = ImmutableSet.copyOf(checkNotNull(mediaTypes, "mediaTypes"));
    }
 
    @Nullable
-   private String status;
-   @Nullable
-   private Date updated;
-
-   @SerializedName(value="media-types")
-   private Set<MediaType> mediaTypes = Sets.newLinkedHashSet();
-
-   protected ApiMetadata(Builder<?> builder) {
-      super(builder);
-      this.status = checkNotNull(builder.status, "status");
-      this.updated = checkNotNull(builder.updated, "updated");
-      this.mediaTypes = ImmutableSet.copyOf(builder.mediaTypes);
-   }
-
-   /**
-    */
    public String getStatus() {
       return this.status;
    }
 
-   /**
-    */
+   @Nullable
    public Date getUpdated() {
       return this.updated;
    }
 
-   /**
-    */
    public Set<MediaType> getMediaTypes() {
-      return Collections.unmodifiableSet(this.mediaTypes);
+      return this.mediaTypes;
    }
 
    @Override
@@ -151,9 +147,7 @@
 
    protected ToStringHelper string() {
       return super.string()
-            .add("status", status)
-            .add("updated", updated)
-            .add("mediaTypes", mediaTypes);
+            .add("status", status).add("updated", updated).add("mediaTypes", mediaTypes);
    }
 
-}
\ No newline at end of file
+}
diff --git a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Endpoint.java b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Endpoint.java
index 5574ed5..9a54135 100644
--- a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Endpoint.java
+++ b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Endpoint.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
  * regarding copyright ownership.  jclouds licenses this file
- * to you under the Apache License, Name 2.0 (the
+ * 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
  *
@@ -16,40 +16,39 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
 package org.jclouds.openstack.keystone.v2_0.domain;
 
-import static com.google.common.base.Objects.equal;
-import static com.google.common.base.Objects.toStringHelper;
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import java.beans.ConstructorProperties;
 import java.net.URI;
 
 import org.jclouds.javax.annotation.Nullable;
 
 import com.google.common.base.Objects;
-import com.google.common.collect.ComparisonChain;
+import com.google.common.base.Objects.ToStringHelper;
 
 /**
  * An network-accessible address, usually described by URL, where a service may be accessed. If
  * using an extension for templates, you can create an endpoint template, which represents the
  * templates of all the consumable services that are available across the regions.
- * 
+ *
  * @author AdrianCole
  * @see <a href="http://docs.openstack.org/api/openstack-identity-service/2.0/content/Identity-Endpoint-Concepts-e1362.html"
- *      />
+/>
  */
-public class Endpoint implements Comparable<Endpoint> {
+public class Endpoint {
 
-   public static Builder builder() {
-      return new Builder();
+   public static Builder<?> builder() {
+      return new ConcreteBuilder();
    }
 
-   public Builder toBuilder() {
-      return builder().fromEndpoint(this);
+   public Builder<?> toBuilder() {
+      return new ConcreteBuilder().fromEndpoint(this);
    }
 
-   public static class Builder {
+   public static abstract class Builder<T extends Builder<T>>  {
+      protected abstract T self();
 
       protected String versionId;
       protected String region;
@@ -63,108 +62,112 @@
       /**
        * @see Endpoint#getVersionId()
        */
-      public Builder versionId(String versionId) {
-         this.versionId = checkNotNull(versionId, "versionId");
-         return this;
+      public T versionId(String versionId) {
+         this.versionId = versionId;
+         return self();
       }
 
       /**
        * @see Endpoint#getRegion()
        */
-      public Builder region(String region) {
-         this.region = checkNotNull(region, "region");
-         return this;
+      public T region(String region) {
+         this.region = region;
+         return self();
       }
 
       /**
        * @see Endpoint#getPublicURL()
        */
-      public Builder publicURL(URI publicURL) {
-         this.publicURL = checkNotNull(publicURL, "publicURL");
-         return this;
+      public T publicURL(URI publicURL) {
+         this.publicURL = publicURL;
+         return self();
       }
 
       /**
        * @see Endpoint#getInternalURL()
        */
-      public Builder internalURL(URI internalURL) {
-         this.internalURL = checkNotNull(internalURL, "internalURL");
-         return this;
+      public T internalURL(URI internalURL) {
+         this.internalURL = internalURL;
+         return self();
       }
 
       /**
-       * @see Endpoint#getInternalURL()
+       * @see Endpoint#getAdminURL()
        */
-      public Builder adminURL(URI adminURL) {
-         this.adminURL = checkNotNull(adminURL, "adminURL");
-         return this;
+      public T adminURL(URI adminURL) {
+         this.adminURL = adminURL;
+         return self();
       }
-      
-      /**
-       * @see Endpoint#getTenantId()
-       */
-      public Builder tenantId(@Nullable String tenantId) {
-         this.tenantId = tenantId;
-         return this;
-      }
-      
+
       /**
        * @see Endpoint#getVersionInfo()
        */
-      public Builder versionInfo(URI versionInfo) {
-         this.versionInfo = checkNotNull(versionInfo, "versionInfo");
-         return this;
+      public T versionInfo(URI versionInfo) {
+         this.versionInfo = versionInfo;
+         return self();
       }
-      
+
       /**
        * @see Endpoint#getVersionList()
        */
-      public Builder versionList(URI versionList) {
-         this.versionList = checkNotNull(versionList, "versionList");
+      public T versionList(URI versionList) {
+         this.versionList = versionList;
+         return self();
+      }
+
+      /**
+       * @see Endpoint#getTenantId()
+       */
+      public T tenantId(String tenantId) {
+         this.tenantId = tenantId;
+         return self();
+      }
+
+      public Endpoint build() {
+         return new Endpoint(null, versionId, region, publicURL, internalURL, adminURL, versionInfo, versionList, null, tenantId);
+      }
+
+      public T fromEndpoint(Endpoint in) {
+         return this
+               .versionId(in.getVersionId())
+               .region(in.getRegion())
+               .publicURL(in.getPublicURL())
+               .internalURL(in.getInternalURL())
+               .adminURL(in.getAdminURL())
+               .versionInfo(in.getVersionInfo())
+               .versionList(in.getVersionList())
+               .tenantId(in.getTenantId());
+      }
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
          return this;
       }
-      
-      public Endpoint build() {
-         return new Endpoint(versionId, region, publicURL, internalURL, adminURL, tenantId, versionInfo, versionList);
-      }
-
-      public Builder fromEndpoint(Endpoint from) {
-         return versionId(from.getVersionId()).region(from.getRegion()).publicURL(from.getPublicURL()).internalURL(
-                  from.getInternalURL()).tenantId(from.getTenantId()).versionInfo(from.getVersionInfo()).versionList(
-                  from.getVersionList());
-      }
    }
-   
-   protected Endpoint() {
-      // we want serializers like Gson to work w/o using sun.misc.Unsafe,
-      // prohibited in GAE. This also implies fields are not final.
-      // see http://code.google.com/p/jclouds/issues/detail?id=925
-   }
-   
-   // renamed half-way through
-   @Deprecated
-   protected String id;
-   protected String versionId;
-   protected String region;
-   protected URI publicURL;
-   protected URI internalURL;
-   protected URI adminURL;
-   protected URI versionInfo;
-   protected URI versionList;
-   
-   // renamed half-way through
-   @Deprecated
-   protected String tenantName;
-   protected String tenantId;
 
-   protected Endpoint(@Nullable String versionId, @Nullable String region, @Nullable URI publicURL, @Nullable URI internalURL,
-            @Nullable URI adminURL, @Nullable String tenantId, @Nullable URI versionInfo, @Nullable URI versionList) {
-      this.versionId = versionId;
+   private final String versionId;
+   private final String region;
+   private final URI publicURL;
+   private final URI internalURL;
+   private final URI adminURL;
+   private final URI versionInfo;
+   private final URI versionList;
+   private final String tenantId;
+
+   @ConstructorProperties({
+         "id", "versionId", "region", "publicURL", "internalURL", "adminURL", "versionInfo", "versionList", "tenantName", "tenantId"
+   })
+   protected Endpoint(@Nullable String id, @Nullable String versionId, @Nullable String region, @Nullable URI publicURL,
+                      @Nullable URI internalURL, @Nullable URI adminURL, @Nullable URI versionInfo, @Nullable URI versionList,
+                      @Nullable String tenantName, @Nullable String tenantId) {
+      this.versionId = versionId != null ? versionId : id;
+      this.tenantId = tenantId != null ? tenantId : tenantName;
       this.region = region;
       this.publicURL = publicURL;
       this.internalURL = internalURL;
       this.adminURL = adminURL;
-      this.tenantId = tenantId;
       this.versionInfo = versionInfo;
       this.versionList = versionList;
    }
@@ -172,20 +175,20 @@
    /**
     * When providing an ID, it is assumed that the endpoint exists in the current OpenStack
     * deployment
-    * 
+    *
     * @return the versionId of the endpoint in the current OpenStack deployment, or null if not specified
     */
    @Nullable
    public String getVersionId() {
-      return versionId != null ? versionId : id;
+      return this.versionId;
    }
 
    /**
     * @return the region of the endpoint, or null if not specified
     */
-   @Nullable 
+   @Nullable
    public String getRegion() {
-      return region;
+      return this.region;
    }
 
    /**
@@ -193,7 +196,7 @@
     */
    @Nullable
    public URI getPublicURL() {
-      return publicURL;
+      return this.publicURL;
    }
 
    /**
@@ -201,7 +204,7 @@
     */
    @Nullable
    public URI getInternalURL() {
-      return internalURL;
+      return this.internalURL;
    }
 
    /**
@@ -209,7 +212,17 @@
     */
    @Nullable
    public URI getAdminURL() {
-      return adminURL;
+      return this.adminURL;
+   }
+
+   @Nullable
+   public URI getVersionInfo() {
+      return this.versionInfo;
+   }
+
+   @Nullable
+   public URI getVersionList() {
+      return this.versionList;
    }
 
    /**
@@ -217,53 +230,38 @@
     */
    @Nullable
    public String getTenantId() {
-      return tenantId != null ? tenantId : tenantName;
-   }
-
-   /**
-    */
-   @Nullable
-   public URI getVersionInfo() {
-      return versionInfo;
-   }
-
-   /**
-    */
-   @Nullable
-   public URI getVersionList() {
-      return versionList;
-   }
-   
-   @Override
-   public boolean equals(Object object) {
-      if (this == object) {
-         return true;
-      }
-      if (object instanceof Endpoint) {
-         final Endpoint other = Endpoint.class.cast(object);
-         return equal(getVersionId(), other.getVersionId()) && equal(region, other.region) && equal(publicURL, other.publicURL)
-                  && equal(internalURL, other.internalURL) && equal(adminURL, other.adminURL) && equal(getTenantId(), other.getTenantId());
-      } else {
-         return false;
-      }
+      return this.tenantId;
    }
 
    @Override
    public int hashCode() {
-      return Objects.hashCode(getVersionId(), region, publicURL, internalURL, adminURL, getTenantId());
+      return Objects.hashCode(versionId, region, publicURL, internalURL, adminURL, versionInfo, versionList, tenantId);
+   }
+
+   @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      Endpoint that = Endpoint.class.cast(obj);
+      return Objects.equal(this.versionId, that.versionId)
+            && Objects.equal(this.region, that.region)
+            && Objects.equal(this.publicURL, that.publicURL)
+            && Objects.equal(this.internalURL, that.internalURL)
+            && Objects.equal(this.adminURL, that.adminURL)
+            && Objects.equal(this.versionInfo, that.versionInfo)
+            && Objects.equal(this.versionList, that.versionList)
+            && Objects.equal(this.tenantId, that.tenantId);
+   }
+
+   protected ToStringHelper string() {
+      return Objects.toStringHelper(this)
+            .add("versionId", versionId).add("region", region).add("publicURL", publicURL).add("internalURL", internalURL)
+            .add("adminURL", adminURL).add("versionInfo", versionInfo).add("versionList", versionList).add("tenantId", tenantId);
    }
 
    @Override
    public String toString() {
-      return toStringHelper("").add("versionId", getVersionId()).add("region", region).add("publicURL", publicURL).add(
-               "internalURL", internalURL).add("adminURL", adminURL).add("tenantId", getTenantId()).add("versionInfo",
-               versionInfo).add("versionList", versionList).toString();
-   }
-
-   @Override
-   public int compareTo(Endpoint that) {
-      return ComparisonChain.start().compare(this.getTenantId(), that.getTenantId()).compare(this.getVersionId(), that.getVersionId())
-               .compare(this.region, that.region).result();
+      return string().toString();
    }
 
 }
diff --git a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/MediaType.java b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/MediaType.java
index 015b69a..9926289 100644
--- a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/MediaType.java
+++ b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/MediaType.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -18,6 +18,8 @@
  */
 package org.jclouds.openstack.keystone.v2_0.domain;
 
+import java.beans.ConstructorProperties;
+
 import org.jclouds.javax.annotation.Nullable;
 
 import com.google.common.base.Objects;
@@ -28,61 +30,70 @@
  */
 public class MediaType {
 
-   public static Builder builder() {
-      return new Builder();
+   public static Builder<?> builder() {
+      return new ConcreteBuilder();
    }
 
-   public Builder toBuilder() {
-      return builder().fromMediaType(this);
+   public Builder<?> toBuilder() {
+      return new ConcreteBuilder().fromMediaType(this);
    }
 
-   public static class Builder  {
+   public static abstract class Builder<T extends Builder<T>>  {
+      protected abstract T self();
 
-      private String base;
-      private String type;
+      protected String base;
+      protected String type;
 
-      public Builder base(String base) {
+      /**
+       * @see MediaType#getBase()
+       */
+      public T base(String base) {
          this.base = base;
-         return this;
+         return self();
       }
 
-      public Builder type(String type) {
+      /**
+       * @see MediaType#getType()
+       */
+      public T type(String type) {
          this.type = type;
-         return this;
+         return self();
       }
 
       public MediaType build() {
-         return new MediaType(this);
+         return new MediaType(base, type);
       }
 
-      public Builder fromMediaType(MediaType in) {
-         return this.base(in.getBase()).type(in.getType());
+      public T fromMediaType(MediaType in) {
+         return this
+               .base(in.getBase())
+               .type(in.getType());
       }
    }
-   
-   protected MediaType() {
-      // we want serializers like Gson to work w/o using sun.misc.Unsafe,
-      // prohibited in GAE. This also implies fields are not final.
-      // see http://code.google.com/p/jclouds/issues/detail?id=925
-   }
-   
-   private String base;
-   private String type;
 
-   protected MediaType(Builder builder) {
-      this.base = builder.base;
-      this.type = builder.type;
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
    }
 
-   /**
-    */
+   private final String base;
+   private final String type;
+
+   @ConstructorProperties({
+         "base", "type"
+   })
+   protected MediaType(@Nullable String base, @Nullable String type) {
+      this.base = base;
+      this.type = type;
+   }
+
    @Nullable
    public String getBase() {
       return this.base;
    }
 
-   /**
-    */
    @Nullable
    public String getType() {
       return this.type;
@@ -99,14 +110,12 @@
       if (obj == null || getClass() != obj.getClass()) return false;
       MediaType that = MediaType.class.cast(obj);
       return Objects.equal(this.base, that.base)
-            && Objects.equal(this.type, that.type)
-            ;
+            && Objects.equal(this.type, that.type);
    }
 
    protected ToStringHelper string() {
-      return Objects.toStringHelper("")
-            .add("base", base)
-            .add("type", type);
+      return Objects.toStringHelper(this)
+            .add("base", base).add("type", type);
    }
 
    @Override
@@ -114,4 +123,4 @@
       return string().toString();
    }
 
-}
\ No newline at end of file
+}
diff --git a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/PasswordCredentials.java b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/PasswordCredentials.java
index 72fec16..4e425d9 100644
--- a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/PasswordCredentials.java
+++ b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/PasswordCredentials.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -16,71 +16,85 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
 package org.jclouds.openstack.keystone.v2_0.domain;
 
-import static com.google.common.base.Objects.equal;
-import static com.google.common.base.Objects.toStringHelper;
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import java.beans.ConstructorProperties;
+
 import org.jclouds.openstack.keystone.v2_0.config.CredentialType;
-import org.jclouds.openstack.keystone.v2_0.config.CredentialTypes;
 
 import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
 
 /**
  * Password Credentials
- * 
+ *
  * @see <a href="http://docs.openstack.org/api/openstack-identity-service/2.0/content/POST_authenticate_v2.0_tokens_Service_API_Client_Operations.html#d662e583"
- *      />
+/>
  * @author Adrian Cole
  */
-@CredentialType(CredentialTypes.PASSWORD_CREDENTIALS)
+@CredentialType("passwordCredentials")
 public class PasswordCredentials {
-   public static Builder builder() {
-      return new Builder();
+
+   public static Builder<?> builder() {
+      return new ConcreteBuilder();
    }
 
-   public Builder toBuilder() {
-      return builder().fromPasswordCredentials(this);
+   public Builder<?> toBuilder() {
+      return new ConcreteBuilder().fromPasswordCredentials(this);
    }
 
    public static PasswordCredentials createWithUsernameAndPassword(String username, String password) {
-      return builder().password(password).username(username).build();
+      return new PasswordCredentials(username, password);
    }
 
-   public static class Builder {
+   public static abstract class Builder<T extends Builder<T>>  {
+      protected abstract T self();
+
       protected String username;
       protected String password;
 
       /**
        * @see PasswordCredentials#getUsername()
        */
-      protected Builder password(String password) {
-         this.password = password;
-         return this;
+      public T username(String username) {
+         this.username = username;
+         return self();
       }
 
       /**
        * @see PasswordCredentials#getPassword()
        */
-      public Builder username(String username) {
-         this.username = username;
-         return this;
+      public T password(String password) {
+         this.password = password;
+         return self();
       }
 
       public PasswordCredentials build() {
          return new PasswordCredentials(username, password);
       }
 
-      public Builder fromPasswordCredentials(PasswordCredentials from) {
-         return username(from.getUsername()).password(from.getPassword());
+      public T fromPasswordCredentials(PasswordCredentials in) {
+         return this
+               .username(in.getUsername())
+               .password(in.getPassword());
       }
    }
 
-   protected final String username;
-   protected final String password;
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
+   }
 
+   private final String username;
+   private final String password;
+
+   @ConstructorProperties({
+         "username", "password"
+   })
    protected PasswordCredentials(String username, String password) {
       this.username = checkNotNull(username, "username");
       this.password = checkNotNull(password, "password");
@@ -90,27 +104,14 @@
     * @return the username
     */
    public String getUsername() {
-      return username;
+      return this.username;
    }
 
    /**
     * @return the password
     */
    public String getPassword() {
-      return password;
-   }
-
-   @Override
-   public boolean equals(Object object) {
-      if (this == object) {
-         return true;
-      }
-      if (object instanceof PasswordCredentials) {
-         final PasswordCredentials other = PasswordCredentials.class.cast(object);
-         return equal(username, other.username) && equal(password, other.password);
-      } else {
-         return false;
-      }
+      return this.password;
    }
 
    @Override
@@ -119,8 +120,22 @@
    }
 
    @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      PasswordCredentials that = PasswordCredentials.class.cast(obj);
+      return Objects.equal(this.username, that.username)
+            && Objects.equal(this.password, that.password);
+   }
+
+   protected ToStringHelper string() {
+      return Objects.toStringHelper(this)
+            .add("username", username).add("password", password);
+   }
+
+   @Override
    public String toString() {
-      return toStringHelper("").add("username", username).add("password", password).toString();
+      return string().toString();
    }
 
 }
diff --git a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Role.java b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Role.java
index 51e8ec4..a84ee66 100644
--- a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Role.java
+++ b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Role.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
  * regarding copyright ownership.  jclouds licenses this file
- * to you under the Apache License, Name 2.0 (the
+ * 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
  *
@@ -16,17 +16,16 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
 package org.jclouds.openstack.keystone.v2_0.domain;
 
-import static com.google.common.base.Objects.equal;
-import static com.google.common.base.Objects.toStringHelper;
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import java.beans.ConstructorProperties;
+
 import org.jclouds.javax.annotation.Nullable;
 
 import com.google.common.base.Objects;
-import com.google.common.collect.ComparisonChain;
+import com.google.common.base.Objects.ToStringHelper;
 
 /**
  * A personality that a user assumes when performing a specific set of operations. A role includes a
@@ -35,22 +34,23 @@
  * In Keystone, a token that is issued to a user includes the list of roles that user can assume.
  * Services that are being called by that user determine how they interpret the set of roles a user
  * has and which operations or resources each roles grants access to.
- * 
+ *
  * @author AdrianCole
  * @see <a href="http://docs.openstack.org/api/openstack-identity-service/2.0/content/Identity-Service-Concepts-e1362.html"
- *      />
+/>
  */
-public class Role implements Comparable<Role> {
+public class Role {
 
-   public static Builder builder() {
-      return new Builder();
+   public static Builder<?> builder() {
+      return new ConcreteBuilder();
    }
 
-   public Builder toBuilder() {
-      return builder().fromRole(this);
+   public Builder<?> toBuilder() {
+      return new ConcreteBuilder().fromRole(this);
    }
 
-   public static class Builder {
+   public static abstract class Builder<T extends Builder<T>>  {
+      protected abstract T self();
       protected String id;
       protected String name;
       protected String description;
@@ -60,97 +60,104 @@
       /**
        * @see Role#getId()
        */
-      public Builder id(String id) {
-         this.id = checkNotNull(id, "id");
-         return this;
+      public T id(String id) {
+         this.id = id;
+         return self();
       }
 
       /**
        * @see Role#getName()
        */
-      public Builder name(String name) {
-         this.name = checkNotNull(name, "name");
-         return this;
+      public T name(String name) {
+         this.name = name;
+         return self();
       }
-      
+
       /**
        * @see Role#getDescription()
        */
-      public Builder description(String description) {
-         this.description = checkNotNull(description, "description");
-         return this;
+      public T description(String description) {
+         this.description = description;
+         return self();
       }
-      
+
       /**
        * @see Role#getServiceId()
        */
-      public Builder serviceId(@Nullable String serviceId) {
+      public T serviceId(String serviceId) {
          this.serviceId = serviceId;
-         return this;
+         return self();
       }
 
       /**
        * @see Role#getTenantId()
        */
-      public Builder tenantId(@Nullable String tenantId) {
+      public T tenantId(String tenantId) {
          this.tenantId = tenantId;
-         return this;
+         return self();
       }
 
       public Role build() {
-         return new Role(id, name, description, serviceId, tenantId);
+         return new Role(id, name, description, serviceId, tenantId, null);
       }
 
-      public Builder fromRole(Role from) {
-         return id(from.getId()).name(from.getName()).description(from.getName()).serviceId(from.getServiceId()).tenantId(from.getTenantId());
+      public T fromRole(Role in) {
+         return this
+               .id(in.getId())
+               .name(in.getName())
+               .description(in.getDescription())
+               .serviceId(in.getServiceId())
+               .tenantId(in.getTenantId());
       }
    }
-   
-   protected Role() {
-      // we want serializers like Gson to work w/o using sun.misc.Unsafe,
-      // prohibited in GAE. This also implies fields are not final.
-      // see http://code.google.com/p/jclouds/issues/detail?id=925
-   }
-   
-   protected String id;
-   protected String name;
-   protected String description;
-   protected String serviceId;
-   // renamed half-way through
-   @Deprecated
-   protected String tenantName;
-   protected String tenantId;
 
-   protected Role(String id, String name, @Nullable String description, @Nullable String serviceId, @Nullable String tenantId) {
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
+   }
+
+   private final String id;
+   private final String name;
+   private final String description;
+   private final String serviceId;
+   private final String tenantId;
+
+   @ConstructorProperties({
+         "id", "name", "description", "serviceId", "tenantId", "tenantName"
+   })
+   protected Role(String id, String name, @Nullable String description, @Nullable String serviceId, @Nullable String tenantId,
+                  @Nullable String tenantName) {
       this.id = checkNotNull(id, "id");
       this.name = checkNotNull(name, "name");
       this.description = description;
       this.serviceId = serviceId;
-      this.tenantId = tenantId;
+      this.tenantId = tenantId != null ? tenantId :  tenantName;
    }
 
    /**
     * When providing an ID, it is assumed that the role exists in the current OpenStack deployment
-    * 
+    *
     * @return the id of the role in the current OpenStack deployment
     */
    public String getId() {
-      return id;
+      return this.id;
    }
 
    /**
     * @return the name of the role
     */
    public String getName() {
-      return name;
+      return this.name;
    }
-   
+
    /**
     * @return the description of the role
     */
    @Nullable
    public String getDescription() {
-      return description;
+      return this.description;
    }
 
    /**
@@ -158,7 +165,7 @@
     */
    @Nullable
    public String getServiceId() {
-      return serviceId;
+      return this.serviceId;
    }
 
    /**
@@ -166,37 +173,34 @@
     */
    @Nullable
    public String getTenantId() {
-      return tenantId != null ? tenantId : tenantName;
-   }
-
-   @Override
-   public boolean equals(Object object) {
-      if (this == object) {
-         return true;
-      }
-      if (object instanceof Role) {
-         final Role other = Role.class.cast(object);
-         return equal(id, other.id) && equal(name, other.name) && equal(serviceId, other.serviceId)
-                  && equal(getTenantId(), other.getTenantId());
-      } else {
-         return false;
-      }
+      return this.tenantId;
    }
 
    @Override
    public int hashCode() {
-      return Objects.hashCode(id, name, serviceId, getTenantId());
+      return Objects.hashCode(id, name, description, serviceId, tenantId);
+   }
+
+   @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      Role that = Role.class.cast(obj);
+      return Objects.equal(this.id, that.id)
+            && Objects.equal(this.name, that.name)
+            && Objects.equal(this.description, that.description)
+            && Objects.equal(this.serviceId, that.serviceId)
+            && Objects.equal(this.tenantId, that.tenantId);
+   }
+
+   protected ToStringHelper string() {
+      return Objects.toStringHelper(this)
+            .add("id", id).add("name", name).add("description", description).add("serviceId", serviceId).add("tenantId", tenantId);
    }
 
    @Override
    public String toString() {
-      return toStringHelper("").add("id", id).add("name", name).add("description", description).add("serviceId", serviceId).add("tenantId", getTenantId())
-               .toString();
-   }
-   
-   @Override
-   public int compareTo(Role that) {
-      return ComparisonChain.start().compare(this.id, that.id).result();
+      return string().toString();
    }
 
 }
diff --git a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Service.java b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Service.java
index 2da7bab..23a1bff 100644
--- a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Service.java
+++ b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Service.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
  * regarding copyright ownership.  jclouds licenses this file
- * to you under the Apache License, Name 2.0 (the
+ * 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
  *
@@ -18,35 +18,39 @@
  */
 package org.jclouds.openstack.keystone.v2_0.domain;
 
-import static com.google.common.base.Objects.equal;
-import static com.google.common.base.Objects.toStringHelper;
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import java.beans.ConstructorProperties;
 import java.util.Set;
 
 import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
+import com.google.common.collect.ComparisonChain;
+import com.google.common.collect.ForwardingSet;
 import com.google.common.collect.ImmutableSet;
 
 /**
  * An OpenStack service, such as Compute (Nova), Object Storage (Swift), or Image Service (Glance).
  * A service provides one or more endpoints through which users can access resources and perform
  * (presumably useful) operations.
- * 
+ *
  * @author Adrian Cole
  * @see <a href="http://docs.openstack.org/api/openstack-typeentity-service/2.0/content/Identity-Service-Concepts-e1362.html"
- *      />
+/>
  */
-public class Service implements Comparable<Service> {
+public class Service extends ForwardingSet<Endpoint> implements Comparable<Service> {
 
-   public static Builder builder() {
-      return new Builder();
+   public static Builder<?> builder() {
+      return new ConcreteBuilder();
    }
 
-   public Builder toBuilder() {
-      return builder().fromService(this);
+   public Builder<?> toBuilder() {
+      return new ConcreteBuilder().fromService(this);
    }
 
-   public static class Builder {
+   public static abstract class Builder<T extends Builder<T>>  {
+      protected abstract T self();
+
       protected String type;
       protected String name;
       protected Set<Endpoint> endpoints = ImmutableSet.of();
@@ -54,54 +58,57 @@
       /**
        * @see Service#getType()
        */
-      public Builder type(String type) {
-         this.type = checkNotNull(type, "type");
-         return this;
+      public T type(String type) {
+         this.type = type;
+         return self();
       }
 
       /**
        * @see Service#getName()
        */
-      public Builder name(String name) {
-         this.name = checkNotNull(name, "name");
-         return this;
+      public T name(String name) {
+         this.name = name;
+         return self();
       }
 
       /**
        * @see Service#getEndpoints()
        */
-      public Builder endpoints(Endpoint... endpoints) {
-         return endpoints(ImmutableSet.copyOf(checkNotNull(endpoints, "endpoints")));
-      }
-
-      /**
-       * @see Service#getEndpoints()
-       */
-      public Builder endpoints(Set<Endpoint> endpoints) {
+      public T endpoints(Set<Endpoint> endpoints) {
          this.endpoints = ImmutableSet.copyOf(checkNotNull(endpoints, "endpoints"));
-         return this;
+         return self();
+      }
+
+      public T endpoints(Endpoint... in) {
+         return endpoints(ImmutableSet.copyOf(in));
       }
 
       public Service build() {
          return new Service(type, name, endpoints);
       }
 
-      public Builder fromService(Service from) {
-         return type(from.getType()).name(from.getName()).endpoints(from.getEndpoints());
+      public T fromService(Service in) {
+         return this
+               .type(in.getType())
+               .name(in.getName())
+               .endpoints(in.getEndpoints());
       }
    }
-   
-   protected Service() {
-      // we want serializers like Gson to work w/o using sun.misc.Unsafe,
-      // prohibited in GAE. This also implies fields are not final.
-      // see http://code.google.com/p/jclouds/issues/detail?id=925
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
    }
-   
-   protected String type;
-   protected String name;
-   protected Set<Endpoint> endpoints = ImmutableSet.of();
 
-   public Service(String type, String name, Set<Endpoint> endpoints) {
+   private final String type;
+   private final String name;
+   private final Set<Endpoint> endpoints;
+
+   @ConstructorProperties({
+         "type", "name", "endpoints"
+   })
+   protected Service(String type, String name, Set<Endpoint> endpoints) {
       this.type = checkNotNull(type, "type");
       this.name = checkNotNull(name, "name");
       this.endpoints = ImmutableSet.copyOf(checkNotNull(endpoints, "endpoints"));
@@ -109,38 +116,25 @@
 
    /**
     * such as {@code compute} (Nova), {@code object-store} (Swift), or {@code image} (Glance)
-    * 
+    *
     * @return the type of the service in the current OpenStack deployment
     */
    public String getType() {
-      return type;
+      return this.type;
    }
 
    /**
     * @return the name of the service
     */
    public String getName() {
-      return name;
+      return this.name;
    }
 
    /**
     * @return the endpoints assigned to the service
     */
    public Set<Endpoint> getEndpoints() {
-      return endpoints;
-   }
-
-   @Override
-   public boolean equals(Object object) {
-      if (this == object) {
-         return true;
-      }
-      if (object instanceof Service) {
-         final Service other = Service.class.cast(object);
-         return equal(type, other.type) && equal(name, other.name) && equal(endpoints, other.endpoints);
-      } else {
-         return false;
-      }
+      return this.endpoints;
    }
 
    @Override
@@ -149,17 +143,35 @@
    }
 
    @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      Service that = Service.class.cast(obj);
+      return Objects.equal(this.type, that.type)
+            && Objects.equal(this.name, that.name)
+            && Objects.equal(this.endpoints, that.endpoints);
+   }
+
+   protected ToStringHelper string() {
+      return Objects.toStringHelper(this)
+            .add("type", type).add("name", name).add("endpoints", endpoints);
+   }
+
+   @Override
    public String toString() {
-      return toStringHelper("").add("type", type).add("name", name).add("endpoints", endpoints).toString();
+      return string().toString();
    }
 
    @Override
    public int compareTo(Service that) {
-      if (that == null)
-         return 1;
-      if (this == that)
-         return 0;
-      return this.type.compareTo(that.type);
+      return ComparisonChain.start()
+                            .compare(this.type, that.type)
+                            .compare(this.name, that.name)
+                            .result();
    }
 
+   @Override
+   protected Set<Endpoint> delegate() {
+      return endpoints;
+   }
 }
diff --git a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Tenant.java b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Tenant.java
index d397350..1410056 100644
--- a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Tenant.java
+++ b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Tenant.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -16,16 +16,16 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
 package org.jclouds.openstack.keystone.v2_0.domain;
 
-import static com.google.common.base.Objects.equal;
-import static com.google.common.base.Objects.toStringHelper;
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import java.beans.ConstructorProperties;
+
 import org.jclouds.javax.annotation.Nullable;
 
 import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
 
 /**
  * A container used to group or isolate resources and/or identity objects. Depending on the service
@@ -33,19 +33,21 @@
  *
  * @author Adrian Cole
  * @see <a href="http://docs.openstack.org/api/openstack-identity-service/2.0/content/Identity-Service-Concepts-e1362.html"
- *      />
+/>
  */
-public class Tenant implements Comparable<Tenant> {
+public class Tenant {
 
-   public static Builder builder() {
-      return new Builder();
+   public static Builder<?> builder() {
+      return new ConcreteBuilder();
    }
 
-   public Builder toBuilder() {
-      return builder().fromTenant(this);
+   public Builder<?> toBuilder() {
+      return new ConcreteBuilder().fromTenant(this);
    }
 
-   public static class Builder {
+   public static abstract class Builder<T extends Builder<T>>  {
+      protected abstract T self();
+
       protected String id;
       protected String name;
       protected String description;
@@ -53,47 +55,54 @@
       /**
        * @see Tenant#getId()
        */
-      public Builder id(String id) {
-         this.id = checkNotNull(id, "id");
-         return this;
+      public T id(String id) {
+         this.id = id;
+         return self();
       }
 
       /**
        * @see Tenant#getName()
        */
-      public Builder name(String name) {
-         this.name = checkNotNull(name, "name");
-         return this;
+      public T name(String name) {
+         this.name = name;
+         return self();
       }
 
       /**
        * @see Tenant#getDescription()
        */
-      public Builder description(String description) {
+      public T description(String description) {
          this.description = description;
-         return this;
+         return self();
       }
 
       public Tenant build() {
          return new Tenant(id, name, description);
       }
 
-      public Builder fromTenant(Tenant from) {
-         return id(from.getId()).name(from.getName());
+      public T fromTenant(Tenant in) {
+         return this
+               .id(in.getId())
+               .name(in.getName())
+               .description(in.getDescription());
       }
    }
-   
-   protected Tenant() {
-      // we want serializers like Gson to work w/o using sun.misc.Unsafe,
-      // prohibited in GAE. This also implies fields are not final.
-      // see http://code.google.com/p/jclouds/issues/detail?id=925
-   }
-   
-   protected String id;
-   protected String name;
-   protected String description;
 
-   protected Tenant(String id, String name, String description) {
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
+   }
+
+   private final String id;
+   private final String name;
+   private final String description;
+
+   @ConstructorProperties({
+         "id", "name", "description"
+   })
+   protected Tenant(String id, String name, @Nullable String description) {
       this.id = checkNotNull(id, "id");
       this.name = checkNotNull(name, "name");
       this.description = description;
@@ -105,14 +114,14 @@
     * @return the id of the tenant in the current OpenStack deployment
     */
    public String getId() {
-      return id;
+      return this.id;
    }
 
    /**
     * @return the name of the tenant
     */
    public String getName() {
-      return name;
+      return this.name;
    }
 
    /**
@@ -120,21 +129,7 @@
     */
    @Nullable
    public String getDescription() {
-      return description;
-   }
-
-   @Override
-   public boolean equals(Object object) {
-      if (this == object) {
-         return true;
-      }
-      if (object instanceof Tenant) {
-         final Tenant other = Tenant.class.cast(object);
-         return equal(id, other.id) && equal(name, other.name)
-               && equal(description, other.description);
-      } else {
-         return false;
-      }
+      return this.description;
    }
 
    @Override
@@ -143,17 +138,23 @@
    }
 
    @Override
-   public String toString() {
-      return toStringHelper("").add("id", id).add("name", name).add("description", description).toString();
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      Tenant that = Tenant.class.cast(obj);
+      return Objects.equal(this.id, that.id)
+            && Objects.equal(this.name, that.name)
+            && Objects.equal(this.description, that.description);
+   }
+
+   protected ToStringHelper string() {
+      return Objects.toStringHelper(this)
+            .add("id", id).add("name", name).add("description", description);
    }
 
    @Override
-   public int compareTo(Tenant that) {
-      if (that == null)
-         return 1;
-      if (this == that)
-         return 0;
-      return this.id.compareTo(that.id);
+   public String toString() {
+      return string().toString();
    }
 
 }
diff --git a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Token.java b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Token.java
index 171ed7d..233426e 100644
--- a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Token.java
+++ b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/Token.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
  * regarding copyright ownership.  jclouds licenses this file
- * to you under the Apache License, Expires 2.0 (the
+ * 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
  *
@@ -16,16 +16,15 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
 package org.jclouds.openstack.keystone.v2_0.domain;
 
-import static com.google.common.base.Objects.equal;
-import static com.google.common.base.Objects.toStringHelper;
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import java.beans.ConstructorProperties;
 import java.util.Date;
 
 import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
 
 /**
  * A token is an arbitrary bit of text that is used to access resources. Each token has a scope
@@ -35,22 +34,24 @@
  * While Keystone supports token-based authentication in this release, the intention is for it to
  * support additional protocols in the future. The intent is for it to be an integration service
  * foremost, and not a aspire to be a full-fledged identity store and management solution.
- * 
+ *
  * @author Adrian Cole
  * @see <a href="http://docs.openstack.org/api/openstack-identity-service/2.0/content/Identity-Service-Concepts-e1362.html"
- *      />
+/>
  */
 public class Token implements Comparable<Token> {
 
-   public static Builder builder() {
-      return new Builder();
+   public static Builder<?> builder() {
+      return new ConcreteBuilder();
    }
 
-   public Builder toBuilder() {
-      return builder().fromToken(this);
+   public Builder<?> toBuilder() {
+      return new ConcreteBuilder().fromToken(this);
    }
 
-   public static class Builder {
+   public static abstract class Builder<T extends Builder<T>>  {
+      protected abstract T self();
+
       protected String id;
       protected Date expires;
       protected Tenant tenant;
@@ -58,47 +59,54 @@
       /**
        * @see Token#getId()
        */
-      public Builder id(String id) {
-         this.id = checkNotNull(id, "id");
-         return this;
+      public T id(String id) {
+         this.id = id;
+         return self();
       }
 
       /**
        * @see Token#getExpires()
        */
-      public Builder expires(Date expires) {
-         this.expires = checkNotNull(expires, "expires");
-         return this;
+      public T expires(Date expires) {
+         this.expires = expires;
+         return self();
       }
 
       /**
        * @see Token#getTenant()
        */
-      public Builder tenant(Tenant tenant) {
-         this.tenant = checkNotNull(tenant, "tenant");
-         return this;
+      public T tenant(Tenant tenant) {
+         this.tenant = tenant;
+         return self();
       }
 
       public Token build() {
          return new Token(id, expires, tenant);
       }
 
-      public Builder fromToken(Token from) {
-         return id(from.getId()).expires(from.getExpires()).tenant(from.getTenant());
+      public T fromToken(Token in) {
+         return this
+               .id(in.getId())
+               .expires(in.getExpires())
+               .tenant(in.getTenant());
       }
    }
-   
-   protected Token() {
-      // we want serializers like Gson to work w/o using sun.misc.Unsafe,
-      // prohibited in GAE. This also implies fields are not final.
-      // see http://code.google.com/p/jclouds/issues/detail?id=925
-   }
-   
-   protected String id;
-   protected Date expires;
-   protected Tenant tenant;
 
-   public Token(String id, Date expires, Tenant tenant) {
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
+   }
+
+   private final String id;
+   private final Date expires;
+   private final Tenant tenant;
+
+   @ConstructorProperties({
+         "id", "expires", "tenant"
+   })
+   protected Token(String id, Date expires, Tenant tenant) {
       this.id = checkNotNull(id, "id");
       this.expires = checkNotNull(expires, "expires");
       this.tenant = checkNotNull(tenant, "tenant");
@@ -106,38 +114,25 @@
 
    /**
     * When providing an ID, it is assumed that the token exists in the current OpenStack deployment
-    * 
+    *
     * @return the id of the token in the current OpenStack deployment
     */
    public String getId() {
-      return id;
+      return this.id;
    }
 
    /**
     * @return the expires of the token
     */
    public Date getExpires() {
-      return expires;
+      return this.expires;
    }
 
    /**
     * @return the tenant assigned to the token
     */
    public Tenant getTenant() {
-      return tenant;
-   }
-
-   @Override
-   public boolean equals(Object object) {
-      if (this == object) {
-         return true;
-      }
-      if (object instanceof Token) {
-         final Token other = Token.class.cast(object);
-         return equal(id, other.id) && equal(expires, other.expires) && equal(tenant, other.tenant);
-      } else {
-         return false;
-      }
+      return this.tenant;
    }
 
    @Override
@@ -146,8 +141,23 @@
    }
 
    @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      Token that = Token.class.cast(obj);
+      return Objects.equal(this.id, that.id)
+            && Objects.equal(this.expires, that.expires)
+            && Objects.equal(this.tenant, that.tenant);
+   }
+
+   protected ToStringHelper string() {
+      return Objects.toStringHelper(this)
+            .add("id", id).add("expires", expires).add("tenant", tenant);
+   }
+
+   @Override
    public String toString() {
-      return toStringHelper("").add("id", id).add("expires", expires).add("tenant", tenant).toString();
+      return string().toString();
    }
 
    @Override
@@ -158,5 +168,4 @@
          return 0;
       return this.id.compareTo(that.id);
    }
-
 }
diff --git a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/User.java b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/User.java
index b6b19cd..82fbcd5 100644
--- a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/User.java
+++ b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/domain/User.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
  * regarding copyright ownership.  jclouds licenses this file
- * to you under the Apache License, Name 2.0 (the
+ * 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
  *
@@ -16,16 +16,17 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
 package org.jclouds.openstack.keystone.v2_0.domain;
 
-import static com.google.common.base.Objects.equal;
-import static com.google.common.base.Objects.toStringHelper;
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import java.beans.ConstructorProperties;
 import java.util.Set;
 
+import org.jclouds.javax.annotation.Nullable;
+
 import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
 import com.google.common.collect.ImmutableSet;
 
 /**
@@ -34,22 +35,24 @@
  * who claims to be making the call. Users have a login and may be assigned tokens to access users.
  * Users may be directly assigned to a particular tenant and behave as if they are contained in that
  * tenant.
- * 
+ *
  * @author Adrian Cole
  * @see <a href="http://docs.openstack.org/api/openstack-identity-service/2.0/content/Identity-Service-Concepts-e1362.html"
  *      />
  */
-public class User implements Comparable<User> {
+public class User {
 
-   public static Builder builder() {
-      return new Builder();
+   public static Builder<?> builder() {
+      return new ConcreteBuilder();
    }
 
-   public Builder toBuilder() {
-      return builder().fromUser(this);
+   public Builder<?> toBuilder() {
+      return new ConcreteBuilder().fromUser(this);
    }
 
-   public static class Builder {
+   public static abstract class Builder<T extends Builder<T>> {
+      protected abstract T self();
+
       protected String id;
       protected String name;
       protected Set<Role> roles = ImmutableSet.of();
@@ -57,93 +60,84 @@
       /**
        * @see User#getId()
        */
-      public Builder id(String id) {
-         this.id = checkNotNull(id, "id");
-         return this;
+      public T id(String id) {
+         this.id = id;
+         return self();
       }
 
       /**
        * @see User#getName()
        */
-      public Builder name(String name) {
-         this.name = checkNotNull(name, "name");
-         return this;
+      public T name(String name) {
+         this.name = name;
+         return self();
       }
 
       /**
        * @see User#getRoles()
        */
-      public Builder roles(Role... roles) {
-         return roles(ImmutableSet.copyOf(checkNotNull(roles, "roles")));
-      }
-
-      /**
-       * @see User#getRoles()
-       */
-      public Builder roles(Set<Role> roles) {
+      public T roles(Set<Role> roles) {
          this.roles = ImmutableSet.copyOf(checkNotNull(roles, "roles"));
-         return this;
+         return self();
+      }
+
+      public T roles(Role... in) {
+         return roles(ImmutableSet.copyOf(in));
       }
 
       public User build() {
          return new User(id, name, roles);
       }
 
-      public Builder fromUser(User from) {
-         return id(from.getId()).name(from.getName()).roles(from.getRoles());
+      public T fromUser(User in) {
+         return this
+               .id(in.getId())
+               .name(in.getName())
+               .roles(in.getRoles());
       }
    }
-   
-   protected User() {
-      // we want serializers like Gson to work w/o using sun.misc.Unsafe,
-      // prohibited in GAE. This also implies fields are not final.
-      // see http://code.google.com/p/jclouds/issues/detail?id=925
-   }
-   
-   protected String id;
-   protected String name;
-   protected Set<Role> roles = ImmutableSet.of();
 
-   protected User(String id, String name, Set<Role> roles) {
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
+   }
+
+   private final String id;
+   private final String name;
+   private final Set<Role> roles;
+
+   @ConstructorProperties({
+         "id", "name", "roles"
+   })
+   protected User(String id, String name, @Nullable Set<Role> roles) {
       this.id = checkNotNull(id, "id");
       this.name = checkNotNull(name, "name");
-      this.roles = ImmutableSet.copyOf(checkNotNull(roles, "roles"));
+      this.roles = roles == null ? ImmutableSet.<Role>of() : ImmutableSet.copyOf(checkNotNull(roles, "roles"));
    }
 
    /**
     * When providing an ID, it is assumed that the user exists in the current OpenStack deployment
-    * 
+    *
     * @return the id of the user in the current OpenStack deployment
     */
    public String getId() {
-      return id;
+      return this.id;
    }
 
    /**
     * @return the name of the user
     */
    public String getName() {
-      return name;
+      return this.name;
    }
 
    /**
     * @return the roles assigned to the user
     */
    public Set<Role> getRoles() {
-      return roles;
-   }
-
-   @Override
-   public boolean equals(Object object) {
-      if (this == object) {
-         return true;
-      }
-      if (object instanceof User) {
-         final User other = User.class.cast(object);
-         return equal(id, other.id) && equal(name, other.name) && equal(roles, other.roles);
-      } else {
-         return false;
-      }
+      return this.roles;
    }
 
    @Override
@@ -152,17 +146,23 @@
    }
 
    @Override
-   public String toString() {
-      return toStringHelper("").add("id", id).add("name", name).add("roles", roles).toString();
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      User that = User.class.cast(obj);
+      return Objects.equal(this.id, that.id)
+            && Objects.equal(this.name, that.name)
+            && Objects.equal(this.roles, that.roles);
+   }
+
+   protected ToStringHelper string() {
+      return Objects.toStringHelper(this)
+            .add("id", id).add("name", name).add("roles", roles);
    }
 
    @Override
-   public int compareTo(User that) {
-      if (that == null)
-         return 1;
-      if (this == that)
-         return 0;
-      return this.id.compareTo(that.id);
+   public String toString() {
+      return string().toString();
    }
 
 }
diff --git a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/suppliers/LocationIdToURIFromAccessForTypeAndVersion.java b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/suppliers/LocationIdToURIFromAccessForTypeAndVersion.java
index 4206cf6..0d5bcb0 100644
--- a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/suppliers/LocationIdToURIFromAccessForTypeAndVersion.java
+++ b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/suppliers/LocationIdToURIFromAccessForTypeAndVersion.java
@@ -21,6 +21,7 @@
 import java.net.URI;
 import java.util.Map;
 import java.util.NoSuchElementException;
+import java.util.Set;
 
 import javax.inject.Inject;
 import javax.inject.Singleton;
@@ -35,6 +36,7 @@
 import com.google.common.base.Supplier;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
 import com.google.inject.assistedinject.Assisted;
 
 @Singleton
@@ -59,32 +61,36 @@
    @Override
    public Map<String, Supplier<URI>> get() {
       Access accessResponse = access.get();
-      Service service = null;
-      try {
-         service = Iterables.find(accessResponse.getServiceCatalog(), new Predicate<Service>() {
+      Set<Service> services = Sets.filter(accessResponse.getServiceCatalog(), new Predicate<Service>() {
 
-            @Override
-            public boolean apply(Service input) {
-               return input.getType().equals(apiType);
+         @Override
+         public boolean apply(Service input) {
+            return input.getType().equals(apiType);
+         }
+
+      });
+      if (services.size() == 0)
+         throw new NoSuchElementException(String.format("apiType %s not found in catalog %s", apiType,
+                  accessResponse.getServiceCatalog()));
+
+      Iterable<Endpoint> endpoints = Iterables.filter(Iterables.concat(services), new Predicate<Endpoint>() {
+
+         @Override
+         public boolean apply(Endpoint input) {
+            if (input.getVersionId() == null) {
+               return true;
             }
+            return input.getVersionId().equals(apiVersion);
+         }
 
-         });
-      } catch (NoSuchElementException e) {
-         throw new NoSuchElementException(String.format("apiType %s not found in catalog %s", apiType, accessResponse
-                  .getServiceCatalog()));
-      }
-      Map<String, Endpoint> locationIdToEndpoint = Maps.uniqueIndex(Iterables.filter(service.getEndpoints(),
-               new Predicate<Endpoint>() {
+      });
 
-                  @Override
-                  public boolean apply(Endpoint input) {
-                     if (input.getVersionId() == null) {
-                         return true;
-                     }
-                     return input.getVersionId().equals(apiVersion);
-                  }
+      if (Iterables.size(endpoints) == 0)
+         throw new NoSuchElementException(String.format(
+                  "no endpoints for apiType %s are of version %s, or version agnostic: %s", apiType, apiVersion,
+                  services));
 
-               }), endpointToLocationId);
+      Map<String, Endpoint> locationIdToEndpoint = Maps.uniqueIndex(endpoints, endpointToLocationId);
       return Maps.transformValues(locationIdToEndpoint, endpointToSupplierURI);
    }
 
diff --git a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/v2_0/domain/Link.java b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/v2_0/domain/Link.java
index 521bd30..0b683fb 100644
--- a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/v2_0/domain/Link.java
+++ b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/v2_0/domain/Link.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
  * regarding copyright ownership.  jclouds licenses this file
- * to you under the Apache License, Href 2.0 (the
+ * 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
  *
@@ -16,27 +16,27 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
 package org.jclouds.openstack.v2_0.domain;
 
-import static com.google.common.base.Objects.equal;
-import static com.google.common.base.Objects.toStringHelper;
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import java.beans.ConstructorProperties;
 import java.net.URI;
 
+import javax.inject.Named;
+
 import org.jclouds.javax.annotation.Nullable;
 
 import com.google.common.base.Objects;
-import com.google.gson.annotations.SerializedName;
+import com.google.common.base.Objects.ToStringHelper;
 
 /**
  * For convenience, resources contain links to themselves. This allows a client to easily obtain a
  * resource URIs rather than to construct them.
- * 
+ *
  * @author AdrianCole
  * @see <a href= "http://docs.openstack.org/api/openstack-compute/1.1/content/LinksReferences.html"
- *      />
+/>
  */
 public class Link {
    /**
@@ -77,75 +77,83 @@
             return UNRECOGNIZED;
          }
       }
-
    }
 
    public static Link create(Relation relation, URI href) {
       return new Link(relation, null, href);
    }
-   
+
    public static Link create(Relation relation,String type, URI href) {
       return new Link(relation, type, href);
    }
-   
-   public static Builder builder() {
-      return new Builder();
+
+   public static Builder<?> builder() {
+      return new ConcreteBuilder();
    }
 
-   public Builder toBuilder() {
-      return builder().fromLink(this);
+   public Builder<?> toBuilder() {
+      return new ConcreteBuilder().fromLink(this);
    }
 
-   public static class Builder {
-      protected Relation relation;
+   public static abstract class Builder<T extends Builder<T>>  {
+      protected abstract T self();
+
+      protected Link.Relation relation;
       protected String type;
       protected URI href;
 
       /**
        * @see Link#getRelation()
        */
-      public Builder relation(Relation relation) {
-         this.relation = checkNotNull(relation, "relation");
-         return this;
+      public T relation(Link.Relation relation) {
+         this.relation = relation;
+         return self();
       }
 
       /**
        * @see Link#getType()
        */
-      public Builder type(String type) {
+      public T type(String type) {
          this.type = type;
-         return this;
+         return self();
       }
-      
+
       /**
        * @see Link#getHref()
        */
-      public Builder href(URI href) {
-         this.href = checkNotNull(href, "href");
-         return this;
+      public T href(URI href) {
+         this.href = href;
+         return self();
       }
 
-      public Link build(){
+      public Link build() {
          return new Link(relation, type, href);
       }
-      
-      public Builder fromLink(Link from) {
-         return relation(from.getRelation()).type(from.getType()).href(from.getHref());
+
+      public T fromLink(Link in) {
+         return this
+               .relation(in.getRelation())
+               .type(in.getType())
+               .href(in.getHref());
       }
    }
-  
-   protected Link() {
-      // we want serializers like Gson to work w/o using sun.misc.Unsafe,
-      // prohibited in GAE. This also implies fields are not final.
-      // see http://code.google.com/p/jclouds/issues/detail?id=925
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
    }
 
-   @SerializedName("rel")
-   protected Relation relation;
-   protected String type;
-   protected URI href;
+   @Named("rel")
+   private final Link.Relation relation;
+   private final String type;
+   private final URI href;
 
-   protected Link(Relation relation, @Nullable String type, URI href) {
+   @ConstructorProperties({
+         "rel", "type", "href"
+   })
+   protected Link(Link.Relation relation, @Nullable String type, URI href) {
       this.relation = checkNotNull(relation, "relation");
       this.type = type;
       this.href = checkNotNull(href, "href");
@@ -159,39 +167,26 @@
     * of the resource. For example, an OpenStack Compute image may have an alternate representation
     * in the OpenStack Image service. Note that the type attribute here is used to provide a hint as
     * to the type of representation to expect when following the link.
-    * 
+    *
     * @return the relation of the resource in the current OpenStack deployment
     */
-   public Relation getRelation() {
-      return relation;
+   public Link.Relation getRelation() {
+      return this.relation;
    }
-   
+
    /**
     * @return the type of the resource or null if not specified
     */
    @Nullable
    public String getType() {
-      return type;
+      return this.type;
    }
-   
+
    /**
     * @return the href of the resource
     */
    public URI getHref() {
-      return href;
-   }
-
-   @Override
-   public boolean equals(Object object) {
-      if (this == object) {
-         return true;
-      }
-      if (object instanceof Link) {
-         final Link other = Link.class.cast(object);
-         return equal(relation, other.relation) && equal(type, other.type) && equal(href, other.href);
-      } else {
-         return false;
-      }
+      return this.href;
    }
 
    @Override
@@ -200,8 +195,23 @@
    }
 
    @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      Link that = Link.class.cast(obj);
+      return Objects.equal(this.relation, that.relation)
+            && Objects.equal(this.type, that.type)
+            && Objects.equal(this.href, that.href);
+   }
+
+   protected ToStringHelper string() {
+      return Objects.toStringHelper(this)
+            .add("relation", relation).add("type", type).add("href", href);
+   }
+
+   @Override
    public String toString() {
-      return toStringHelper("").add("relation", relation).add("type", type).add("href", href).toString();
+      return string().toString();
    }
 
 }
diff --git a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/v2_0/domain/Resource.java b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/v2_0/domain/Resource.java
index 84dd35e..53a6d35 100644
--- a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/v2_0/domain/Resource.java
+++ b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/v2_0/domain/Resource.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
  * regarding copyright ownership.  jclouds licenses this file
- * to you under the Apache License, Name 2.0 (the
+ * 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
  *
@@ -16,12 +16,11 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
 package org.jclouds.openstack.v2_0.domain;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
-import java.util.Collections;
+import java.beans.ConstructorProperties;
 import java.util.Set;
 
 import org.jclouds.javax.annotation.Nullable;
@@ -32,11 +31,11 @@
 
 /**
  * Resource found in a paginated collection
- * 
+ *
  * @author AdrianCole
  * @see <a href=
- *      "http://docs.openstack.org/api/openstack-compute/1.1/content/Paginated_Collections-d1e664.html"
- *      />
+"http://docs.openstack.org/api/openstack-compute/1.1/content/Paginated_Collections-d1e664.html"
+/>
  */
 public class Resource implements Comparable<Resource> {
 
@@ -51,9 +50,9 @@
    public static abstract class Builder<T extends Builder<T>>  {
       protected abstract T self();
 
-      private String id;
-      private String name;
-      private Set<Link> links = ImmutableSet.of();
+      protected String id;
+      protected String name;
+      protected Set<Link> links = ImmutableSet.of();
 
       /**
        * @see Resource#getId()
@@ -74,28 +73,24 @@
       /**
        * @see Resource#getLinks()
        */
-      public T links(Link... links) {
-         return links(ImmutableSet.copyOf(checkNotNull(links, "links")));
-      }
-
-      /**
-       * @see Resource#getLinks()
-       */
       public T links(Set<Link> links) {
-         this.links = links;
+         this.links = ImmutableSet.copyOf(checkNotNull(links, "links"));
          return self();
       }
 
+      public T links(Link... in) {
+         return links(ImmutableSet.copyOf(in));
+      }
+
       public Resource build() {
-         return new Resource(this);
+         return new Resource(id, name, links);
       }
 
       public T fromResource(Resource in) {
          return this
                .id(in.getId())
                .name(in.getName())
-               .links(in.getLinks())
-               ;
+               .links(in.getLinks());
       }
    }
 
@@ -105,21 +100,18 @@
          return this;
       }
    }
-   
-   protected Resource() {
-      // we want serializers like Gson to work w/o using sun.misc.Unsafe,
-      // prohibited in GAE. This also implies fields are not final.
-      // see http://code.google.com/p/jclouds/issues/detail?id=925
-   }
 
-   private String id;
-   private String name;
-   private Set<Link> links = ImmutableSet.of();
+   private final String id;
+   private final String name;
+   private final Set<Link> links;
 
-   protected Resource(Builder<?> builder) {
-      this.id = checkNotNull(builder.id, "id");
-      this.name = builder.name;
-      this.links = ImmutableSet.copyOf(checkNotNull(builder.links, "links"));
+   @ConstructorProperties({
+         "id", "name", "links"
+   })
+   protected Resource(String id, @Nullable String name, @Nullable Set<Link> links) {
+      this.id = checkNotNull(id, "id");
+      this.name = name;
+      this.links = links == null ? ImmutableSet.<Link>of() : ImmutableSet.copyOf(checkNotNull(links, "links"));
    }
    
    /**
@@ -129,7 +121,7 @@
     * @return the id of the resource in the current OpenStack deployment
     */
    public String getId() {
-      return id;
+      return this.id;
    }
 
    /**
@@ -137,14 +129,14 @@
     */
    @Nullable
    public String getName() {
-      return name;
+      return this.name;
    }
 
    /**
     * @return the links of the id address allocated to the new server
     */
    public Set<Link> getLinks() {
-      return Collections.unmodifiableSet(this.links);
+      return this.links;
    }
 
    @Override
@@ -157,22 +149,21 @@
       if (this == obj) return true;
       if (obj == null || getClass() != obj.getClass()) return false;
       Resource that = Resource.class.cast(obj);
-      return Objects.equal(this.getId(), that.getId())
+      return Objects.equal(this.id, that.id)
             && Objects.equal(this.name, that.name)
             && Objects.equal(this.links, that.links);
    }
 
    protected ToStringHelper string() {
-      return Objects.toStringHelper("")
-            .add("id", getId())
-            .add("name", name)
-            .add("links", links);
+      return Objects.toStringHelper(this)
+            .add("id", id).add("name", name).add("links", links);
    }
 
    @Override
    public String toString() {
       return string().toString();
    }
+
    @Override
    public int compareTo(Resource that) {
       if (that == null)
diff --git a/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/suppliers/RegionIdToURIFromAccessForTypeAndVersionTest.java b/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/suppliers/RegionIdToURIFromAccessForTypeAndVersionTest.java
index 650fcd5..1b73e47 100644
--- a/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/suppliers/RegionIdToURIFromAccessForTypeAndVersionTest.java
+++ b/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/suppliers/RegionIdToURIFromAccessForTypeAndVersionTest.java
@@ -59,7 +59,6 @@
                   RegionIdToURISupplier.Factory.class));
       }
 
-      @SuppressWarnings("unused")
       @Provides
       @Singleton
       public Supplier<Access> provide() {
@@ -67,9 +66,13 @@
       }
    }).getInstance(RegionIdToURISupplier.Factory.class);
 
+   @Test(expectedExceptions = NoSuchElementException.class)
+   public void testRegionUnmatches() {
+      Maps.transformValues(factory.createForApiTypeAndVersion("compute", "1.0").get(),
+               Suppliers.<URI> supplierFunction());
+   }
+   
    public void testRegionMatches() {
-      assertEquals(Maps.transformValues(factory.createForApiTypeAndVersion("compute", "1.0").get(), Suppliers
-               .<URI> supplierFunction()), ImmutableMap.of());
       assertEquals(Maps.transformValues(factory.createForApiTypeAndVersion("compute", "1.1").get(), Suppliers
                .<URI> supplierFunction()), ImmutableMap.of("az-1.region-a.geo-1", URI
                .create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456"), "az-2.region-a.geo-1", URI
@@ -89,7 +92,6 @@
                   RegionIdToURISupplier.Factory.class));
       }
 
-      @SuppressWarnings("unused")
       @Provides
       @Singleton
       public Supplier<Access> provide() {
diff --git a/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/suppliers/ZoneIdToURIFromAccessForTypeAndVersionTest.java b/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/suppliers/ZoneIdToURIFromAccessForTypeAndVersionTest.java
index 3805196..8c7c2b3 100644
--- a/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/suppliers/ZoneIdToURIFromAccessForTypeAndVersionTest.java
+++ b/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v2_0/suppliers/ZoneIdToURIFromAccessForTypeAndVersionTest.java
@@ -58,7 +58,6 @@
                   ZoneIdToURIFromAccessForTypeAndVersion.class).build(ZoneIdToURISupplier.Factory.class));
       }
 
-      @SuppressWarnings("unused")
       @Provides
       @Singleton
       public Supplier<Access> provide() {
@@ -66,9 +65,14 @@
       }
    }).getInstance(ZoneIdToURISupplier.Factory.class);
 
+
+   @Test(expectedExceptions = NoSuchElementException.class)
+   public void testZoneUnmatches() {
+      Maps.transformValues(factory.createForApiTypeAndVersion("compute", "1.0").get(),
+               Suppliers.<URI> supplierFunction());
+   }
+  
    public void testZoneMatches() {
-      assertEquals(Maps.transformValues(factory.createForApiTypeAndVersion("compute", "1.0").get(), Suppliers
-            .<URI> supplierFunction()), ImmutableMap.of());
       assertEquals(Maps.transformValues(factory.createForApiTypeAndVersion("compute", "1.1").get(), Suppliers
             .<URI> supplierFunction()), ImmutableMap.of("az-1.region-a.geo-1", URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456"),
                                                         "az-2.region-a.geo-1", URI.create("https://az-2.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456"),
@@ -86,7 +90,6 @@
                   ZoneIdToURIFromAccessForTypeAndVersion.class).build(ZoneIdToURISupplier.Factory.class));
       }
 
-      @SuppressWarnings("unused")
       @Provides
       @Singleton
       public Supplier<Access> provide() {
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/NovaApiMetadata.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/NovaApiMetadata.java
index 74ff180..385cbcd 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/NovaApiMetadata.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/NovaApiMetadata.java
@@ -31,7 +31,8 @@
 import org.jclouds.apis.ApiMetadata;
 import org.jclouds.compute.ComputeServiceContext;
 import org.jclouds.openstack.keystone.v2_0.config.CredentialTypes;
-import org.jclouds.openstack.keystone.v2_0.config.KeystoneAuthenticationModule.KeystoneAuthenticationModuleForZones;
+import org.jclouds.openstack.keystone.v2_0.config.KeystoneAuthenticationModule;
+import org.jclouds.openstack.keystone.v2_0.config.KeystoneAuthenticationModule.ZoneModule;
 import org.jclouds.openstack.nova.v2_0.compute.config.NovaComputeServiceContextModule;
 import org.jclouds.openstack.nova.v2_0.config.NovaRestClientModule;
 import org.jclouds.openstack.v2_0.ServiceType;
@@ -100,7 +101,11 @@
          .defaultEndpoint("http://localhost:5000")
          .defaultProperties(NovaApiMetadata.defaultProperties())
          .view(TypeToken.of(ComputeServiceContext.class))
-         .defaultModules(ImmutableSet.<Class<? extends Module>>of(KeystoneAuthenticationModuleForZones.class, NovaRestClientModule.class, NovaComputeServiceContextModule.class));
+         .defaultModules(ImmutableSet.<Class<? extends Module>>builder()
+                                     .add(KeystoneAuthenticationModule.class)
+                                     .add(ZoneModule.class)
+                                     .add(NovaRestClientModule.class)
+                                     .add(NovaComputeServiceContextModule.class).build());
       }
       
       @Override
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/NovaComputeServiceAdapter.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/NovaComputeServiceAdapter.java
index f24965e..21844e1 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/NovaComputeServiceAdapter.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/NovaComputeServiceAdapter.java
@@ -19,6 +19,7 @@
 package org.jclouds.openstack.nova.v2_0.compute;
 
 import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
 import static com.google.common.collect.Iterables.filter;
 import static com.google.common.collect.Iterables.transform;
 import static org.jclouds.compute.util.ComputeServiceUtils.metadataAndTagsAsCommaDelimitedValue;
@@ -55,6 +56,7 @@
 import org.jclouds.openstack.nova.v2_0.predicates.ImagePredicates;
 
 import com.google.common.base.Function;
+import com.google.common.base.Objects;
 import com.google.common.base.Optional;
 import com.google.common.base.Supplier;
 import com.google.common.cache.LoadingCache;
@@ -154,9 +156,29 @@
    @Override
    public Iterable<ImageInZone> listImages() {
       Builder<ImageInZone> builder = ImmutableSet.builder();
-      for (final String zoneId : zoneIds.get()) {
-         builder.addAll(transform(filter(novaClient.getImageClientForZone(zoneId).listImagesInDetail(), ImagePredicates
-                  .statusEquals(Image.Status.ACTIVE)), new Function<Image, ImageInZone>() {
+      Set<String> zones = zoneIds.get();
+      checkState(zones.size() > 0, "no zones found in supplier %s", zoneIds);
+      for (final String zoneId : zones) {
+         Set<Image> images = novaClient.getImageClientForZone(zoneId).listImagesInDetail();
+         if (images.size() == 0) {
+            logger.debug("no images found in zone %s", zoneId);
+            continue;
+         }
+         Iterable<Image> active = filter(images, ImagePredicates.statusEquals(Image.Status.ACTIVE));
+         if (images.size() == 0) {
+            logger.debug("no images with status active in zone %s; non-active: %s", zoneId,
+                     transform(active, new Function<Image, String>() {
+
+                        @Override
+                        public String apply(Image input) {
+                           return Objects.toStringHelper("").add("id", input.getId()).add("status", input.getStatus())
+                                    .toString();
+                        }
+
+                     }));
+            continue;
+         }
+         builder.addAll(transform(active, new Function<Image, ImageInZone>() {
 
             @Override
             public ImageInZone apply(Image arg0) {
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/config/NovaComputeServiceContextModule.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/config/NovaComputeServiceContextModule.java
index c3c2764..7217db0 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/config/NovaComputeServiceContextModule.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/config/NovaComputeServiceContextModule.java
@@ -72,13 +72,13 @@
 import org.jclouds.openstack.nova.v2_0.predicates.FindSecurityGroupWithNameAndReturnTrue;
 import org.jclouds.predicates.PredicateWithResult;
 import org.jclouds.predicates.RetryablePredicate;
+import org.jclouds.util.Suppliers2;
 
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Function;
 import com.google.common.base.Optional;
 import com.google.common.base.Predicate;
 import com.google.common.base.Supplier;
-import com.google.common.base.Suppliers;
 import com.google.common.cache.CacheBuilder;
 import com.google.common.cache.CacheLoader;
 import com.google.common.cache.LoadingCache;
@@ -124,7 +124,7 @@
 
       // we aren't converting location from a provider-specific type
       bind(new TypeLiteral<Function<Location, Location>>() {
-      }).to((Class) IdentityFunction.class);
+      }).to(Class.class.cast(IdentityFunction.class));
 
       bind(TemplateOptions.class).to(NovaTemplateOptions.class);
 
@@ -200,7 +200,7 @@
    @Singleton
    protected Supplier<Map<String, Location>> createLocationIndexedById(
             @Memoized Supplier<Set<? extends Location>> locations) {
-      return Suppliers.compose(new Function<Set<? extends Location>, Map<String, Location>>() {
+      return Suppliers2.compose(new Function<Set<? extends Location>, Map<String, Location>>() {
 
          @SuppressWarnings("unchecked")
          @Override
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/ImageInZoneToImage.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/ImageInZoneToImage.java
index 76eb8ec..3e3423f 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/ImageInZoneToImage.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/ImageInZoneToImage.java
@@ -33,6 +33,7 @@
 import org.jclouds.openstack.nova.v2_0.domain.zonescoped.ImageInZone;
 
 import com.google.common.base.Function;
+import com.google.common.base.Objects;
 import com.google.common.base.Supplier;
 
 /**
@@ -63,4 +64,9 @@
                .userMetadata(image.getMetadata()).operatingSystem(imageToOs.apply(image)).description(image.getName())
                .location(location).status(toPortableImageStatus.get(image.getStatus())).build();
    }
+   
+   @Override
+   public String toString() {
+      return Objects.toStringHelper(this).toString();
+   }
 }
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/config/NovaParserModule.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/config/NovaParserModule.java
index f713262..ba96190 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/config/NovaParserModule.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/config/NovaParserModule.java
@@ -18,22 +18,29 @@
  */
 package org.jclouds.openstack.nova.v2_0.config;
 
+import java.beans.ConstructorProperties;
 import java.lang.reflect.Type;
+import java.util.Date;
 import java.util.Map;
 import java.util.Set;
 
 import javax.inject.Singleton;
 
+import org.jclouds.javax.annotation.Nullable;
 import org.jclouds.json.config.GsonModule;
 import org.jclouds.json.config.GsonModule.DateAdapter;
+import org.jclouds.openstack.nova.v2_0.domain.Address;
 import org.jclouds.openstack.nova.v2_0.domain.HostResourceUsage;
 import org.jclouds.openstack.nova.v2_0.domain.Server;
 import org.jclouds.openstack.nova.v2_0.domain.ServerExtendedAttributes;
 import org.jclouds.openstack.nova.v2_0.domain.ServerExtendedStatus;
 import org.jclouds.openstack.nova.v2_0.domain.ServerWithSecurityGroups;
+import org.jclouds.openstack.v2_0.domain.Link;
+import org.jclouds.openstack.v2_0.domain.Resource;
 
 import com.google.common.base.Objects;
 import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Multimap;
 import com.google.common.collect.Sets;
 import com.google.gson.JsonArray;
 import com.google.gson.JsonDeserializationContext;
@@ -71,7 +78,7 @@
       public HostResourceUsage apply(HostResourceUsageView in) {
          return in.resource.toBuilder().build();
       }
-      
+
       @Override
       public HostResourceUsage deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext context) throws JsonParseException {
          return apply((HostResourceUsageView) context.deserialize(jsonElement, HostResourceUsageView.class));
@@ -81,13 +88,18 @@
       public JsonElement serialize(HostResourceUsage hostResourceUsage, Type type, JsonSerializationContext context) {
          return context.serialize(hostResourceUsage);
       }
-      
+
       private static class HostResourceUsageView {
          protected HostResourceUsageInternal resource;
       }
+
       private static class HostResourceUsageInternal extends HostResourceUsage {
-         protected HostResourceUsageInternal(Builder<?> builder) {
-            super(builder);
+
+         @ConstructorProperties({
+               "host", "project", "memory_mb", "cpu", "disk_gb"
+         })
+         protected HostResourceUsageInternal(String host, @Nullable String project, int memoryMb, int cpu, int diskGb) {
+            super(host, project, memoryMb, cpu, diskGb);
          }
       }
    }
@@ -124,7 +136,7 @@
          }
          ServerExtendedAttributes extraAttributes = context.deserialize(jsonElement, ServerExtendedAttributes.class);
          if (!Objects.equal(extraAttributes, ServerExtendedAttributes.builder().build())) {
-            result.extraAttributes(extraAttributes);
+            result.extendedAttributes(extraAttributes);
          }
          return result.build();
       }
@@ -134,9 +146,16 @@
       }
 
       private static class ServerInternal extends Server {
-         protected ServerInternal() {
+         @ConstructorProperties({
+               "id", "name", "links", "uuid", "tenant_id", "user_id", "updated", "created", "hostId", "accessIPv4", "accessIPv6", "status", "image", "flavor", "key_name", "config_drive", "addresses", "metadata", "extendedStatus", "extendedAttributes", "OS-DCF:diskConfig"
+         })
+         protected ServerInternal(String id, @Nullable String name, java.util.Set<Link> links, @Nullable String uuid, String tenantId,
+                                  String userId, Date updated, Date created, @Nullable String hostId, @Nullable String accessIPv4,
+                                  @Nullable String accessIPv6, Server.Status status, Resource image, Resource flavor, @Nullable String keyName,
+                                  @Nullable String configDrive, Multimap<String, Address> addresses, Map<String, String> metadata,
+                                  @Nullable ServerExtendedStatus extendedStatus, @Nullable ServerExtendedAttributes extendedAttributes, @Nullable String diskConfig) {
+            super(id, name, links, uuid, tenantId, userId, updated, created, hostId, accessIPv4, accessIPv6, status, image, flavor, keyName, configDrive, addresses, metadata, extendedStatus, extendedAttributes, diskConfig);
          }
       }
    }
-
 }
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/config/NovaRestClientModule.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/config/NovaRestClientModule.java
index 5fd5076..52e84ea 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/config/NovaRestClientModule.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/config/NovaRestClientModule.java
@@ -29,8 +29,6 @@
 import org.jclouds.http.annotation.ClientError;
 import org.jclouds.http.annotation.Redirection;
 import org.jclouds.http.annotation.ServerError;
-import org.jclouds.location.suppliers.ImplicitLocationSupplier;
-import org.jclouds.location.suppliers.implicit.OnlyLocationOrFirstZone;
 import org.jclouds.openstack.nova.v2_0.NovaAsyncClient;
 import org.jclouds.openstack.nova.v2_0.NovaClient;
 import org.jclouds.openstack.nova.v2_0.domain.Extension;
@@ -81,7 +79,6 @@
 import com.google.common.cache.LoadingCache;
 import com.google.common.collect.ImmutableMap;
 import com.google.inject.Provides;
-import com.google.inject.Scopes;
 
 /**
  * Configures the Nova connection.
@@ -122,12 +119,6 @@
       super.configure();
    }
 
-   @Override
-   protected void installLocations() {
-      super.installLocations();
-      bind(ImplicitLocationSupplier.class).to(OnlyLocationOrFirstZone.class).in(Scopes.SINGLETON);
-   }
-
    @Provides
    @Singleton
    public LoadingCache<String, Set<Extension>> provideExtensionsByZone(final Provider<NovaClient> novaClient) {
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/Address.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/Address.java
index f906e16..33a172e 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/Address.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/Address.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -16,27 +16,28 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
 package org.jclouds.openstack.nova.v2_0.domain;
 
-import static com.google.common.base.Objects.equal;
-import static com.google.common.base.Objects.toStringHelper;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.beans.ConstructorProperties;
 
 import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
 
 /**
  * IP address
  * 
  * @author AdrianCole
- */
+*/
 public class Address {
-  
-   public static Builder builder() {
-      return new Builder();
-   }
 
-   public Builder toBuilder() {
-      return builder().fromAddress(this);
+   public static Builder<?> builder() { 
+      return new ConcreteBuilder();
+   }
+   
+   public Builder<?> toBuilder() { 
+      return new ConcreteBuilder().fromAddress(this);
    }
 
    public static Address createV4(String addr) {
@@ -47,46 +48,54 @@
       return builder().version(6).addr(addr).build();
    }
 
-   public static class Builder {
+   public static abstract class Builder<T extends Builder<T>>  {
+      protected abstract T self();
+
       protected String addr;
       protected int version;
-
-      /**
-       * @see Address#getVersion()
-       */
-      protected Builder version(int version) {
-         this.version = version;
-         return this;
-      }
-
-      /**
+   
+      /** 
        * @see Address#getAddr()
        */
-      public Builder addr(String addr) {
+      public T addr(String addr) {
          this.addr = addr;
-         return this;
+         return self();
+      }
+
+      /** 
+       * @see Address#getVersion()
+       */
+      public T version(int version) {
+         this.version = version;
+         return self();
       }
 
       public Address build() {
          return new Address(addr, version);
       }
-
-      public Builder fromAddress(Address from) {
-         return addr(from.getAddr()).version(from.getVersion());
+      
+      public T fromAddress(Address in) {
+         return this
+                  .addr(in.getAddr())
+                  .version(in.getVersion());
       }
    }
-   
-   protected Address() {
-      // we want serializers like Gson to work w/o using sun.misc.Unsafe,
-      // prohibited in GAE. This also implies fields are not final.
-      // see http://code.google.com/p/jclouds/issues/detail?id=925
-   }
-   
-   protected String addr;
-   protected int version;
 
-   public Address(String addr, int version) {
-      this.addr = addr;
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
+   }
+
+   private final String addr;
+   private final int version;
+
+   @ConstructorProperties({
+      "addr", "version"
+   })
+   protected Address(String addr, int version) {
+      this.addr = checkNotNull(addr, "addr");
       this.version = version;
    }
 
@@ -94,27 +103,14 @@
     * @return the ip address
     */
    public String getAddr() {
-      return addr;
+      return this.addr;
    }
 
    /**
     * @return the IP version, ex. 4
     */
    public int getVersion() {
-      return version;
-   }
-
-   @Override
-   public boolean equals(Object object) {
-      if (this == object) {
-         return true;
-      }
-      if (object instanceof Address) {
-         final Address other = Address.class.cast(object);
-         return equal(addr, other.addr) && equal(version, other.version);
-      } else {
-         return false;
-      }
+      return this.version;
    }
 
    @Override
@@ -123,8 +119,22 @@
    }
 
    @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      Address that = Address.class.cast(obj);
+      return Objects.equal(this.addr, that.addr)
+               && Objects.equal(this.version, that.version);
+   }
+   
+   protected ToStringHelper string() {
+      return Objects.toStringHelper(this)
+            .add("addr", addr).add("version", version);
+   }
+   
+   @Override
    public String toString() {
-      return toStringHelper("").add("addr", addr).add("version", version).toString();
+      return string().toString();
    }
 
 }
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/Extension.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/Extension.java
index a762957..e96d583 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/Extension.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/Extension.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
  * regarding copyright ownership.  jclouds licenses this file
- * to you under the Apache License, Name 2.0 (the
+ * 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
  *
@@ -18,12 +18,19 @@
  */
 package org.jclouds.openstack.nova.v2_0.domain;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.beans.ConstructorProperties;
 import java.net.URI;
 import java.util.Date;
+import java.util.Set;
 
+import org.jclouds.javax.annotation.Nullable;
+import org.jclouds.openstack.v2_0.domain.Link;
 import org.jclouds.openstack.v2_0.domain.Resource;
 
 import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
 
 /**
  * The OpenStack Compute API is extensible. Extensions serve two purposes: They
@@ -33,25 +40,26 @@
  * 
  * @author Adrian Cole
  * @see <a href=
- *      "http://docs.openstack.org/api/openstack-compute/2/content/Extensions-d1e1444.html"
- *      />
- */
+      "http://docs.openstack.org/api/openstack-compute/2/content/Extensions-d1e1444.html"
+      />
+*/
 public class Extension extends Resource {
-   public static Builder<?> builder() {
+
+   public static Builder<?> builder() { 
       return new ConcreteBuilder();
    }
-
-   public Builder<?> toBuilder() {
+   
+   public Builder<?> toBuilder() { 
       return new ConcreteBuilder().fromExtension(this);
    }
 
    public static abstract class Builder<T extends Builder<T>> extends Resource.Builder<T>  {
-      private URI namespace;
-      private String alias;
-      private Date updated;
-      private String description;
-
-      /**
+      protected URI namespace;
+      protected String alias;
+      protected Date updated;
+      protected String description;
+   
+      /** 
        * @see Extension#getNamespace()
        */
       public T namespace(URI namespace) {
@@ -59,16 +67,23 @@
          return self();
       }
 
-      /**
+      /** 
        * @see Extension#getAlias()
        */
       public T alias(String alias) {
-         id(alias);
          this.alias = alias;
          return self();
       }
 
       /**
+       * @see Extension#getAlias()
+       */
+      @Override
+      public T id(String id) {
+         return alias(id);
+      }
+
+      /** 
        * @see Extension#getUpdated()
        */
       public T updated(Date updated) {
@@ -76,7 +91,7 @@
          return self();
       }
 
-      /**
+      /** 
        * @see Extension#getDescription()
        */
       public T description(String description) {
@@ -85,18 +100,16 @@
       }
 
       public Extension build() {
-         return new Extension(this);
+         return new Extension(name, links, namespace, alias, updated, description);
       }
-
+      
       public T fromExtension(Extension in) {
          return super.fromResource(in)
-               .namespace(in.getNamespace())
-               .alias(in.getAlias())
-               .updated(in.getUpdated())
-               .description(in.getDescription())
-               ;
+                  .namespace(in.getNamespace())
+                  .alias(in.getAlias())
+                  .updated(in.getUpdated())
+                  .description(in.getDescription());
       }
-
    }
 
    private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
@@ -105,39 +118,32 @@
          return this;
       }
    }
-   
-   protected Extension() {
-      // we want serializers like Gson to work w/o using sun.misc.Unsafe,
-      // prohibited in GAE. This also implies fields are not final.
-      // see http://code.google.com/p/jclouds/issues/detail?id=925
-   }
-   
-   private URI namespace;
-   private String alias;
-   private Date updated;
-   private String description;
 
-   protected Extension(Builder<?> builder) {
-      super(builder);
-      this.namespace = builder.namespace;
-      this.alias = builder.alias;
-      this.updated = builder.updated;
-      this.description = builder.description;
+   private final URI namespace;
+   private final String alias;
+   private final Date updated;
+   private final String description;
+
+   @ConstructorProperties({
+      "name", "links", "namespace", "alias", "updated", "description"
+   })
+   protected Extension(@Nullable String name, Set<Link> links, URI namespace, String alias, @Nullable Date updated, String description) {
+      super(alias, name, links);
+      this.namespace = checkNotNull(namespace, "namespace");
+      this.alias = checkNotNull(alias, "alias");
+      this.updated = updated;
+      this.description = checkNotNull(description, "description");
    }
 
    public URI getNamespace() {
       return this.namespace;
    }
 
-   @Override
-   public String getId() {
-      return this.alias;
-   }
-
    public String getAlias() {
       return this.alias;
    }
 
+   @Nullable
    public Date getUpdated() {
       return this.updated;
    }
@@ -147,11 +153,24 @@
    }
 
    @Override
-   public Objects.ToStringHelper string() {
-      return super.string()
-            .add("namespace", namespace)
-            .add("alias", alias)
-            .add("updated", updated)
-            .add("description", description);
+   public int hashCode() {
+      return Objects.hashCode(namespace, alias, updated, description);
    }
+
+   @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      Extension that = Extension.class.cast(obj);
+      return super.equals(that) && Objects.equal(this.namespace, that.namespace)
+               && Objects.equal(this.alias, that.alias)
+               && Objects.equal(this.updated, that.updated)
+               && Objects.equal(this.description, that.description);
+   }
+   
+   protected ToStringHelper string() {
+      return super.string()
+            .add("namespace", namespace).add("alias", alias).add("updated", updated).add("description", description);
+   }
+   
 }
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/Flavor.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/Flavor.java
index e65f9d1..11c90cd 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/Flavor.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/Flavor.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -18,40 +18,46 @@
  */
 package org.jclouds.openstack.nova.v2_0.domain;
 
+import java.beans.ConstructorProperties;
+
+import javax.inject.Named;
+
+import org.jclouds.javax.annotation.Nullable;
+import org.jclouds.openstack.v2_0.domain.Link;
 import org.jclouds.openstack.v2_0.domain.Resource;
 
 import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
 import com.google.common.base.Optional;
-import com.google.gson.annotations.SerializedName;
 
 /**
  * A flavor is an available hardware configuration for a server. Each flavor has
  * a unique combination of disk space and memory capacity.
- *
+ * 
  * @author Jeremy Daggett
  * @see <a href=
- *      "http://docs.openstack.org/api/openstack-compute/1.1/content/Flavors-d1e4180.html"
- *      />
- */
+      "http://docs.openstack.org/api/openstack-compute/1.1/content/Flavors-d1e4180.html"
+      />
+*/
 public class Flavor extends Resource {
-   public static Builder<?> builder() {
+
+   public static Builder<?> builder() { 
       return new ConcreteBuilder();
    }
-
-   public Builder<?> toBuilder() {
+   
+   public Builder<?> toBuilder() { 
       return new ConcreteBuilder().fromFlavor(this);
    }
 
    public static abstract class Builder<T extends Builder<T>> extends Resource.Builder<T>  {
-      private int ram;
-      private int disk;
-      private int vcpus;
-      private String swap;
-      private Double rxtxFactor;
-      private Integer ephemeral;
-
-
-      /**
+      protected int ram;
+      protected int disk;
+      protected int vcpus;
+      protected String swap;
+      protected Double rxtxFactor;
+      protected Integer ephemeral;
+   
+      /** 
        * @see Flavor#getRam()
        */
       public T ram(int ram) {
@@ -59,7 +65,7 @@
          return self();
       }
 
-      /**
+      /** 
        * @see Flavor#getDisk()
        */
       public T disk(int disk) {
@@ -67,7 +73,7 @@
          return self();
       }
 
-      /**
+      /** 
        * @see Flavor#getVcpus()
        */
       public T vcpus(int vcpus) {
@@ -75,45 +81,43 @@
          return self();
       }
 
-      /**
-       * @see Flavor#getVcpus()
+      /** 
+       * @see Flavor#getSwap()
        */
       public T swap(String swap) {
          this.swap = swap;
          return self();
       }
 
-      /**
-       * @see Flavor#getVcpus()
+      /** 
+       * @see Flavor#getRxtxFactor()
        */
       public T rxtxFactor(Double rxtxFactor) {
          this.rxtxFactor = rxtxFactor;
          return self();
       }
 
-      /**
-       * @see Flavor#getVcpus()
+      /** 
+       * @see Flavor#getEphemeral()
        */
       public T ephemeral(Integer ephemeral) {
          this.ephemeral = ephemeral;
          return self();
       }
 
-
       public Flavor build() {
-         return new Flavor(this);
+         return new Flavor(id, name, links, ram, disk, vcpus, swap, rxtxFactor, ephemeral);
       }
-
+      
       public T fromFlavor(Flavor in) {
          return super.fromResource(in)
-               .ram(in.getRam())
-               .disk(in.getDisk())
-               .vcpus(in.getVcpus())
-               .swap(in.getSwap().orNull())
-               .rxtxFactor(in.getRxtxFactor().orNull())
-               .ephemeral(in.getEphemeral().orNull());
+                  .ram(in.getRam())
+                  .disk(in.getDisk())
+                  .vcpus(in.getVcpus())
+                  .swap(in.getSwap().orNull())
+                  .rxtxFactor(in.getRxtxFactor().orNull())
+                  .ephemeral(in.getEphemeral().orNull());
       }
-
    }
 
    private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
@@ -122,30 +126,28 @@
          return this;
       }
    }
-   
-   protected Flavor() {
-      // we want serializers like Gson to work w/o using sun.misc.Unsafe,
-      // prohibited in GAE. This also implies fields are not final.
-      // see http://code.google.com/p/jclouds/issues/detail?id=925
-   }
-   
-   private int ram;
-   private int disk;
-   private int vcpus;
-   private Optional<String> swap = Optional.absent();
-   @SerializedName("rxtx_factor")
-   private Optional<Double> rxtxFactor = Optional.absent();
-   @SerializedName("OS-FLV-EXT-DATA:ephemeral")
-   private Optional<Integer> ephemeral = Optional.absent();
 
-   protected Flavor(Builder<?> builder) {
-      super(builder);
-      this.ram = builder.ram;
-      this.disk = builder.disk;
-      this.vcpus = builder.vcpus;
-      this.swap = Optional.fromNullable(builder.swap);
-      this.rxtxFactor = Optional.fromNullable(builder.rxtxFactor);
-      this.ephemeral = Optional.fromNullable(builder.ephemeral);
+   private final int ram;
+   private final int disk;
+   private final int vcpus;
+   private final Optional<String> swap;
+   @Named("rxtx_factor")
+   private final Optional<Double> rxtxFactor;
+   @Named("OS-FLV-EXT-DATA:ephemeral")
+   private final Optional<Integer> ephemeral;
+
+   @ConstructorProperties({
+      "id", "name", "links", "ram", "disk", "vcpus", "swap", "rxtx_factor", "OS-FLV-EXT-DATA:ephemeral"
+   })
+   protected Flavor(String id, @Nullable String name, java.util.Set<Link> links, int ram, int disk, int vcpus,
+                    @Nullable String swap, @Nullable Double rxtxFactor, @Nullable Integer ephemeral) {
+      super(id, name, links);
+      this.ram = ram;
+      this.disk = disk;
+      this.vcpus = vcpus;
+      this.swap = Optional.fromNullable(swap);
+      this.rxtxFactor = Optional.fromNullable(rxtxFactor);
+      this.ephemeral = Optional.fromNullable(ephemeral);
    }
 
    public int getRam() {
@@ -161,33 +163,46 @@
    }
 
    public Optional<String> getSwap() {
-      return swap;
+      return this.swap;
    }
 
    public Optional<Double> getRxtxFactor() {
-      return rxtxFactor;
+      return this.rxtxFactor;
    }
 
    /**
     * Retrieves ephemeral disk space in GB
     * <p/>
     * NOTE: This field is only present if the Flavor Extra Data extension is installed (alias "OS-FLV-EXT-DATA").
-    *
+    * 
     * @see org.jclouds.openstack.nova.v2_0.features.ExtensionClient#getExtensionByAlias
     * @see org.jclouds.openstack.nova.v2_0.extensions.ExtensionNamespaces#FLAVOR_EXTRA_DATA
     */
    public Optional<Integer> getEphemeral() {
-      return ephemeral;
+      return this.ephemeral;
    }
 
    @Override
-   protected Objects.ToStringHelper string() {
-      return super.string()
-            .add("ram", ram)
-            .add("disk", disk)
-            .add("vcpus", vcpus)
-            .add("swap", swap)
-            .add("rxtxFactor", rxtxFactor)
-            .add("ephemeral", ephemeral);
+   public int hashCode() {
+      return Objects.hashCode(ram, disk, vcpus, swap, rxtxFactor, ephemeral);
    }
-}
\ No newline at end of file
+
+   @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      Flavor that = Flavor.class.cast(obj);
+      return super.equals(that) && Objects.equal(this.ram, that.ram)
+               && Objects.equal(this.disk, that.disk)
+               && Objects.equal(this.vcpus, that.vcpus)
+               && Objects.equal(this.swap, that.swap)
+               && Objects.equal(this.rxtxFactor, that.rxtxFactor)
+               && Objects.equal(this.ephemeral, that.ephemeral);
+   }
+   
+   protected ToStringHelper string() {
+      return super.string()
+            .add("ram", ram).add("disk", disk).add("vcpus", vcpus).add("swap", swap).add("rxtxFactor", rxtxFactor).add("ephemeral", ephemeral);
+   }
+   
+}
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/FloatingIP.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/FloatingIP.java
index d3764be..b6f875c 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/FloatingIP.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/FloatingIP.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -18,11 +18,16 @@
  */
 package org.jclouds.openstack.nova.v2_0.domain;
 
-import static com.google.common.base.Objects.toStringHelper;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.beans.ConstructorProperties;
+
+import javax.inject.Named;
 
 import org.jclouds.javax.annotation.Nullable;
 
-import com.google.gson.annotations.SerializedName;
+import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
 
 /**
  * A Floating IP is an IP address that can be created and associated with a
@@ -31,68 +36,90 @@
  * 
  * @author Jeremy Daggett
  * @author chamerling
- */
+*/
 public class FloatingIP implements Comparable<FloatingIP> {
-   public static Builder builder() {
-      return new Builder();
+
+   public static Builder<?> builder() { 
+      return new ConcreteBuilder();
+   }
+   
+   public Builder<?> toBuilder() { 
+      return new ConcreteBuilder().fromFloatingIP(this);
    }
 
-   public Builder toBuilder() {
-      return builder().fromFloatingIp(this);
-   }
+   public static abstract class Builder<T extends Builder<T>>  {
+      protected abstract T self();
 
-   public static class Builder {
-      private String id;
-      private String ip;
-      private String fixedIp;
-      private String instanceId;
-
-      public Builder id(String id) {
+      protected String id;
+      protected String ip;
+      protected String fixedIp;
+      protected String instanceId;
+   
+      /** 
+       * @see FloatingIP#getId()
+       */
+      public T id(String id) {
          this.id = id;
-         return this;
+         return self();
       }
 
-      public Builder ip(String ip) {
+      /** 
+       * @see FloatingIP#getIp()
+       */
+      public T ip(String ip) {
          this.ip = ip;
-         return this;
+         return self();
       }
 
-      public Builder fixedIp(String fixedIp) {
+      /** 
+       * @see FloatingIP#getFixedIp()
+       */
+      public T fixedIp(String fixedIp) {
          this.fixedIp = fixedIp;
-         return this;
+         return self();
       }
 
-      public Builder instanceId(String instanceId) {
+      /** 
+       * @see FloatingIP#getInstanceId()
+       */
+      public T instanceId(String instanceId) {
          this.instanceId = instanceId;
-         return this;
+         return self();
       }
 
       public FloatingIP build() {
          return new FloatingIP(id, ip, fixedIp, instanceId);
       }
-
-      public Builder fromFloatingIp(FloatingIP in) {
-         return id(in.getId()).ip(in.getIp()).fixedIp(in.getFixedIp()).instanceId(in.getInstanceId());
+      
+      public T fromFloatingIP(FloatingIP in) {
+         return this
+                  .id(in.getId())
+                  .ip(in.getIp())
+                  .fixedIp(in.getFixedIp())
+                  .instanceId(in.getInstanceId());
       }
-
    }
-   
-   protected FloatingIP() {
-      // we want serializers like Gson to work w/o using sun.misc.Unsafe,
-      // prohibited in GAE. This also implies fields are not final.
-      // see http://code.google.com/p/jclouds/issues/detail?id=925
-   }
-   
-   private String id;
-   private String ip;
-   @SerializedName("fixed_ip")
-   private String fixedIp;
-   @SerializedName("instance_id")
-   private String instanceId;
 
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
+   }
+
+   private final String id;
+   private final String ip;
+   @Named("fixed_ip")
+   private final String fixedIp;
+   @Named("instance_id")
+   private final String instanceId;
+
+   @ConstructorProperties({
+      "id", "ip", "fixed_ip", "instance_id"
+   })
    protected FloatingIP(String id, String ip, @Nullable String fixedIp, @Nullable String instanceId) {
-      this.id = id;
-      this.ip = ip;
+      this.id = checkNotNull(id, "id");
+      this.ip = checkNotNull(ip, "ip");
       this.fixedIp = fixedIp;
       this.instanceId = instanceId;
    }
@@ -105,66 +132,44 @@
       return this.ip;
    }
 
+   @Nullable
    public String getFixedIp() {
       return this.fixedIp;
    }
 
+   @Nullable
    public String getInstanceId() {
       return this.instanceId;
    }
 
    @Override
+   public int hashCode() {
+      return Objects.hashCode(id, ip, fixedIp, instanceId);
+   }
+
+   @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      FloatingIP that = FloatingIP.class.cast(obj);
+      return Objects.equal(this.id, that.id)
+               && Objects.equal(this.ip, that.ip)
+               && Objects.equal(this.fixedIp, that.fixedIp)
+               && Objects.equal(this.instanceId, that.instanceId);
+   }
+   
+   protected ToStringHelper string() {
+      return Objects.toStringHelper(this)
+            .add("id", id).add("ip", ip).add("fixedIp", fixedIp).add("instanceId", instanceId);
+   }
+   
+   @Override
+   public String toString() {
+      return string().toString();
+   }
+
+   @Override
    public int compareTo(FloatingIP o) {
       return this.id.compareTo(o.getId());
    }
-
-   @Override
-   public int hashCode() {
-      final int prime = 31;
-      int result = 1;
-      result = prime * result + ((fixedIp == null) ? 0 : fixedIp.hashCode());
-      result = prime * result + ((id == null) ? 0 : id.hashCode());
-      result = prime * result + ((instanceId == null) ? 0 : instanceId.hashCode());
-      result = prime * result + ((ip == null) ? 0 : ip.hashCode());
-      return result;
-   }
-
-   @Override
-   public boolean equals(Object obj) {
-      if (this == obj)
-         return true;
-      if (obj == null)
-         return false;
-      if (getClass() != obj.getClass())
-         return false;
-      FloatingIP other = (FloatingIP) obj;
-      if (fixedIp == null) {
-         if (other.fixedIp != null)
-            return false;
-      } else if (!fixedIp.equals(other.fixedIp))
-         return false;
-      if (id == null) {
-         if (other.id != null)
-            return false;
-      } else if (!id.equals(other.id))
-         return false;
-      if (instanceId == null) {
-         if (other.instanceId != null)
-            return false;
-      } else if (!instanceId.equals(other.instanceId))
-         return false;
-      if (ip == null) {
-         if (other.ip != null)
-            return false;
-      } else if (!ip.equals(other.ip))
-         return false;
-      return true;
-   }
-
-   @Override
-   public String toString() {
-      return toStringHelper("").add("id", id).add("ip", ip).add("fixedIp", fixedIp).add("instanceId", instanceId)
-            .toString();
-   }
-
 }
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/Host.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/Host.java
index 3a79e0a..2732742 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/Host.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/Host.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -18,52 +18,59 @@
  */
 package org.jclouds.openstack.nova.v2_0.domain;
 
+import java.beans.ConstructorProperties;
+
+import javax.inject.Named;
+
 import org.jclouds.javax.annotation.Nullable;
 
 import com.google.common.base.Objects;
 import com.google.common.base.Objects.ToStringHelper;
-import com.google.gson.annotations.SerializedName;
 
 /**
  * Class Host
- */
+*/
 public class Host {
 
-   public static Builder<?> builder() {
+   public static Builder<?> builder() { 
       return new ConcreteBuilder();
    }
-
-   public Builder<?> toBuilder() {
+   
+   public Builder<?> toBuilder() { 
       return new ConcreteBuilder().fromHost(this);
    }
 
    public static abstract class Builder<T extends Builder<T>>  {
       protected abstract T self();
 
-      private String name;
-      private String service;
-
+      protected String name;
+      protected String service;
+   
+      /** 
+       * @see Host#getName()
+       */
       public T name(String name) {
          this.name = name;
          return self();
       }
 
+      /** 
+       * @see Host#getService()
+       */
       public T service(String service) {
          this.service = service;
          return self();
       }
 
       public Host build() {
-         return new Host(this);
+         return new Host(name, service);
       }
-
+      
       public T fromHost(Host in) {
          return this
-               .name(in.getName())
-               .service(in.getService())
-               ;
+                  .name(in.getName())
+                  .service(in.getService());
       }
-
    }
 
    private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
@@ -72,31 +79,24 @@
          return this;
       }
    }
-   
-   protected Host() {
-      // we want serializers like Gson to work w/o using sun.misc.Unsafe,
-      // prohibited in GAE. This also implies fields are not final.
-      // see http://code.google.com/p/jclouds/issues/detail?id=925
-   }
-   
-   @SerializedName(value="host_name")
-   private String name;
-   private String service;
 
-   protected Host(Builder<?> builder) {
-      this.name = builder.name;
-      this.service = builder.service;
+   @Named("host_name")
+   private final String name;
+   private final String service;
+
+   @ConstructorProperties({
+      "host_name", "service"
+   })
+   protected Host(@Nullable String name, @Nullable String service) {
+      this.name = name;
+      this.service = service;
    }
 
-   /**
-    */
    @Nullable
    public String getName() {
       return this.name;
    }
 
-   /**
-    */
    @Nullable
    public String getService() {
       return this.service;
@@ -113,20 +113,17 @@
       if (obj == null || getClass() != obj.getClass()) return false;
       Host that = Host.class.cast(obj);
       return Objects.equal(this.name, that.name)
-            && Objects.equal(this.service, that.service)
-            ;
+               && Objects.equal(this.service, that.service);
    }
-
+   
    protected ToStringHelper string() {
-      return Objects.toStringHelper("")
-            .add("name", name)
-            .add("service", service)
-            ;
+      return Objects.toStringHelper(this)
+            .add("name", name).add("service", service);
    }
-
+   
    @Override
    public String toString() {
       return string().toString();
    }
 
-}
\ No newline at end of file
+}
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/HostAggregate.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/HostAggregate.java
index 2821cea..b40f914 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/HostAggregate.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/HostAggregate.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -20,46 +20,49 @@
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
-import java.util.Collections;
+import java.beans.ConstructorProperties;
 import java.util.Date;
 import java.util.Map;
 import java.util.Set;
 
+import javax.inject.Named;
+
+import org.jclouds.javax.annotation.Nullable;
+
 import com.google.common.base.Objects;
-import com.google.common.base.Optional;
 import com.google.common.base.Objects.ToStringHelper;
+import com.google.common.base.Optional;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
-import com.google.gson.annotations.SerializedName;
 
 /**
  * Aggregates can be manipulated using the Aggregate Extension to Nova (alias "OS-AGGREGATES")
- *
+ * 
  * @see org.jclouds.openstack.nova.v2_0.extensions.HostAggregateClient
- */
+*/
 public class HostAggregate {
 
-   public static Builder<?> builder() {
+   public static Builder<?> builder() { 
       return new ConcreteBuilder();
    }
-
-   public Builder<?> toBuilder() {
-      return new ConcreteBuilder().fromAggregate(this);
+   
+   public Builder<?> toBuilder() { 
+      return new ConcreteBuilder().fromHostAggregate(this);
    }
 
-   public static abstract class Builder<T extends Builder<T>> {
+   public static abstract class Builder<T extends Builder<T>>  {
       protected abstract T self();
 
-      private String id;
-      private String name;
-      private String availabilityZone;
-      private Set<String> hosts = ImmutableSet.of();
-      private String state;
-      private Date created = new Date();
-      private Date updated;
-      private Map<String, String> metadata = ImmutableMap.of();
-
-      /**
+      protected String id;
+      protected String name;
+      protected String availabilityZone;
+      protected Set<String> hosts = ImmutableSet.of();
+      protected String state;
+      protected Date created;
+      protected Date updated;
+      protected Map<String, String> metadata = ImmutableMap.of();
+   
+      /** 
        * @see HostAggregate#getId()
        */
       public T id(String id) {
@@ -67,7 +70,7 @@
          return self();
       }
 
-      /**
+      /** 
        * @see HostAggregate#getName()
        */
       public T name(String name) {
@@ -75,7 +78,7 @@
          return self();
       }
 
-      /**
+      /** 
        * @see HostAggregate#getAvailabilityZone()
        */
       public T availabilityZone(String availabilityZone) {
@@ -83,22 +86,19 @@
          return self();
       }
 
-      /**
-       * @see HostAggregate#getHosts()
-       */
-      public T hosts(String... hosts) {
-         return hosts(ImmutableSet.copyOf(hosts));
-      }
-      
-      /**
+      /** 
        * @see HostAggregate#getHosts()
        */
       public T hosts(Set<String> hosts) {
-         this.hosts = hosts;
+         this.hosts = ImmutableSet.copyOf(checkNotNull(hosts, "hosts"));      
          return self();
       }
 
-      /**
+      public T hosts(String... in) {
+         return hosts(ImmutableSet.copyOf(in));
+      }
+
+      /** 
        * @see HostAggregate#getState()
        */
       public T state(String state) {
@@ -106,7 +106,7 @@
          return self();
       }
 
-      /**
+      /** 
        * @see HostAggregate#getCreated()
        */
       public T created(Date created) {
@@ -114,7 +114,7 @@
          return self();
       }
 
-      /**
+      /** 
        * @see HostAggregate#getUpdated()
        */
       public T updated(Date updated) {
@@ -122,30 +122,29 @@
          return self();
       }
 
-      /**
+      /** 
        * @see HostAggregate#getMetadata()
        */
       public T metadata(Map<String, String> metadata) {
-         this.metadata = metadata;
+         this.metadata = ImmutableMap.copyOf(checkNotNull(metadata, "metadata"));     
          return self();
       }
 
       public HostAggregate build() {
-         return new HostAggregate(this);
+         return new HostAggregate(id, name, availabilityZone, hosts, state, created, updated, metadata);
       }
-
-      public T fromAggregate(HostAggregate in) {
+      
+      public T fromHostAggregate(HostAggregate in) {
          return this
-               .id(in.getId())
-               .name(in.getName())
-               .availabilityZone(in.getAvailabilityZone())
-               .hosts(in.getHosts())
-               .state(in.getState())
-               .created(in.getCreated())
-               .updated(in.getUpdated().orNull())
-               .metadata(in.getMetadata());
+                  .id(in.getId())
+                  .name(in.getName())
+                  .availabilityZone(in.getAvailabilityZone())
+                  .hosts(in.getHosts())
+                  .state(in.getState())
+                  .created(in.getCreated())
+                  .updated(in.getUpdated().get())
+                  .metadata(in.getMetadata());
       }
-
    }
 
    private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
@@ -154,35 +153,33 @@
          return this;
       }
    }
-   
-   protected HostAggregate() {
-      // we want serializers like Gson to work w/o using sun.misc.Unsafe,
-      // prohibited in GAE. This also implies fields are not final.
-      // see http://code.google.com/p/jclouds/issues/detail?id=925
-   }
-  
-   private String id;
-   private String name;
-   @SerializedName(value = "availability_zone")
-   private String availabilityZone;
-   private Set<String> hosts = ImmutableSet.of();
-   @SerializedName(value = "operational_state")
-   private String state;
-   @SerializedName(value = "created_at")
-   private Date created;
-   @SerializedName(value = "updated_at")
-   private Optional<Date> updated = Optional.absent();
-   private Map<String, String> metadata = ImmutableMap.of();
 
-   protected HostAggregate(Builder<?> builder) {
-      this.id = checkNotNull(builder.id, "id");
-      this.name = checkNotNull(builder.name, "name");
-      this.availabilityZone = checkNotNull(builder.availabilityZone, "availabilityZone");
-      this.hosts = ImmutableSet.copyOf(checkNotNull(builder.hosts, "hosts"));
-      this.state = checkNotNull(builder.state, "state");
-      this.created = checkNotNull(builder.created, "created");
-      this.updated = Optional.fromNullable(builder.updated);
-      this.metadata = ImmutableMap.copyOf(checkNotNull(builder.metadata, "metadata"));
+   private final String id;
+   private final String name;
+   @Named("availability_zone")
+   private final String availabilityZone;
+   private final Set<String> hosts;
+   @Named("operational_state")
+   private final String state;
+   @Named("created_at")
+   private final Date created;
+   @Named("updated_at")
+   private final Optional<Date> updated;
+   private final Map<String, String> metadata;
+
+   @ConstructorProperties({
+      "id", "name", "availability_zone", "hosts", "operational_state", "created_at", "updated_at", "metadata"
+   })
+   protected HostAggregate(String id, String name, String availabilityZone, @Nullable Set<String> hosts, String state, Date created,
+                           @Nullable Date updated, @Nullable Map<String, String> metadata) {
+      this.id = checkNotNull(id, "id");
+      this.name = checkNotNull(name, "name");
+      this.availabilityZone = checkNotNull(availabilityZone, "availabilityZone");
+      this.hosts = hosts == null ? ImmutableSet.<String>of() : ImmutableSet.copyOf(hosts);      
+      this.state = checkNotNull(state, "state");
+      this.created = checkNotNull(created, "created");
+      this.updated = Optional.fromNullable(updated);
+      this.metadata = metadata == null ? ImmutableMap.<String,String>of() : ImmutableMap.copyOf(metadata);     
    }
 
    public String getId() {
@@ -195,7 +192,7 @@
 
    /**
     * note: an "Availability Zone" is different from a Nova "Zone"
-    *
+    * 
     * @return the availability zone this aggregate is in
     */
    public String getAvailabilityZone() {
@@ -203,7 +200,7 @@
    }
 
    public Set<String> getHosts() {
-      return Collections.unmodifiableSet(this.hosts);
+      return this.hosts;
    }
 
    public String getState() {
@@ -233,31 +230,23 @@
       if (obj == null || getClass() != obj.getClass()) return false;
       HostAggregate that = HostAggregate.class.cast(obj);
       return Objects.equal(this.id, that.id)
-            && Objects.equal(this.name, that.name)
-            && Objects.equal(this.availabilityZone, that.availabilityZone)
-            && Objects.equal(this.hosts, that.hosts)
-            && Objects.equal(this.state, that.state)
-            && Objects.equal(this.created, that.created)
-            && Objects.equal(this.updated, that.updated)
-            && Objects.equal(this.metadata, that.metadata)
-            ;
+               && Objects.equal(this.name, that.name)
+               && Objects.equal(this.availabilityZone, that.availabilityZone)
+               && Objects.equal(this.hosts, that.hosts)
+               && Objects.equal(this.state, that.state)
+               && Objects.equal(this.created, that.created)
+               && Objects.equal(this.updated, that.updated)
+               && Objects.equal(this.metadata, that.metadata);
    }
-
+   
    protected ToStringHelper string() {
-      return Objects.toStringHelper("")
-            .add("id", id)
-            .add("name", name)
-            .add("availabilityZone", availabilityZone)
-            .add("hosts", hosts)
-            .add("state", state)
-            .add("created", created)
-            .add("updated", updated)
-            .add("metadata", metadata);
+      return Objects.toStringHelper(this)
+            .add("id", id).add("name", name).add("availabilityZone", availabilityZone).add("hosts", hosts).add("state", state).add("created", created).add("updated", updated).add("metadata", metadata);
    }
-
+   
    @Override
    public String toString() {
       return string().toString();
    }
 
-}
\ No newline at end of file
+}
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/HostResourceUsage.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/HostResourceUsage.java
index 4ae7c12..f838d15 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/HostResourceUsage.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/HostResourceUsage.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -20,73 +20,89 @@
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import java.beans.ConstructorProperties;
+
+import javax.inject.Named;
+
 import org.jclouds.javax.annotation.Nullable;
 
 import com.google.common.base.Objects;
 import com.google.common.base.Objects.ToStringHelper;
-import com.google.gson.annotations.SerializedName;
 
 /**
  * Class HostResourceUsage
- */
+*/
 public class HostResourceUsage {
 
-   public static Builder<?> builder() {
+   public static Builder<?> builder() { 
       return new ConcreteBuilder();
    }
-
-   public Builder<?> toBuilder() {
+   
+   public Builder<?> toBuilder() { 
       return new ConcreteBuilder().fromHostResourceUsage(this);
    }
 
    public static abstract class Builder<T extends Builder<T>>  {
       protected abstract T self();
 
-      private String host;
-      private String project;
-      private int memoryMb;
-      private int cpu;
-      private int diskGb;
-
+      protected String host;
+      protected String project;
+      protected int memoryMb;
+      protected int cpu;
+      protected int diskGb;
+   
+      /** 
+       * @see HostResourceUsage#getHost()
+       */
       public T host(String host) {
          this.host = host;
          return self();
       }
 
+      /** 
+       * @see HostResourceUsage#getProject()
+       */
       public T project(String project) {
          this.project = project;
          return self();
       }
 
+      /** 
+       * @see HostResourceUsage#getMemoryMb()
+       */
       public T memoryMb(int memoryMb) {
          this.memoryMb = memoryMb;
          return self();
       }
 
+      /** 
+       * @see HostResourceUsage#getCpu()
+       */
       public T cpu(int cpu) {
          this.cpu = cpu;
          return self();
       }
 
+      /** 
+       * @see HostResourceUsage#getDiskGb()
+       */
       public T diskGb(int diskGb) {
          this.diskGb = diskGb;
          return self();
       }
 
       public HostResourceUsage build() {
-         return new HostResourceUsage(this);
+         return new HostResourceUsage(host, project, memoryMb, cpu, diskGb);
       }
-
+      
       public T fromHostResourceUsage(HostResourceUsage in) {
          return this
-               .host(in.getHost())
-               .project(in.getProject())
-               .memoryMb(in.getMemoryMb())
-               .cpu(in.getCpu())
-               .diskGb(in.getDiskGb())
-               ;
+                  .host(in.getHost())
+                  .project(in.getProject())
+                  .memoryMb(in.getMemoryMb())
+                  .cpu(in.getCpu())
+                  .diskGb(in.getDiskGb());
       }
-
    }
 
    private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
@@ -95,56 +111,43 @@
          return this;
       }
    }
-   
-   protected HostResourceUsage() {
-      // we want serializers like Gson to work w/o using sun.misc.Unsafe,
-      // prohibited in GAE. This also implies fields are not final.
-      // see http://code.google.com/p/jclouds/issues/detail?id=925
-   }
-  
-   private String host;
-   private String project;
-   @SerializedName(value="memory_mb")
-   private int memoryMb;
-   private int cpu;
-   @SerializedName(value="disk_gb")
-   private int diskGb;
 
-   protected HostResourceUsage(Builder<?> builder) {
-      this.host = checkNotNull(builder.host, "host");
-      this.project = builder.project;
-      this.memoryMb = checkNotNull(builder.memoryMb, "memoryMb");
-      this.cpu = checkNotNull(builder.cpu, "cpu");
-      this.diskGb = checkNotNull(builder.diskGb, "diskGb");
+   private final String host;
+   private final String project;
+   @Named("memory_mb")
+   private final int memoryMb;
+   private final int cpu;
+   @Named("disk_gb")
+   private final int diskGb;
+
+   @ConstructorProperties({
+      "host", "project", "memory_mb", "cpu", "disk_gb"
+   })
+   protected HostResourceUsage(String host, @Nullable String project, int memoryMb, int cpu, int diskGb) {
+      this.host = checkNotNull(host, "host");
+      this.project = project;
+      this.memoryMb = memoryMb;
+      this.cpu = cpu;
+      this.diskGb = diskGb;
    }
 
-   /**
-    */
    public String getHost() {
       return this.host;
    }
 
-   /**
-    */
    @Nullable
    public String getProject() {
       return this.project;
    }
 
-   /**
-    */
    public int getMemoryMb() {
       return this.memoryMb;
    }
 
-   /**
-    */
    public int getCpu() {
       return this.cpu;
    }
 
-   /**
-    */
    public int getDiskGb() {
       return this.diskGb;
    }
@@ -160,24 +163,20 @@
       if (obj == null || getClass() != obj.getClass()) return false;
       HostResourceUsage that = HostResourceUsage.class.cast(obj);
       return Objects.equal(this.host, that.host)
-            && Objects.equal(this.project, that.project)
-            && Objects.equal(this.memoryMb, that.memoryMb)
-            && Objects.equal(this.cpu, that.cpu)
-            && Objects.equal(this.diskGb, that.diskGb);
+               && Objects.equal(this.project, that.project)
+               && Objects.equal(this.memoryMb, that.memoryMb)
+               && Objects.equal(this.cpu, that.cpu)
+               && Objects.equal(this.diskGb, that.diskGb);
    }
-
+   
    protected ToStringHelper string() {
-      return Objects.toStringHelper("")
-            .add("host", host)
-            .add("project", project)
-            .add("memoryMb", memoryMb)
-            .add("cpu", cpu)
-            .add("diskGb", diskGb);
+      return Objects.toStringHelper(this)
+            .add("host", host).add("project", project).add("memoryMb", memoryMb).add("cpu", cpu).add("diskGb", diskGb);
    }
-
+   
    @Override
    public String toString() {
       return string().toString();
    }
 
-}
\ No newline at end of file
+}
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/Image.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/Image.java
index 38bdfaf..6e6b8af 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/Image.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/Image.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -18,16 +18,23 @@
  */
 package org.jclouds.openstack.nova.v2_0.domain;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.beans.ConstructorProperties;
 import java.util.Date;
 import java.util.Map;
 
+import javax.inject.Named;
+
+import org.jclouds.javax.annotation.Nullable;
+import org.jclouds.openstack.v2_0.domain.Link;
 import org.jclouds.openstack.v2_0.domain.Resource;
 
 import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
 import com.google.common.base.Predicates;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Maps;
-import com.google.gson.annotations.SerializedName;
 
 /**
  * An image is a collection of files you use to create or rebuild a server. Operators provide
@@ -35,9 +42,10 @@
  * 
  * @author Jeremy Daggett
  * @see <a href= "http://docs.openstack.org/api/openstack-compute/1.1/content/Images-d1e4427.html"
- *      />
- */
+      />
+*/
 public class Image extends Resource {
+
    /**
     * In-flight images will have the status attribute set to SAVING and the conditional progress
     * element (0-100% completion) will also be returned. Other possible values for the status
@@ -48,44 +56,44 @@
     * @author Adrian Cole
     */
    public static enum Status {
-
+      
       UNRECOGNIZED, UNKNOWN, ACTIVE, SAVING, ERROR, DELETED;
-
+      
       public String value() {
-         return name();
+      return name();
       }
-
+      
       public static Status fromValue(String v) {
-         try {
-            return valueOf(v);
-         } catch (IllegalArgumentException e) {
-            return UNRECOGNIZED;
-         }
+      try {
+      return valueOf(v);
+      } catch (IllegalArgumentException e) {
+      return UNRECOGNIZED;
       }
-
+      }
+      
    }
 
-   public static Builder<?> builder() {
+   public static Builder<?> builder() { 
       return new ConcreteBuilder();
    }
-
-   public Builder<?> toBuilder() {
+   
+   public Builder<?> toBuilder() { 
       return new ConcreteBuilder().fromImage(this);
    }
 
    public static abstract class Builder<T extends Builder<T>> extends Resource.Builder<T>  {
-      private Date updated;
-      private Date created;
-      private String tenantId;
-      private String userId;
-      private Image.Status status;
-      private int progress;
-      private int minDisk;
-      private int minRam;
-      private Resource server;
-      private Map<String, String> metadata = ImmutableMap.of();
-
-      /**
+      protected Date updated;
+      protected Date created;
+      protected String tenantId;
+      protected String userId;
+      protected Image.Status status;
+      protected int progress;
+      protected int minDisk;
+      protected int minRam;
+      protected Resource server;
+      protected Map<String, String> metadata = ImmutableMap.of();
+   
+      /** 
        * @see Image#getUpdated()
        */
       public T updated(Date updated) {
@@ -93,7 +101,7 @@
          return self();
       }
 
-      /**
+      /** 
        * @see Image#getCreated()
        */
       public T created(Date created) {
@@ -101,7 +109,7 @@
          return self();
       }
 
-      /**
+      /** 
        * @see Image#getTenantId()
        */
       public T tenantId(String tenantId) {
@@ -109,7 +117,7 @@
          return self();
       }
 
-      /**
+      /** 
        * @see Image#getUserId()
        */
       public T userId(String userId) {
@@ -117,7 +125,7 @@
          return self();
       }
 
-      /**
+      /** 
        * @see Image#getStatus()
        */
       public T status(Image.Status status) {
@@ -125,7 +133,7 @@
          return self();
       }
 
-      /**
+      /** 
        * @see Image#getProgress()
        */
       public T progress(int progress) {
@@ -133,7 +141,7 @@
          return self();
       }
 
-      /**
+      /** 
        * @see Image#getMinDisk()
        */
       public T minDisk(int minDisk) {
@@ -141,7 +149,7 @@
          return self();
       }
 
-      /**
+      /** 
        * @see Image#getMinRam()
        */
       public T minRam(int minRam) {
@@ -149,7 +157,7 @@
          return self();
       }
 
-      /**
+      /** 
        * @see Image#getServer()
        */
       public T server(Resource server) {
@@ -157,32 +165,31 @@
          return self();
       }
 
-      /**
+      /** 
        * @see Image#getMetadata()
        */
       public T metadata(Map<String, String> metadata) {
-         this.metadata = metadata;
+         this.metadata = ImmutableMap.copyOf(checkNotNull(metadata, "metadata"));     
          return self();
       }
 
       public Image build() {
-         return new Image(this);
+         return new Image(id, name, links, updated, created, tenantId, userId, status, progress, minDisk, minRam, server, metadata);
       }
-
+      
       public T fromImage(Image in) {
          return super.fromResource(in)
-               .updated(in.getUpdated())
-               .created(in.getCreated())
-               .tenantId(in.getTenantId())
-               .userId(in.getUserId())
-               .status(in.getStatus())
-               .progress(in.getProgress())
-               .minDisk(in.getMinDisk())
-               .minRam(in.getMinRam())
-               .server(in.getServer())
-               .metadata(in.getMetadata());
+                  .updated(in.getUpdated())
+                  .created(in.getCreated())
+                  .tenantId(in.getTenantId())
+                  .userId(in.getUserId())
+                  .status(in.getStatus())
+                  .progress(in.getProgress())
+                  .minDisk(in.getMinDisk())
+                  .minRam(in.getMinRam())
+                  .server(in.getServer())
+                  .metadata(in.getMetadata());
       }
-
    }
 
    private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
@@ -191,56 +198,60 @@
          return this;
       }
    }
-   
-   protected Image() {
-      // we want serializers like Gson to work w/o using sun.misc.Unsafe,
-      // prohibited in GAE. This also implies fields are not final.
-      // see http://code.google.com/p/jclouds/issues/detail?id=925
-   }
-  
-   private Date updated;
-   private Date created;
-   @SerializedName("tenant_id")
-   private String tenantId;
-   @SerializedName("user_id")
-   private String userId;
-   private Status status;
-   private int progress;
-   private int minDisk;
-   private int minRam;
-   private Resource server;
-   private Map<String, String> metadata = ImmutableMap.of();
 
-   protected Image(Builder<?> builder) {
-      super(builder);
-      this.updated = builder.updated;
-      this.created = builder.created;
-      this.tenantId = builder.tenantId;
-      this.userId = builder.userId;
-      this.status = builder.status;
-      this.progress = builder.progress;
-      this.minDisk = builder.minDisk;
-      this.minRam = builder.minRam;
-      this.server = builder.server;
-      this.metadata = ImmutableMap.copyOf(builder.metadata);
+   private final Date updated;
+   private final Date created;
+   @Named("tenant_id")
+   private final String tenantId;
+   @Named("user_id")
+   private final String userId;
+   private final Image.Status status;
+   private final int progress;
+   private final int minDisk;
+   private final int minRam;
+   private final Resource server;
+   private final Map<String, String> metadata;
+
+   @ConstructorProperties({
+      "id", "name", "links", "updated", "created", "tenant_id", "user_id", "status", "progress", "minDisk", "minRam", "server", "metadata"
+   })
+   protected Image(String id, @Nullable String name, java.util.Set<Link> links, @Nullable Date updated, @Nullable Date created,
+                   String tenantId, @Nullable String userId, @Nullable Status status, int progress, int minDisk, int minRam,
+                   @Nullable Resource server, @Nullable Map<String, String> metadata) {
+      super(id, name, links);
+      this.updated = updated;
+      this.created = created;
+      this.tenantId = tenantId;
+      this.userId = userId;
+      this.status = status;
+      this.progress = progress;
+      this.minDisk = minDisk;
+      this.minRam = minRam;
+      this.server = server;
+      this.metadata = metadata == null ? ImmutableMap.<String, String>of() : ImmutableMap.copyOf(metadata);
    }
 
+   @Nullable
    public Date getUpdated() {
       return this.updated;
    }
 
+   @Nullable
    public Date getCreated() {
       return this.created;
    }
 
+   @Nullable
    public String getTenantId() {
       return this.tenantId;
    }
 
+   @Nullable
    public String getUserId() {
       return this.userId;
    }
 
+   @Nullable
    public Status getStatus() {
       return this.status;
    }
@@ -257,28 +268,40 @@
       return this.minRam;
    }
 
+   @Nullable
    public Resource getServer() {
       return this.server;
    }
 
    public Map<String, String> getMetadata() {
-      // in case this was assigned in gson
-      return ImmutableMap.copyOf(Maps.filterValues(this.metadata, Predicates.notNull()));
+      return this.metadata;
    }
 
    @Override
-   protected Objects.ToStringHelper string() {
-      return super.string()
-            .add("updated", updated)
-            .add("created", created)
-            .add("tenantId", tenantId)
-            .add("userId", userId)
-            .add("status", status)
-            .add("progress", progress)
-            .add("minDisk", minDisk)
-            .add("minRam", minRam)
-            .add("server", server)
-            .add("metadata", metadata);
+   public int hashCode() {
+      return Objects.hashCode(updated, created, tenantId, userId, status, progress, minDisk, minRam, server, metadata);
    }
 
+   @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      Image that = Image.class.cast(obj);
+      return super.equals(that) && Objects.equal(this.updated, that.updated)
+               && Objects.equal(this.created, that.created)
+               && Objects.equal(this.tenantId, that.tenantId)
+               && Objects.equal(this.userId, that.userId)
+               && Objects.equal(this.status, that.status)
+               && Objects.equal(this.progress, that.progress)
+               && Objects.equal(this.minDisk, that.minDisk)
+               && Objects.equal(this.minRam, that.minRam)
+               && Objects.equal(this.server, that.server)
+               && Objects.equal(this.metadata, that.metadata);
+   }
+   
+   protected ToStringHelper string() {
+      return super.string()
+            .add("updated", updated).add("created", created).add("tenantId", tenantId).add("userId", userId).add("status", status).add("progress", progress).add("minDisk", minDisk).add("minRam", minRam).add("server", server).add("metadata", metadata);
+   }
+   
 }
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/Ingress.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/Ingress.java
index cad1cd6..ed4245a 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/Ingress.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/Ingress.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -18,86 +18,103 @@
  */
 package org.jclouds.openstack.nova.v2_0.domain;
 
-import static com.google.common.base.Objects.equal;
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import java.beans.ConstructorProperties;
+
+import javax.inject.Named;
+
 import com.google.common.annotations.Beta;
 import com.google.common.base.Objects;
 import com.google.common.base.Objects.ToStringHelper;
-import com.google.gson.annotations.SerializedName;
 
 /**
  * Ingress access to a destination protocol on particular ports
  * 
  * @author Adrian Cole
- */
+*/
 @Beta
 public class Ingress {
-   public static Builder builder() {
-      return new Builder();
+
+   public static Builder<?> builder() { 
+      return new ConcreteBuilder();
+   }
+   
+   public Builder<?> toBuilder() { 
+      return new ConcreteBuilder().fromIngress(this);
    }
 
-   public static class Builder {
+   public static abstract class Builder<T extends Builder<T>>  {
+      protected abstract T self();
+
       protected IpProtocol ipProtocol;
       protected int fromPort;
       protected int toPort;
-
-      /**
-       * 
+   
+      /** 
        * @see Ingress#getIpProtocol()
        */
-      public Builder ipProtocol(IpProtocol ipProtocol) {
+      public T ipProtocol(IpProtocol ipProtocol) {
          this.ipProtocol = ipProtocol;
-         return this;
+         return self();
       }
 
-      /**
-       * 
+      /** 
        * @see Ingress#getFromPort()
        */
-      public Builder fromPort(int fromPort) {
+      public T fromPort(int fromPort) {
          this.fromPort = fromPort;
-         return this;
+         return self();
       }
 
-      /**
-       * 
+      /** 
        * @see Ingress#getToPort()
        */
-      public Builder toPort(int toPort) {
+      public T toPort(int toPort) {
          this.toPort = toPort;
-         return this;
+         return self();
       }
 
       public Ingress build() {
          return new Ingress(ipProtocol, fromPort, toPort);
       }
+      
+      public T fromIngress(Ingress in) {
+         return this
+                  .ipProtocol(in.getIpProtocol())
+                  .fromPort(in.getFromPort())
+                  .toPort(in.getToPort());
+      }
    }
-   
-   protected Ingress() {
-      // we want serializers like Gson to work w/o using sun.misc.Unsafe,
-      // prohibited in GAE. This also implies fields are not final.
-      // see http://code.google.com/p/jclouds/issues/detail?id=925
-   }
-  
-   @SerializedName(value = "ip_protocol")
-   protected IpProtocol ipProtocol;
-   @SerializedName(value = "from_port")
-   protected int fromPort;
-   @SerializedName(value = "to_port")
-   protected int toPort;
 
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
+   }
+
+   @Named("ip_protocol")
+   private final IpProtocol ipProtocol;
+   @Named("from_port")
+   private final int fromPort;
+   @Named("to_port")
+   private final int toPort;
+
+   @ConstructorProperties({
+      "ip_protocol", "from_port", "to_port"
+   })
    protected Ingress(IpProtocol ipProtocol, int fromPort, int toPort) {
+      this.ipProtocol = checkNotNull(ipProtocol, "ipProtocol");
       this.fromPort = fromPort;
       this.toPort = toPort;
-      this.ipProtocol = checkNotNull(ipProtocol, "ipProtocol");
    }
 
    /**
     * destination IP protocol
     */
    public IpProtocol getIpProtocol() {
-      return ipProtocol;
+      return this.ipProtocol;
    }
 
    /**
@@ -105,7 +122,7 @@
     * type number of -1 indicates a wildcard (i.e., any ICMP type number).
     */
    public int getFromPort() {
-      return fromPort;
+      return this.fromPort;
    }
 
    /**
@@ -113,19 +130,7 @@
     * -1 indicates a wildcard (i.e., any ICMP code).
     */
    public int getToPort() {
-      return toPort;
-   }
-
-   @Override
-   public boolean equals(Object o) {
-      if (this == o)
-         return true;
-      // allow subtypes
-      if (o == null || !(o instanceof Ingress))
-         return false;
-      Ingress that = Ingress.class.cast(o);
-      return equal(this.ipProtocol, that.ipProtocol) && equal(this.fromPort, that.fromPort)
-               && equal(this.toPort, that.toPort);
+      return this.toPort;
    }
 
    @Override
@@ -134,12 +139,23 @@
    }
 
    @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      Ingress that = Ingress.class.cast(obj);
+      return Objects.equal(this.ipProtocol, that.ipProtocol)
+               && Objects.equal(this.fromPort, that.fromPort)
+               && Objects.equal(this.toPort, that.toPort);
+   }
+   
+   protected ToStringHelper string() {
+      return Objects.toStringHelper(this)
+            .add("ipProtocol", ipProtocol).add("fromPort", fromPort).add("toPort", toPort);
+   }
+   
+   @Override
    public String toString() {
       return string().toString();
    }
 
-   protected ToStringHelper string() {
-      return Objects.toStringHelper("").add("ipProtocol", ipProtocol).add("fromPort", fromPort).add("toPort", toPort);
-   }
-
 }
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/KeyPair.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/KeyPair.java
index 541e812..cb59e67 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/KeyPair.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/KeyPair.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -18,158 +18,169 @@
  */
 package org.jclouds.openstack.nova.v2_0.domain;
 
-import static com.google.common.base.Objects.toStringHelper;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.beans.ConstructorProperties;
+
+import javax.inject.Named;
 
 import org.jclouds.javax.annotation.Nullable;
 
-import com.google.gson.annotations.SerializedName;
+import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
 
-public class KeyPair implements Comparable<KeyPair> {
-   public static Builder builder() {
-      return new Builder();
+/**
+ * Class KeyPair
+*/
+public class KeyPair {
+
+   public static Builder<?> builder() { 
+      return new ConcreteBuilder();
+   }
+   
+   public Builder<?> toBuilder() { 
+      return new ConcreteBuilder().fromKeyPair(this);
    }
 
-   public Builder toBuilder() {
-      return builder().fromKeyPair(this);
-   }
+   public static abstract class Builder<T extends Builder<T>>  {
+      protected abstract T self();
 
-   public static class Builder {
-
-      private String publicKey;
-      private String privateKey;
-      private String userId;
-      private String name;
-      private String fingerprint;
-
-      public Builder publicKey(String publicKey) {
+      protected String publicKey;
+      protected String privateKey;
+      protected String userId;
+      protected String name;
+      protected String fingerprint;
+   
+      /** 
+       * @see KeyPair#getPublicKey()
+       */
+      public T publicKey(String publicKey) {
          this.publicKey = publicKey;
-         return this;
+         return self();
       }
 
-      public Builder privateKey(String privateKey) {
+      /** 
+       * @see KeyPair#getPrivateKey()
+       */
+      public T privateKey(String privateKey) {
          this.privateKey = privateKey;
-         return this;
+         return self();
       }
 
-      public Builder userId(String userId) {
+      /** 
+       * @see KeyPair#getUserId()
+       */
+      public T userId(String userId) {
          this.userId = userId;
-         return this;
+         return self();
       }
 
-      public Builder name(String name) {
+      /** 
+       * @see KeyPair#getName()
+       */
+      public T name(String name) {
          this.name = name;
-         return this;
+         return self();
       }
 
-      public Builder fingerprint(String fingerprint) {
+      /** 
+       * @see KeyPair#getFingerprint()
+       */
+      public T fingerprint(String fingerprint) {
          this.fingerprint = fingerprint;
-         return this;
+         return self();
       }
 
       public KeyPair build() {
          return new KeyPair(publicKey, privateKey, userId, name, fingerprint);
       }
-
-      public Builder fromKeyPair(KeyPair in) {
-         return publicKey(in.getPublicKey()).privateKey(in.getPrivateKey()).userId(in.getUserId()).name(in.getName())
-               .fingerprint(in.getFingerprint());
+      
+      public T fromKeyPair(KeyPair in) {
+         return this
+                  .publicKey(in.getPublicKey())
+                  .privateKey(in.getPrivateKey())
+                  .userId(in.getUserId())
+                  .name(in.getName())
+                  .fingerprint(in.getFingerprint());
       }
-
    }
-   
-   protected KeyPair() {
-      // we want serializers like Gson to work w/o using sun.misc.Unsafe,
-      // prohibited in GAE. This also implies fields are not final.
-      // see http://code.google.com/p/jclouds/issues/detail?id=925
-   }
-  
-   @SerializedName("public_key")
-   private String publicKey;
-   @SerializedName("private_key")
-   private String privateKey;
-   @SerializedName("user_id")
-   private String userId;
-   private String name;
-   private String fingerprint;
 
-   protected KeyPair(String publicKey, String privateKey, @Nullable String userId, String name, String fingerprint) {
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
+   }
+
+   @Named("public_key")
+   private final String publicKey;
+   @Named("private_key")
+   private final String privateKey;
+   @Named("user_id")
+   private final String userId;
+   private final String name;
+   private final String fingerprint;
+
+   @ConstructorProperties({
+      "public_key", "private_key", "user_id", "name", "fingerprint"
+   })
+   protected KeyPair(@Nullable String publicKey, @Nullable String privateKey, @Nullable String userId, String name, @Nullable String fingerprint) {
       this.publicKey = publicKey;
       this.privateKey = privateKey;
       this.userId = userId;
-      this.name = name;
+      this.name = checkNotNull(name, "name");
       this.fingerprint = fingerprint;
    }
 
+   @Nullable
    public String getPublicKey() {
       return this.publicKey;
    }
 
+   @Nullable
    public String getPrivateKey() {
       return this.privateKey;
    }
 
+   @Nullable
    public String getUserId() {
-      return this.privateKey;
+      return this.userId;
    }
 
    public String getName() {
       return this.name;
    }
 
+   @Nullable
    public String getFingerprint() {
       return this.fingerprint;
    }
 
    @Override
-   public int compareTo(KeyPair o) {
-      return this.fingerprint.compareTo(o.getFingerprint());
-   }
-
-   @Override
    public int hashCode() {
-      final int prime = 31;
-      int result = 1;
-      result = prime * result + ((publicKey == null) ? 0 : publicKey.hashCode());
-      result = prime * result + ((privateKey == null) ? 0 : privateKey.hashCode());
-      result = prime * result + ((name == null) ? 0 : name.hashCode());
-      result = prime * result + ((fingerprint == null) ? 0 : fingerprint.hashCode());
-      return result;
+      return Objects.hashCode(publicKey, privateKey, userId, name, fingerprint);
    }
 
    @Override
    public boolean equals(Object obj) {
-      if (this == obj)
-         return true;
-      if (obj == null)
-         return false;
-      if (getClass() != obj.getClass())
-         return false;
-      KeyPair other = (KeyPair) obj;
-      if (publicKey == null) {
-         if (other.publicKey != null)
-            return false;
-      } else if (!publicKey.equals(other.publicKey))
-         return false;
-      if (privateKey == null) {
-         if (other.privateKey != null)
-            return false;
-      } else if (!privateKey.equals(other.privateKey))
-         return false;
-      if (name == null) {
-         if (other.name != null)
-            return false;
-      } else if (!name.equals(other.name))
-         return false;
-      if (fingerprint == null) {
-         if (other.fingerprint != null)
-            return false;
-      } else if (!fingerprint.equals(other.fingerprint))
-         return false;
-      return true;
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      KeyPair that = KeyPair.class.cast(obj);
+      return Objects.equal(this.publicKey, that.publicKey)
+               && Objects.equal(this.privateKey, that.privateKey)
+               && Objects.equal(this.userId, that.userId)
+               && Objects.equal(this.name, that.name)
+               && Objects.equal(this.fingerprint, that.fingerprint);
    }
-
+   
+   protected ToStringHelper string() {
+      return Objects.toStringHelper(this)
+            .add("publicKey", publicKey).add("privateKey", privateKey).add("userId", userId).add("name", name).add("fingerprint", fingerprint);
+   }
+   
    @Override
    public String toString() {
-      return toStringHelper("").add("userId", userId).add("name", name).add("fingerprint", fingerprint).toString();
+      return string().toString();
    }
-}
\ No newline at end of file
+
+}
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/QuotaClass.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/QuotaClass.java
index 9c84a11..8535cdb 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/QuotaClass.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/QuotaClass.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -18,33 +18,32 @@
  */
 package org.jclouds.openstack.nova.v2_0.domain;
 
+import java.beans.ConstructorProperties;
+
 /**
  * Represents the set of limits (quota class) returned by the Quota Class Extension
- *
+ * 
  * @see org.jclouds.openstack.nova.v2_0.extensions.QuotaClassClient
- */
+*/
 public class QuotaClass extends Quotas {
 
-   public static Builder<?> builder() {
+   public static Builder<?> builder() { 
       return new ConcreteBuilder();
    }
-
-   public Builder<?> toBuilder() {
-      return new ConcreteBuilder().fromQuotas(this);
+   
+   public Builder<?> toBuilder() { 
+      return new ConcreteBuilder().fromQuotaClass(this);
    }
 
-   public static abstract class Builder<T extends Builder<T>> extends Quotas.Builder<T> {
-      /**
-       * @see QuotaClass#getId()
-       */
-      @Override
-      public T id(String id) {
-         return super.id(id);
-      }
+   public static abstract class Builder<T extends Builder<T>> extends Quotas.Builder<T>  {
+   
       public QuotaClass build() {
-         return new QuotaClass(this);
+         return new QuotaClass(id, metadataItems, injectedFileContentBytes, volumes, gigabytes, ram, floatingIps, instances, injectedFiles, cores, securityGroups, securityGroupRules, keyPairs);
       }
-
+      
+      public T fromQuotaClass(QuotaClass in) {
+         return super.fromQuotas(in);
+      }
    }
 
    private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
@@ -54,15 +53,12 @@
       }
    }
 
-   protected QuotaClass(Builder<?> builder) {
-      super(builder);
+
+   @ConstructorProperties({
+      "id", "metadata_items", "injected_file_content_bytes", "volumes", "gigabytes", "ram", "floating_ips", "instances", "injected_files", "cores", "security_groups", "security_group_rules", "key_pairs"
+   })
+   protected QuotaClass(String id, int metadataItems, int injectedFileContentBytes, int volumes, int gigabytes, int ram, int floatingIps, int instances, int injectedFiles, int cores, int securityGroups, int securityGroupRules, int keyPairs) {
+      super(id, metadataItems, injectedFileContentBytes, volumes, gigabytes, ram, floatingIps, instances, injectedFiles, cores, securityGroups, securityGroupRules, keyPairs);
    }
 
-   /**
-    * The id of this Quota Class.
-    */
-   @Override
-   public String getId() {
-      return super.getId();
-   }
-}
\ No newline at end of file
+}
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/Quotas.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/Quotas.java
index a708b3c..1bf9fdd 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/Quotas.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/Quotas.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -20,43 +20,46 @@
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import java.beans.ConstructorProperties;
+
+import javax.inject.Named;
+
 import com.google.common.base.Objects;
 import com.google.common.base.Objects.ToStringHelper;
-import com.google.gson.annotations.SerializedName;
 
 /**
  * Represents the set of limits (quotas) returned by the Quota Extension
- *
+ * 
  * @see org.jclouds.openstack.nova.v2_0.extensions.QuotaClient
- */
+*/
 public class Quotas {
 
-   public static Builder<?> builder() {
+   public static Builder<?> builder() { 
       return new ConcreteBuilder();
    }
-
-   public Builder<?> toBuilder() {
+   
+   public Builder<?> toBuilder() { 
       return new ConcreteBuilder().fromQuotas(this);
    }
 
    public static abstract class Builder<T extends Builder<T>>  {
       protected abstract T self();
 
-      private String id;
-      private int metadataItems;
-      private int injectedFileContentBytes;
-      private int volumes;
-      private int gigabytes;
-      private int ram;
-      private int floatingIps;
-      private int instances;
-      private int injectedFiles;
-      private int cores;
-      private int securityGroups;
-      private int securityGroupRules;
-      private int keyPairs;
-
-      /**
+      protected String id;
+      protected int metadataItems;
+      protected int injectedFileContentBytes;
+      protected int volumes;
+      protected int gigabytes;
+      protected int ram;
+      protected int floatingIps;
+      protected int instances;
+      protected int injectedFiles;
+      protected int cores;
+      protected int securityGroups;
+      protected int securityGroupRules;
+      protected int keyPairs;
+   
+      /** 
        * @see Quotas#getId()
        */
       public T id(String id) {
@@ -64,7 +67,7 @@
          return self();
       }
 
-      /**
+      /** 
        * @see Quotas#getMetadataItems()
        */
       public T metadataItems(int metadataItems) {
@@ -72,7 +75,7 @@
          return self();
       }
 
-      /**
+      /** 
        * @see Quotas#getInjectedFileContentBytes()
        */
       public T injectedFileContentBytes(int injectedFileContentBytes) {
@@ -80,7 +83,7 @@
          return self();
       }
 
-      /**
+      /** 
        * @see Quotas#getVolumes()
        */
       public T volumes(int volumes) {
@@ -88,7 +91,7 @@
          return self();
       }
 
-      /**
+      /** 
        * @see Quotas#getGigabytes()
        */
       public T gigabytes(int gigabytes) {
@@ -96,7 +99,7 @@
          return self();
       }
 
-      /**
+      /** 
        * @see Quotas#getRam()
        */
       public T ram(int ram) {
@@ -104,7 +107,7 @@
          return self();
       }
 
-      /**
+      /** 
        * @see Quotas#getFloatingIps()
        */
       public T floatingIps(int floatingIps) {
@@ -112,7 +115,7 @@
          return self();
       }
 
-      /**
+      /** 
        * @see Quotas#getInstances()
        */
       public T instances(int instances) {
@@ -120,7 +123,7 @@
          return self();
       }
 
-      /**
+      /** 
        * @see Quotas#getInjectedFiles()
        */
       public T injectedFiles(int injectedFiles) {
@@ -128,7 +131,7 @@
          return self();
       }
 
-      /**
+      /** 
        * @see Quotas#getCores()
        */
       public T cores(int cores) {
@@ -136,7 +139,7 @@
          return self();
       }
 
-      /**
+      /** 
        * @see Quotas#getSecurityGroups()
        */
       public T securityGroups(int securityGroups) {
@@ -144,7 +147,7 @@
          return self();
       }
 
-      /**
+      /** 
        * @see Quotas#getSecurityGroupRules()
        */
       public T securityGroupRules(int securityGroupRules) {
@@ -152,7 +155,7 @@
          return self();
       }
 
-      /**
+      /** 
        * @see Quotas#getKeyPairs()
        */
       public T keyPairs(int keyPairs) {
@@ -161,23 +164,24 @@
       }
 
       public Quotas build() {
-         return new Quotas(this);
+         return new Quotas(id, metadataItems, injectedFileContentBytes, volumes, gigabytes, ram, floatingIps, instances, injectedFiles, cores, securityGroups, securityGroupRules, keyPairs);
       }
-
+      
       public T fromQuotas(Quotas in) {
-         return this.id(in.getId())
-               .metadataItems(in.getMetadataItems())
-               .injectedFileContentBytes(in.getInjectedFileContentBytes())
-               .volumes(in.getVolumes())
-               .gigabytes(in.getGigabytes())
-               .ram(in.getRam())
-               .floatingIps(in.getFloatingIps())
-               .instances(in.getInstances())
-               .injectedFiles(in.getInjectedFiles())
-               .cores(in.getCores())
-               .securityGroups(in.getSecurityGroups())
-               .securityGroupRules(in.getSecurityGroupRules())
-               .keyPairs(in.getKeyPairs());
+         return this
+                  .id(in.getId())
+                  .metadataItems(in.getMetadataItems())
+                  .injectedFileContentBytes(in.getInjectedFileContentBytes())
+                  .volumes(in.getVolumes())
+                  .gigabytes(in.getGigabytes())
+                  .ram(in.getRam())
+                  .floatingIps(in.getFloatingIps())
+                  .instances(in.getInstances())
+                  .injectedFiles(in.getInjectedFiles())
+                  .cores(in.getCores())
+                  .securityGroups(in.getSecurityGroups())
+                  .securityGroupRules(in.getSecurityGroupRules())
+                  .keyPairs(in.getKeyPairs());
       }
    }
 
@@ -187,49 +191,45 @@
          return this;
       }
    }
-   
-   protected Quotas() {
-      // we want serializers like Gson to work w/o using sun.misc.Unsafe,
-      // prohibited in GAE. This also implies fields are not final.
-      // see http://code.google.com/p/jclouds/issues/detail?id=925
-   }
-  
-   @SerializedName("id")
-   private String id;
-   @SerializedName("metadata_items")
-   private int metadataItems;
-   @SerializedName("injected_file_content_bytes")
-   private int injectedFileContentBytes;
-   private int volumes;
-   private int gigabytes;
-   private int ram;
-   @SerializedName("floating_ips")
-   private int floatingIps;
-   private int instances;
-   @SerializedName("injected_files")
-   private int injectedFiles;
-   private int cores;
-   @SerializedName("security_groups")
-   private int securityGroups;
-   @SerializedName("security_group_rules")
-   private int securityGroupRules;
-   @SerializedName("key_pairs")
-   private int keyPairs;
 
-   protected Quotas(Builder<?> builder) {
-      this.id = checkNotNull(builder.id, "id");
-      this.metadataItems = checkNotNull(builder.metadataItems, "metadataItems");
-      this.injectedFileContentBytes = checkNotNull(builder.injectedFileContentBytes, "injectedFileContentBytes");
-      this.volumes = checkNotNull(builder.volumes, "volumes");
-      this.gigabytes = checkNotNull(builder.gigabytes, "gigabytes");
-      this.ram = checkNotNull(builder.ram, "ram");
-      this.floatingIps = checkNotNull(builder.floatingIps, "floatingIps");
-      this.instances = checkNotNull(builder.instances, "instances");
-      this.injectedFiles = checkNotNull(builder.injectedFiles, "injectedFiles");
-      this.cores = checkNotNull(builder.cores, "cores");
-      this.securityGroups = checkNotNull(builder.securityGroups, "securityGroups");
-      this.securityGroupRules = checkNotNull(builder.securityGroupRules, "securityGroupRules");
-      this.keyPairs = checkNotNull(builder.keyPairs, "keyPairs");
+   private final String id;
+   @Named("metadata_items")
+   private final int metadataItems;
+   @Named("injected_file_content_bytes")
+   private final int injectedFileContentBytes;
+   private final int volumes;
+   private final int gigabytes;
+   private final int ram;
+   @Named("floating_ips")
+   private final int floatingIps;
+   private final int instances;
+   @Named("injected_files")
+   private final int injectedFiles;
+   private final int cores;
+   @Named("security_groups")
+   private final int securityGroups;
+   @Named("security_group_rules")
+   private final int securityGroupRules;
+   @Named("key_pairs")
+   private final int keyPairs;
+
+   @ConstructorProperties({
+      "id", "metadata_items", "injected_file_content_bytes", "volumes", "gigabytes", "ram", "floating_ips", "instances", "injected_files", "cores", "security_groups", "security_group_rules", "key_pairs"
+   })
+   protected Quotas(String id, int metadataItems, int injectedFileContentBytes, int volumes, int gigabytes, int ram, int floatingIps, int instances, int injectedFiles, int cores, int securityGroups, int securityGroupRules, int keyPairs) {
+      this.id = checkNotNull(id, "id");
+      this.metadataItems = metadataItems;
+      this.injectedFileContentBytes = injectedFileContentBytes;
+      this.volumes = volumes;
+      this.gigabytes = gigabytes;
+      this.ram = ram;
+      this.floatingIps = floatingIps;
+      this.instances = instances;
+      this.injectedFiles = injectedFiles;
+      this.cores = cores;
+      this.securityGroups = securityGroups;
+      this.securityGroupRules = securityGroupRules;
+      this.keyPairs = keyPairs;
    }
 
    /**
@@ -298,7 +298,6 @@
 
    /**
     * @return the limit of the number of security groups that can be created for the tenant
-    *
     * @see org.jclouds.openstack.nova.v2_0.extensions.SecurityGroupClient
     */
    public int getSecurityGroups() {
@@ -307,7 +306,6 @@
 
    /**
     * @return the limit of the number of security group rules that can be created for the tenant
-    *
     * @see org.jclouds.openstack.nova.v2_0.extensions.SecurityGroupClient
     */
    public int getSecurityGroupRules() {
@@ -316,7 +314,6 @@
 
    /**
     * @return the limit of the number of key pairs that can be created for the tenant
-    *
     * @see org.jclouds.openstack.nova.v2_0.extensions.KeyPairClient
     */
    public int getKeyPairs() {
@@ -334,40 +331,28 @@
       if (obj == null || getClass() != obj.getClass()) return false;
       Quotas that = Quotas.class.cast(obj);
       return Objects.equal(this.id, that.id)
-            && Objects.equal(this.metadataItems, that.metadataItems)
-            && Objects.equal(this.injectedFileContentBytes, that.injectedFileContentBytes)
-            && Objects.equal(this.volumes, that.volumes)
-            && Objects.equal(this.gigabytes, that.gigabytes)
-            && Objects.equal(this.ram, that.ram)
-            && Objects.equal(this.floatingIps, that.floatingIps)
-            && Objects.equal(this.instances, that.instances)
-            && Objects.equal(this.injectedFiles, that.injectedFiles)
-            && Objects.equal(this.cores, that.cores)
-            && Objects.equal(this.securityGroups, that.securityGroups)
-            && Objects.equal(this.securityGroupRules, that.securityGroupRules)
-            && Objects.equal(this.keyPairs, that.keyPairs);
+               && Objects.equal(this.metadataItems, that.metadataItems)
+               && Objects.equal(this.injectedFileContentBytes, that.injectedFileContentBytes)
+               && Objects.equal(this.volumes, that.volumes)
+               && Objects.equal(this.gigabytes, that.gigabytes)
+               && Objects.equal(this.ram, that.ram)
+               && Objects.equal(this.floatingIps, that.floatingIps)
+               && Objects.equal(this.instances, that.instances)
+               && Objects.equal(this.injectedFiles, that.injectedFiles)
+               && Objects.equal(this.cores, that.cores)
+               && Objects.equal(this.securityGroups, that.securityGroups)
+               && Objects.equal(this.securityGroupRules, that.securityGroupRules)
+               && Objects.equal(this.keyPairs, that.keyPairs);
    }
-
+   
    protected ToStringHelper string() {
-      return Objects.toStringHelper("")
-            .add("id", id)
-            .add("metadataItems", metadataItems)
-            .add("injectedFileContentBytes", injectedFileContentBytes)
-            .add("volumes", volumes)
-            .add("gigabytes", gigabytes)
-            .add("ram", ram)
-            .add("floatingIps", floatingIps)
-            .add("instances", instances)
-            .add("injectedFiles", injectedFiles)
-            .add("cores", cores)
-            .add("securityGroups", securityGroups)
-            .add("securityGroupRules", securityGroupRules)
-            .add("keyPairs", keyPairs);
+      return Objects.toStringHelper(this)
+            .add("id", id).add("metadataItems", metadataItems).add("injectedFileContentBytes", injectedFileContentBytes).add("volumes", volumes).add("gigabytes", gigabytes).add("ram", ram).add("floatingIps", floatingIps).add("instances", instances).add("injectedFiles", injectedFiles).add("cores", cores).add("securityGroups", securityGroups).add("securityGroupRules", securityGroupRules).add("keyPairs", keyPairs);
    }
-
+   
    @Override
    public String toString() {
       return string().toString();
    }
 
-}
\ No newline at end of file
+}
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/SecurityGroup.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/SecurityGroup.java
index 4edeb8a..4122e33 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/SecurityGroup.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/SecurityGroup.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -18,107 +18,118 @@
  */
 package org.jclouds.openstack.nova.v2_0.domain;
 
-import static com.google.common.base.Objects.equal;
-import static com.google.common.base.Objects.toStringHelper;
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import java.beans.ConstructorProperties;
 import java.util.Set;
 
+import javax.inject.Named;
+
 import org.jclouds.javax.annotation.Nullable;
 
 import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
 import com.google.common.collect.ImmutableSet;
-import com.google.gson.annotations.SerializedName;
 
 /**
  * Defines a security group
- * 
- */
+*/
 public class SecurityGroup {
-   public static Builder builder() {
-      return new Builder();
+
+   public static Builder<?> builder() { 
+      return new ConcreteBuilder();
+   }
+   
+   public Builder<?> toBuilder() { 
+      return new ConcreteBuilder().fromSecurityGroup(this);
    }
 
-   public Builder toBuilder() {
-      return builder().fromSecurityGroup(this);
-   }
+   public static abstract class Builder<T extends Builder<T>>  {
+      protected abstract T self();
 
-   public static class Builder {
-
-      private String id;
-      private String tenantId;
-      private String name;
-      private String description;
-      private Set<SecurityGroupRule> rules = ImmutableSet.<SecurityGroupRule> of();
-
-      public Builder id(String id) {
+      protected String id;
+      protected String tenantId;
+      protected String name;
+      protected String description;
+      protected Set<SecurityGroupRule> rules = ImmutableSet.of();
+   
+      /** 
+       * @see SecurityGroup#getId()
+       */
+      public T id(String id) {
          this.id = id;
-         return this;
+         return self();
       }
 
-      public Builder tenantId(String tenantId) {
+      /** 
+       * @see SecurityGroup#getTenantId()
+       */
+      public T tenantId(String tenantId) {
          this.tenantId = tenantId;
-         return this;
+         return self();
       }
 
-      public Builder name(String name) {
+      /** 
+       * @see SecurityGroup#getName()
+       */
+      public T name(String name) {
          this.name = name;
-         return this;
+         return self();
       }
 
-      public Builder description(String description) {
+      /** 
+       * @see SecurityGroup#getDescription()
+       */
+      public T description(String description) {
          this.description = description;
-         return this;
+         return self();
       }
 
-      /**
-       * 
-       * @see #getSecurityGroupNames
+      /** 
+       * @see SecurityGroup#getRules()
        */
-      public Builder rules(SecurityGroupRule... rules) {
-         return rules(ImmutableSet.copyOf(checkNotNull(rules, "rules")));
+      public T rules(Set<SecurityGroupRule> rules) {
+         this.rules = ImmutableSet.copyOf(checkNotNull(rules, "rules"));      
+         return self();
       }
 
-      /**
-       * @see #getSecurityGroupNames
-       */
-      public Builder rules(Iterable<SecurityGroupRule> rules) {
-         this.rules = ImmutableSet.copyOf(checkNotNull(rules, "rules"));
-         return this;
-      }
-
-      public Builder rules(Set<SecurityGroupRule> rules) {
-         this.rules = rules;
-         return this;
+      public T rules(SecurityGroupRule... in) {
+         return rules(ImmutableSet.copyOf(in));
       }
 
       public SecurityGroup build() {
          return new SecurityGroup(id, tenantId, name, description, rules);
       }
-
-      public Builder fromSecurityGroup(SecurityGroup in) {
-         return id(in.getId()).tenantId(in.getTenantId()).name(in.getName()).description(in.getDescription()).rules(
-                  in.getRules());
+      
+      public T fromSecurityGroup(SecurityGroup in) {
+         return this
+                  .id(in.getId())
+                  .tenantId(in.getTenantId())
+                  .name(in.getName())
+                  .description(in.getDescription())
+                  .rules(in.getRules());
       }
-
    }
-   
-   protected SecurityGroup() {
-      // we want serializers like Gson to work w/o using sun.misc.Unsafe,
-      // prohibited in GAE. This also implies fields are not final.
-      // see http://code.google.com/p/jclouds/issues/detail?id=925
-   }
-  
-   protected String id;
-   @SerializedName("tenant_id")
-   protected String tenantId;
-   protected String name;
-   protected String description;
-   protected Set<SecurityGroupRule> rules = ImmutableSet.of();
 
-   protected SecurityGroup(String id, String tenantId, @Nullable String name, @Nullable String description,
-            Set<SecurityGroupRule> rules) {
-      this.id = id;
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
+   }
+
+   private final String id;
+   @Named("tenant_id")
+   private final String tenantId;
+   private final String name;
+   private final String description;
+   private final Set<SecurityGroupRule> rules;
+
+   @ConstructorProperties({
+      "id", "tenant_id", "name", "description", "rules"
+   })
+   protected SecurityGroup(String id, @Nullable String tenantId, @Nullable String name, @Nullable String description, Set<SecurityGroupRule> rules) {
+      this.id = checkNotNull(id, "id");
       this.tenantId = tenantId;
       this.name = name;
       this.description = description;
@@ -130,44 +141,50 @@
       return this.id;
    }
 
+   @Nullable
    public String getTenantId() {
       return this.tenantId;
    }
 
+   @Nullable
    public String getName() {
       return this.name;
    }
 
+   @Nullable
    public String getDescription() {
       return this.description;
    }
 
    public Set<SecurityGroupRule> getRules() {
-      return this.rules == null ? ImmutableSet.<SecurityGroupRule> of() : rules;
-   }
-
-   @Override
-   public boolean equals(Object object) {
-      if (this == object) {
-         return true;
-      }
-      if (object instanceof SecurityGroup) {
-         final SecurityGroup other = SecurityGroup.class.cast(object);
-         return equal(tenantId, other.tenantId) && equal(id, other.id) && equal(name, other.name);
-      } else {
-         return false;
-      }
+      return this.rules;
    }
 
    @Override
    public int hashCode() {
-      return Objects.hashCode(tenantId, id, name);
+      return Objects.hashCode(id, tenantId, name, description, rules);
    }
 
    @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      SecurityGroup that = SecurityGroup.class.cast(obj);
+      return Objects.equal(this.id, that.id)
+               && Objects.equal(this.tenantId, that.tenantId)
+               && Objects.equal(this.name, that.name)
+               && Objects.equal(this.description, that.description)
+               && Objects.equal(this.rules, that.rules);
+   }
+   
+   protected ToStringHelper string() {
+      return Objects.toStringHelper(this)
+            .add("id", id).add("tenantId", tenantId).add("name", name).add("description", description).add("rules", rules);
+   }
+   
+   @Override
    public String toString() {
-      return toStringHelper("").add("tenantId", getTenantId()).add("id", getId()).add("name", getName()).add(
-               "description", description).add("rules", getRules()).toString();
+      return string().toString();
    }
 
-}
\ No newline at end of file
+}
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/SecurityGroupRule.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/SecurityGroupRule.java
index 2500a58..6047542 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/SecurityGroupRule.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/SecurityGroupRule.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -18,103 +18,29 @@
  */
 package org.jclouds.openstack.nova.v2_0.domain;
 
-import static com.google.common.base.Objects.toStringHelper;
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import java.beans.ConstructorProperties;
+
+import javax.inject.Named;
+
 import org.jclouds.javax.annotation.Nullable;
 
+import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
 import com.google.common.collect.ForwardingObject;
-import com.google.gson.annotations.SerializedName;
 
 /**
  * Defines a security group rule
- * 
  */
 public class SecurityGroupRule extends Ingress {
 
-   public static Builder builder() {
-      return new Builder();
-   }
-
-   public Builder toBuilder() {
-      return builder().fromSecurityGroupRule(this);
-   }
-
-   public static class Builder extends Ingress.Builder {
-      private String id;
-      private String parentGroupId;
-      private TenantIdAndName group;
-      private String ipRange;
-
-      public Builder id(String id) {
-         this.id = id;
-         return this;
-      }
-
-      public Builder group(TenantIdAndName group) {
-         this.group = group;
-         return this;
-      }
-
-      public Builder parentGroupId(String parentGroupId) {
-         this.parentGroupId = parentGroupId;
-         return this;
-      }
-
-      public Builder ipRange(String ipRange) {
-         this.ipRange = ipRange;
-         return this;
-      }
-
-      @Override
-      public SecurityGroupRule build() {
-         return new SecurityGroupRule(ipProtocol, fromPort, toPort, id, parentGroupId, group, ipRange);
-      }
-
-      public Builder fromSecurityGroupRule(SecurityGroupRule in) {
-         return id(in.getId()).fromPort(in.getFromPort()).group(in.getGroup()).ipProtocol(in.getIpProtocol()).toPort(
-                  in.getToPort()).parentGroupId(in.getParentGroupId()).ipRange(in.getIpRange());
-      }
-
-      @Override
-      public Builder ipProtocol(IpProtocol ipProtocol) {
-         super.ipProtocol(ipProtocol);
-         return this;
-      }
-
-      @Override
-      public Builder fromPort(int fromPort) {
-         super.fromPort(fromPort);
-         return this;
-      }
-
-      @Override
-      public Builder toPort(int toPort) {
-         super.toPort(toPort);
-         return this;
-      }
-
-   }
-   
-   protected SecurityGroupRule() {
-      // we want serializers like Gson to work w/o using sun.misc.Unsafe,
-      // prohibited in GAE. This also implies fields are not final.
-      // see http://code.google.com/p/jclouds/issues/detail?id=925
-   }
-  
-   protected String id;
-   protected TenantIdAndName group;
-   @SerializedName(value = "parent_group_id")
-   protected String parentGroupId;
-   @SerializedName(value = "ip_range")
-   protected Cidr ipRange;
-
-   // type to get around unnecessary structure
-   private static class Cidr extends ForwardingObject {
+   public static class Cidr extends ForwardingObject {
       private String cidr;
 
-      private Cidr(String cidr) {
-         this.cidr = cidr;
+      @ConstructorProperties("cidr")
+      protected Cidr(String cidr) {
+         this.cidr = checkNotNull(cidr);
       }
 
       @Override
@@ -123,18 +49,88 @@
       }
    }
 
-
-   protected SecurityGroupRule(IpProtocol ipProtocol, int fromPort, int toPort, String id, String parentGroupId,
-            @Nullable TenantIdAndName group, @Nullable String ipRange) {
-      super(ipProtocol, fromPort, toPort);
-      this.parentGroupId = checkNotNull(parentGroupId, "parentGroupId");
-      this.id = checkNotNull(id, "id");
-      this.group = group;
-      this.ipRange = ipRange != null ? new Cidr(ipRange) : null;
+   public static Builder<?> builder() {
+      return new ConcreteBuilder();
    }
 
-   public String getParentGroupId() {
-      return this.parentGroupId;
+   public Builder<?> toBuilder() {
+      return new ConcreteBuilder().fromSecurityGroupRule(this);
+   }
+
+   public static abstract class Builder<T extends Builder<T>> extends Ingress.Builder<T> {
+      protected String id;
+      protected TenantIdAndName group;
+      protected String parentGroupId;
+      protected String ipRange;
+
+      /**
+       * @see SecurityGroupRule#getId()
+       */
+      public T id(String id) {
+         this.id = id;
+         return self();
+      }
+
+      /**
+       * @see SecurityGroupRule#getGroup()
+       */
+      public T group(TenantIdAndName group) {
+         this.group = group;
+         return self();
+      }
+
+      /**
+       * @see SecurityGroupRule#getParentGroupId()
+       */
+      public T parentGroupId(String parentGroupId) {
+         this.parentGroupId = parentGroupId;
+         return self();
+      }
+
+      /**
+       * @see SecurityGroupRule#getIpRange()
+       */
+      public T ipRange(String ipRange) {
+         this.ipRange = ipRange;
+         return self();
+      }
+
+      public SecurityGroupRule build() {
+         return new SecurityGroupRule(ipProtocol, fromPort, toPort, id, group, parentGroupId, ipRange == null ? null : new Cidr(ipRange));
+      }
+
+      public T fromSecurityGroupRule(SecurityGroupRule in) {
+         return super.fromIngress(in)
+               .id(in.getId())
+               .group(in.getGroup())
+               .parentGroupId(in.getParentGroupId())
+               .ipRange(in.getIpRange());
+      }
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
+   }
+
+   private final String id;
+   private final TenantIdAndName group;
+   @Named("parent_group_id")
+   private final String parentGroupId;
+   @Named("ip_range")
+   private final SecurityGroupRule.Cidr ipRange;
+
+   @ConstructorProperties({
+         "ip_protocol", "from_port", "to_port", "id", "group", "parent_group_id", "ip_range"
+   })
+   protected SecurityGroupRule(IpProtocol ipProtocol, int fromPort, int toPort, String id, @Nullable TenantIdAndName group, String parentGroupId, @Nullable Cidr ipRange) {
+      super(ipProtocol, fromPort, toPort);
+      this.id = checkNotNull(id, "id");
+      this.group = group;
+      this.parentGroupId = checkNotNull(parentGroupId, "parentGroupId");
+      this.ipRange = ipRange;
    }
 
    public String getId() {
@@ -143,73 +139,37 @@
 
    @Nullable
    public TenantIdAndName getGroup() {
-      if (this.group == null || this.group.getName() == null)
-         return null;
       return this.group;
    }
 
+   public String getParentGroupId() {
+      return this.parentGroupId;
+   }
+
    @Nullable
    public String getIpRange() {
-      return this.ipRange == null ? null : ipRange.cidr;
+      return ipRange == null ? null : ipRange.cidr;
    }
 
    @Override
    public int hashCode() {
-      final int prime = 31;
-      int result = 1;
-      result = prime * result + fromPort;
-      result = prime * result + ((group == null) ? 0 : group.hashCode());
-      result = prime * result + ((id == null) ? 0 : id.hashCode());
-      result = prime * result + ((ipProtocol == null) ? 0 : ipProtocol.hashCode());
-      result = prime * result + ((ipRange == null) ? 0 : ipRange.hashCode());
-      result = prime * result + ((parentGroupId == null) ? 0 : parentGroupId.hashCode());
-      result = prime * result + toPort;
-      return result;
+      return Objects.hashCode(id, group, parentGroupId, ipRange);
    }
 
    @Override
    public boolean equals(Object obj) {
-      if (this == obj)
-         return true;
-      if (obj == null)
-         return false;
-      if (getClass() != obj.getClass())
-         return false;
-      SecurityGroupRule other = (SecurityGroupRule) obj;
-      if (fromPort != other.fromPort)
-         return false;
-      if (group == null) {
-         if (other.group != null)
-            return false;
-      } else if (!group.equals(other.group))
-         return false;
-      if (id == null) {
-         if (other.id != null)
-            return false;
-      } else if (!id.equals(other.id))
-         return false;
-      if (ipProtocol != other.ipProtocol)
-         return false;
-      if (ipRange == null) {
-         if (other.ipRange != null)
-            return false;
-      } else if (!ipRange.equals(other.ipRange))
-         return false;
-      if (parentGroupId == null) {
-         if (other.parentGroupId != null)
-            return false;
-      } else if (!parentGroupId.equals(other.parentGroupId))
-         return false;
-      if (toPort != other.toPort)
-         return false;
-      return true;
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      SecurityGroupRule that = SecurityGroupRule.class.cast(obj);
+      return super.equals(that) && Objects.equal(this.id, that.id)
+            && Objects.equal(this.group, that.group)
+            && Objects.equal(this.parentGroupId, that.parentGroupId)
+            && Objects.equal(this.ipRange, that.ipRange);
    }
 
-   @Override
-   public String toString() {
-      return toStringHelper("").add("id", id).add("fromPort", fromPort).add("group", getGroup()).add("ipProtocol",
-               ipProtocol).add("toPort", toPort).add("parentGroupId", parentGroupId).add("ipRange", getIpRange())
-               .toString();
+   protected ToStringHelper string() {
+      return super.string()
+            .add("id", id).add("group", group).add("parentGroupId", parentGroupId).add("ipRange", ipRange);
    }
 
-}
\ No newline at end of file
+}
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/Server.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/Server.java
index 1c2d901..a80cb11 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/Server.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/Server.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -20,29 +20,28 @@
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import java.beans.ConstructorProperties;
 import java.util.Date;
 import java.util.Map;
-import java.util.Set;
+
+import javax.inject.Named;
 
 import org.jclouds.javax.annotation.Nullable;
 import org.jclouds.openstack.nova.v2_0.extensions.KeyPairClient;
+import org.jclouds.openstack.v2_0.domain.Link;
 import org.jclouds.openstack.v2_0.domain.Resource;
-import org.jclouds.util.Multimaps2;
 
-import com.google.common.base.Optional;
-import com.google.common.base.Predicates;
-import com.google.common.base.Strings;
 import com.google.common.base.Objects.ToStringHelper;
+import com.google.common.base.Optional;
+import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableMultimap;
-import com.google.common.collect.Maps;
 import com.google.common.collect.Multimap;
-import com.google.gson.annotations.SerializedName;
 
 /**
  * A server is a virtual machine instance in the compute system. Flavor and image are requisite
  * elements when creating a server.
- * 
+ *
  * @author Adrian Cole
  * @see <a href=
  *      "http://docs.openstack.org/api/openstack-compute/1.1/content/Get_Server_Details-d1e2623.html"
@@ -53,10 +52,10 @@
    /**
     * Servers contain a status attribute that can be used as an indication of the current server
     * state. Servers with an ACTIVE status are available for use.
-    * 
+    * <p/>
     * Other possible values for the status attribute include: BUILD, REBUILD, SUSPENDED, RESIZE,
     * VERIFY_RESIZE, REVERT_RESIZE, PASSWORD, REBOOT, HARD_REBOOT, DELETED, UNKNOWN, and ERROR.
-    * 
+    *
     * @author Adrian Cole
     */
    public static enum Status {
@@ -76,7 +75,6 @@
       }
    }
 
-
    public static Builder<?> builder() {
       return new ConcreteBuilder();
    }
@@ -85,28 +83,25 @@
       return new ConcreteBuilder().fromServer(this);
    }
 
-   public static abstract class Builder<T extends Builder<T>> extends Resource.Builder<T>  {
-      private String uuid;
-      private String tenantId;
-      private String userId;
-      private Date updated;
-      private Date created;
-      private String hostId;
-      private String accessIPv4;
-      private String accessIPv6;
-      private Server.Status status;
-      private Resource image;
-      private Resource flavor;
-      private String keyName;
-      private String configDrive;
-      private Multimap<String, Address> addresses = ImmutableMultimap.of();
-      private Map<String, String> metadata = ImmutableMap.of();
-      // Extended status extension
-      private ServerExtendedStatus extendedStatus;
-      // Extended server attributes extension
-      private ServerExtendedAttributes extendedAttributes;
-      // Disk Config extension
-      private String diskConfig;
+   public static abstract class Builder<T extends Builder<T>> extends Resource.Builder<T> {
+      protected String uuid;
+      protected String tenantId;
+      protected String userId;
+      protected Date updated;
+      protected Date created;
+      protected String hostId;
+      protected String accessIPv4;
+      protected String accessIPv6;
+      protected Server.Status status;
+      protected Resource image;
+      protected Resource flavor;
+      protected String keyName;
+      protected String configDrive;
+      protected Multimap<String, Address> addresses = ImmutableMultimap.of();
+      protected Map<String, String> metadata = ImmutableMap.of();
+      protected ServerExtendedStatus extendedStatus;
+      protected ServerExtendedAttributes extendedAttributes;
+      protected String diskConfig;
 
       /**
        * @see Server#getUuid()
@@ -224,10 +219,10 @@
        * @see Server#getMetadata()
        */
       public T metadata(Map<String, String> metadata) {
-         this.metadata = metadata;
+         this.metadata = ImmutableMap.copyOf(checkNotNull(metadata, "metadata"));
          return self();
       }
-      
+
       /**
        * @see Server#getExtendedStatus()
        */
@@ -237,9 +232,9 @@
       }
 
       /**
-       * @see Server#getExtendedAttributes() 
+       * @see Server#getExtendedAttributes()
        */
-      public T extraAttributes(ServerExtendedAttributes extendedAttributes) {
+      public T extendedAttributes(ServerExtendedAttributes extendedAttributes) {
          this.extendedAttributes = extendedAttributes;
          return self();
       }
@@ -253,7 +248,9 @@
       }
 
       public Server build() {
-         return new Server(this);
+         return new Server(id, name, links, uuid, tenantId, userId, updated, created, hostId, accessIPv4, accessIPv6,
+               status, image, flavor, keyName, configDrive, addresses, metadata, extendedStatus,
+               extendedAttributes, diskConfig);
       }
 
       public T fromServer(Server in) {
@@ -274,7 +271,7 @@
                .addresses(in.getAddresses())
                .metadata(in.getMetadata())
                .extendedStatus(in.getExtendedStatus().orNull())
-               .extraAttributes(in.getExtendedAttributes().orNull())
+               .extendedAttributes(in.getExtendedAttributes().orNull())
                .diskConfig(in.getDiskConfig().orNull());
       }
    }
@@ -285,68 +282,64 @@
          return this;
       }
    }
-   
-   protected Server() {
-      // we want serializers like Gson to work w/o using sun.misc.Unsafe,
-      // prohibited in GAE. This also implies fields are not final.
-      // see http://code.google.com/p/jclouds/issues/detail?id=925
-   }
-  
-   private String uuid;
-   @SerializedName("tenant_id")
-   private String tenantId;
-   @SerializedName("user_id")
-   private String userId;
-   private Date updated;
-   private Date created;
-   private String hostId;
-   private String accessIPv4;
-   private String accessIPv6;
-   private Status status;
-   private Resource image;
-   private Resource flavor;
-   @SerializedName("key_name")
-   private String keyName;
-   @SerializedName("config_drive")
-   private String configDrive;
-   // TODO: get gson multimap adapter!
-   private Map<String, Set<Address>> addresses = ImmutableMap.of();
-   private Map<String, String> metadata = ImmutableMap.of();
-   // Extended status extension
-   // @Prefixed("OS-EXT-STS:")
-   private Optional<ServerExtendedStatus> extendedStatus = Optional.absent();
-   // Extended server attributes extension
-   // @Prefixed("OS-EXT-SRV-ATTR:")
-   private Optional<ServerExtendedAttributes> extendedAttributes = Optional.absent();
-   // Disk Config extension
-   @SerializedName("OS-DCF:diskConfig")
-   private Optional<String> diskConfig = Optional.absent();
 
-   protected Server(Builder<?> builder) {
-      super(builder);
-      this.uuid = builder.uuid; // TODO: see what version this came up in
-      this.tenantId = checkNotNull(builder.tenantId, "tenantId");
-      this.userId = checkNotNull(builder.userId, "userId");
-      this.updated = checkNotNull(builder.updated, "updated");
-      this.created = checkNotNull(builder.created, "created");
-      this.hostId = builder.hostId;
-      this.accessIPv4 = builder.accessIPv4;
-      this.accessIPv6 = builder.accessIPv6;
-      this.status = checkNotNull(builder.status, "status");
-      this.configDrive = builder.configDrive;
-      this.image = checkNotNull(builder.image, "image");
-      this.flavor = checkNotNull(builder.flavor, "flavor");
-      this.metadata = Maps.newHashMap(builder.metadata);
-      this.addresses = Multimaps2.toOldSchool(ImmutableMultimap.copyOf(checkNotNull(builder.addresses, "addresses")));
-      this.keyName = builder.keyName;
-      this.extendedStatus = Optional.fromNullable(builder.extendedStatus);
-      this.extendedAttributes = Optional.fromNullable(builder.extendedAttributes);
-      this.diskConfig = builder.diskConfig == null ? Optional.<String>absent() : Optional.of(builder.diskConfig);
+   private final String uuid;
+   @Named("tenant_id")
+   private final String tenantId;
+   @Named("user_id")
+   private final String userId;
+   private final Date updated;
+   private final Date created;
+   private final String hostId;
+   private final String accessIPv4;
+   private final String accessIPv6;
+   private final Server.Status status;
+   private final Resource image;
+   private final Resource flavor;
+   @Named("key_name")
+   private final String keyName;
+   @Named("config_drive")
+   private final String configDrive;
+   private final Multimap<String, Address> addresses;
+   private final Map<String, String> metadata;
+   private final Optional<ServerExtendedStatus> extendedStatus;
+   private final Optional<ServerExtendedAttributes> extendedAttributes;
+   @Named("OS-DCF:diskConfig")
+   private final Optional<String> diskConfig;
+
+   @ConstructorProperties({
+         "id", "name", "links", "uuid", "tenant_id", "user_id", "updated", "created", "hostId", "accessIPv4", "accessIPv6", "status", "image", "flavor", "key_name", "config_drive", "addresses", "metadata", "extendedStatus", "extendedAttributes", "OS-DCF:diskConfig"
+   })
+   protected Server(String id, @Nullable String name, java.util.Set<Link> links, @Nullable String uuid, String tenantId,
+                    String userId, @Nullable Date updated, Date created, @Nullable String hostId, @Nullable String accessIPv4,
+                    @Nullable String accessIPv6, Server.Status status, Resource image, Resource flavor, @Nullable String keyName,
+                    @Nullable String configDrive, Multimap<String, Address> addresses, Map<String, String> metadata,
+                    @Nullable ServerExtendedStatus extendedStatus, @Nullable ServerExtendedAttributes extendedAttributes,
+                    @Nullable String diskConfig) {
+      super(id, name, links);
+      this.uuid = uuid;
+      this.tenantId = checkNotNull(tenantId, "tenantId");
+      this.userId = checkNotNull(userId, "userId");
+      this.updated = updated;
+      this.created = checkNotNull(created, "created");
+      this.hostId = Strings.emptyToNull(hostId);
+      this.accessIPv4 = Strings.emptyToNull(accessIPv4);
+      this.accessIPv6 = Strings.emptyToNull(accessIPv6);
+      this.status = checkNotNull(status, "status");
+      this.image = checkNotNull(image, "image");
+      this.flavor = checkNotNull(flavor, "flavor");
+      this.keyName = Strings.emptyToNull(keyName);
+      this.configDrive = Strings.emptyToNull(configDrive);
+      this.addresses = ImmutableMultimap.copyOf(checkNotNull(addresses, "addresses"));
+      this.metadata = ImmutableMap.copyOf(checkNotNull(metadata, "metadata"));
+      this.extendedStatus = Optional.fromNullable(extendedStatus);
+      this.extendedAttributes = Optional.fromNullable(extendedAttributes);
+      this.diskConfig = Optional.fromNullable(diskConfig);
    }
 
    /**
     * only present until the id is in uuid form
-    * 
+    *
     * @return uuid, if id is an integer val
     */
    @Nullable
@@ -362,6 +355,7 @@
       return this.userId;
    }
 
+   @Nullable
    public Date getUpdated() {
       return this.updated;
    }
@@ -375,17 +369,17 @@
     */
    @Nullable
    public String getHostId() {
-      return Strings.emptyToNull(this.hostId);
+      return this.hostId;
    }
 
    @Nullable
    public String getAccessIPv4() {
-      return Strings.emptyToNull(this.accessIPv4);
+      return this.accessIPv4;
    }
 
    @Nullable
    public String getAccessIPv6() {
-      return Strings.emptyToNull(this.accessIPv6);
+      return this.accessIPv6;
    }
 
    public Status getStatus() {
@@ -394,7 +388,7 @@
 
    @Nullable
    public String getConfigDrive() {
-      return Strings.emptyToNull(this.configDrive);
+      return this.configDrive;
    }
 
    public Resource getImage() {
@@ -406,15 +400,14 @@
    }
 
    public Map<String, String> getMetadata() {
-      // in case this was assigned in gson
-      return ImmutableMap.copyOf(Maps.filterValues(this.metadata, Predicates.notNull()));
+      return metadata;
    }
 
    /**
     * @return the ip addresses assigned to the server
     */
    public Multimap<String, Address> getAddresses() {
-      return Multimaps2.fromOldSchool(addresses);
+      return addresses;
    }
 
    /**
@@ -426,7 +419,6 @@
       return keyName;
    }
 
-
    /**
     * Retrieves the extended server status fields (alias "OS-EXT-STS")
     * <p/>
@@ -468,11 +460,12 @@
 
    @Override
    protected ToStringHelper string() {
-      return super.string().add("uuid", uuid).add("tenantId", tenantId).add(
-            "userId", userId).add("hostId", getHostId()).add("updated", updated).add("created", created).add(
-            "accessIPv4", getAccessIPv4()).add("accessIPv6", getAccessIPv6()).add("status", status).add(
-            "configDrive", getConfigDrive()).add("image", image).add("flavor", flavor).add("metadata", metadata)
-            .add("addresses", getAddresses()).add("diskConfig", diskConfig)
-            .add("extendedStatus", extendedStatus).add("extendedAttributes", extendedAttributes);
+      return super.string()
+            .add("uuid", uuid).add("tenantId", tenantId).add("userId", userId).add("updated", updated).add("created", created)
+            .add("hostId", hostId).add("accessIPv4", accessIPv4).add("accessIPv6", accessIPv6).add("status", status).add("image", image)
+            .add("flavor", flavor).add("keyName", keyName).add("configDrive", configDrive).add("addresses", addresses)
+            .add("metadata", metadata).add("extendedStatus", extendedStatus).add("extendedAttributes", extendedAttributes)
+            .add("diskConfig", diskConfig);
    }
+
 }
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/ServerCreated.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/ServerCreated.java
index fe5d389..81dcc78 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/ServerCreated.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/ServerCreated.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -18,8 +18,16 @@
  */
 package org.jclouds.openstack.nova.v2_0.domain;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.beans.ConstructorProperties;
+import java.util.Set;
+
+import org.jclouds.javax.annotation.Nullable;
+import org.jclouds.openstack.v2_0.domain.Link;
 import org.jclouds.openstack.v2_0.domain.Resource;
 
+import com.google.common.base.Objects;
 import com.google.common.base.Objects.ToStringHelper;
 
 /**
@@ -27,23 +35,23 @@
  * 
  * @author Adam Lowe
  * @see <a href=
- *      "http://docs.openstack.org/api/openstack-compute/1.1/content/Get_Server_Details-d1e2623.html"
- *      />
- */
+      "http://docs.openstack.org/api/openstack-compute/1.1/content/Get_Server_Details-d1e2623.html"
+      />
+*/
 public class ServerCreated extends Resource {
 
-   public static Builder<?> builder() {
+   public static Builder<?> builder() { 
       return new ConcreteBuilder();
    }
-
-   public Builder<?> toBuilder() {
+   
+   public Builder<?> toBuilder() { 
       return new ConcreteBuilder().fromServerCreated(this);
    }
 
    public static abstract class Builder<T extends Builder<T>> extends Resource.Builder<T>  {
-      private String adminPass;
-
-      /**
+      protected String adminPass;
+   
+      /** 
        * @see ServerCreated#getAdminPass()
        */
       public T adminPass(String adminPass) {
@@ -51,12 +59,13 @@
          return self();
       }
 
-      public T fromServerCreated(ServerCreated in) {
-         return super.fromResource(in).adminPass(in.getAdminPass());
-      }
-
       public ServerCreated build() {
-         return new ServerCreated(this);
+         return new ServerCreated(id, name, links, adminPass);
+      }
+      
+      public T fromServerCreated(ServerCreated in) {
+         return super.fromResource(in)
+                  .adminPass(in.getAdminPass());
       }
    }
 
@@ -66,31 +75,40 @@
          return this;
       }
    }
-   
-   protected ServerCreated() {
-      // we want serializers like Gson to work w/o using sun.misc.Unsafe,
-      // prohibited in GAE. This also implies fields are not final.
-      // see http://code.google.com/p/jclouds/issues/detail?id=925
-   }
-  
-   private String adminPass;
 
-   protected ServerCreated(Builder<?> builder) {
-      super(builder);
-      this.adminPass = builder.adminPass;
+   private final String adminPass;
+
+   @ConstructorProperties({
+      "id", "name", "links", "adminPass"
+   })
+   protected ServerCreated(String id, @Nullable String name, Set<Link> links, String adminPass) {
+      super(id, name, links);
+      this.adminPass = checkNotNull(adminPass, "adminPass");
    }
 
    /**
     * @return the administrative password for this server. Note: this is not available in Server responses.
     */
    public String getAdminPass() {
-      return adminPass;
+      return this.adminPass;
    }
 
-   // hashCode/equals from super is ok
-
    @Override
-   protected ToStringHelper string() {
-      return super.string().add("adminPass", adminPass);
+   public int hashCode() {
+      return Objects.hashCode(adminPass);
    }
+
+   @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      ServerCreated that = ServerCreated.class.cast(obj);
+      return super.equals(that) && Objects.equal(this.adminPass, that.adminPass);
+   }
+   
+   protected ToStringHelper string() {
+      return super.string()
+            .add("adminPass", adminPass);
+   }
+   
 }
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/ServerExtendedAttributes.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/ServerExtendedAttributes.java
index 66deac7..f3541b1 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/ServerExtendedAttributes.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/ServerExtendedAttributes.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -18,39 +18,45 @@
  */
 package org.jclouds.openstack.nova.v2_0.domain;
 
+import java.beans.ConstructorProperties;
+
+import javax.inject.Named;
+
+import org.jclouds.javax.annotation.Nullable;
+
 import com.google.common.base.Objects;
 import com.google.common.base.Objects.ToStringHelper;
-import com.google.gson.annotations.SerializedName;
 
 /**
  * Additional attributes delivered by Extended Server Attributes extension (alias "OS-EXT-SRV-ATTR")
- *
+ * 
  * @author Adam Lowe
  * @see <a href=
- *        "http://nova.openstack.org/api/nova.api.openstack.compute.contrib.extended_server_attributes.html"
- *       />
+        "http://nova.openstack.org/api/nova.api.openstack.compute.contrib.extended_server_attributes.html"
+       />
  * @see org.jclouds.openstack.nova.v2_0.features.ExtensionClient#getExtensionByAlias
- * @see org.jclouds.openstack.nova.v1_1.extensions.ExtensionNamespaces#EXTENDED_STATUS (extended status?)
- */
+ * @see org.jclouds.openstack.nova.v2_0.extensions.ExtensionNamespaces#EXTENDED_STATUS
+*/
 public class ServerExtendedAttributes {
-   public static final String PREFIX = "OS-EXT-SRV-ATTR:";
 
-   public static Builder<?> builder() {
+   public static String PREFIX;
+
+   public static Builder<?> builder() { 
       return new ConcreteBuilder();
    }
-
-   public Builder<?> toBuilder() {
-      return new ConcreteBuilder().fromServerExtraAttributes(this);
+   
+   public Builder<?> toBuilder() { 
+      return new ConcreteBuilder().fromServerExtendedAttributes(this);
    }
 
    public static abstract class Builder<T extends Builder<T>>  {
       protected abstract T self();
 
-      private String instanceName;
-      private String hostName;
-      private String hypervisorHostName;
-
-      /**
+      protected String instanceName;
+      protected String hostName;
+      protected String hypervisorHostName;
+   
+      /** 
        * @see ServerExtendedAttributes#getInstanceName()
        */
       public T instanceName(String instanceName) {
@@ -58,7 +64,7 @@
          return self();
       }
 
-      /**
+      /** 
        * @see ServerExtendedAttributes#getHostName()
        */
       public T hostName(String hostName) {
@@ -66,23 +72,23 @@
          return self();
       }
 
-      /**
+      /** 
        * @see ServerExtendedAttributes#getHypervisorHostName()
        */
-      public T hypervisorHostame(String hypervisorHostName) {
+      public T hypervisorHostName(String hypervisorHostName) {
          this.hypervisorHostName = hypervisorHostName;
          return self();
       }
 
       public ServerExtendedAttributes build() {
-         return new ServerExtendedAttributes(this);
+         return new ServerExtendedAttributes(instanceName, hostName, hypervisorHostName);
       }
-
-      public T fromServerExtraAttributes(ServerExtendedAttributes in) {
+      
+      public T fromServerExtendedAttributes(ServerExtendedAttributes in) {
          return this
-               .instanceName(in.getInstanceName())
-               .hostName(in.getHostName())
-               .hypervisorHostame(in.getHypervisorHostName());
+                  .instanceName(in.getInstanceName())
+                  .hostName(in.getHostName())
+                  .hypervisorHostName(in.getHypervisorHostName());
       }
    }
 
@@ -92,34 +98,34 @@
          return this;
       }
    }
-   
-   protected ServerExtendedAttributes() {
-      // we want serializers like Gson to work w/o using sun.misc.Unsafe,
-      // prohibited in GAE. This also implies fields are not final.
-      // see http://code.google.com/p/jclouds/issues/detail?id=925
-   }
-  
-   @SerializedName(value=PREFIX + "instance_name")
-   private String instanceName;
-   @SerializedName(value=PREFIX + "host")
-   private String hostName;
-   @SerializedName(value=PREFIX + "hypervisor_hostname")
-   private String hypervisorHostName;
 
-   protected ServerExtendedAttributes(Builder<?> builder) {
-      this.instanceName = builder.instanceName;
-      this.hostName = builder.hostName;
-      this.hypervisorHostName = builder.hypervisorHostName;
+   @Named("OS-EXT-SRV-ATTR:instance_name")
+   private final String instanceName;
+   @Named("OS-EXT-SRV-ATTR:host")
+   private final String hostName;
+   @Named("OS-EXT-SRV-ATTR:hypervisor_hostname")
+   private final String hypervisorHostName;
+
+   @ConstructorProperties({
+      "OS-EXT-SRV-ATTR:instance_name", "OS-EXT-SRV-ATTR:host", "OS-EXT-SRV-ATTR:hypervisor_hostname"
+   })
+   protected ServerExtendedAttributes(@Nullable String instanceName, @Nullable String hostName, @Nullable String hypervisorHostName) {
+      this.instanceName = instanceName;
+      this.hostName = hostName;
+      this.hypervisorHostName = hypervisorHostName;
    }
 
+   @Nullable
    public String getInstanceName() {
       return this.instanceName;
    }
 
+   @Nullable
    public String getHostName() {
       return this.hostName;
    }
 
+   @Nullable
    public String getHypervisorHostName() {
       return this.hypervisorHostName;
    }
@@ -135,20 +141,18 @@
       if (obj == null || getClass() != obj.getClass()) return false;
       ServerExtendedAttributes that = ServerExtendedAttributes.class.cast(obj);
       return Objects.equal(this.instanceName, that.instanceName)
-            && Objects.equal(this.hostName, that.hostName)
-            && Objects.equal(this.hypervisorHostName, that.hypervisorHostName);
+               && Objects.equal(this.hostName, that.hostName)
+               && Objects.equal(this.hypervisorHostName, that.hypervisorHostName);
    }
-
+   
    protected ToStringHelper string() {
-      return Objects.toStringHelper("")
-            .add("instanceName", instanceName)
-            .add("hostName", hostName)
-            .add("hypervisorHostName", hypervisorHostName);
+      return Objects.toStringHelper(this)
+            .add("instanceName", instanceName).add("hostName", hostName).add("hypervisorHostName", hypervisorHostName);
    }
-
+   
    @Override
    public String toString() {
       return string().toString();
    }
 
-}
\ No newline at end of file
+}
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/ServerExtendedStatus.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/ServerExtendedStatus.java
index 7559cb8..3e5baec 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/ServerExtendedStatus.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/ServerExtendedStatus.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -18,41 +18,45 @@
  */
 package org.jclouds.openstack.nova.v2_0.domain;
 
+import java.beans.ConstructorProperties;
+
+import javax.inject.Named;
+
 import org.jclouds.javax.annotation.Nullable;
 
 import com.google.common.base.Objects;
 import com.google.common.base.Objects.ToStringHelper;
-import com.google.gson.annotations.SerializedName;
 
 /**
  * Additional attributes delivered by Extended Server Status extension (alias "OS-EXT-STS")
- *
+ * 
  * @author Adam Lowe
  * @see <a href=
- *       "http://nova.openstack.org/api/nova.api.openstack.compute.contrib.extended_status.html"
- *       />
+       "http://nova.openstack.org/api/nova.api.openstack.compute.contrib.extended_status.html"
+       />
  * @see org.jclouds.openstack.nova.v2_0.features.ExtensionClient#getExtensionByAlias
  * @see org.jclouds.openstack.nova.v1_1.extensions.ExtensionNamespaces#EXTENDED_STATUS (extended status?)
- */
+*/
 public class ServerExtendedStatus {
-   public static final String PREFIX = "OS-EXT-STS:";
 
-   public static Builder<?> builder() {
+   public static String PREFIX;
+
+   public static Builder<?> builder() { 
       return new ConcreteBuilder();
    }
-
-   public Builder<?> toBuilder() {
+   
+   public Builder<?> toBuilder() { 
       return new ConcreteBuilder().fromServerExtendedStatus(this);
    }
 
    public static abstract class Builder<T extends Builder<T>>  {
       protected abstract T self();
 
-      private String taskState;
-      private String vmState;
-      private int powerState = Integer.MIN_VALUE;
-
-      /**
+      protected String taskState;
+      protected String vmState;
+      protected int powerState;
+   
+      /** 
        * @see ServerExtendedStatus#getTaskState()
        */
       public T taskState(String taskState) {
@@ -60,7 +64,7 @@
          return self();
       }
 
-      /**
+      /** 
        * @see ServerExtendedStatus#getVmState()
        */
       public T vmState(String vmState) {
@@ -68,7 +72,7 @@
          return self();
       }
 
-      /**
+      /** 
        * @see ServerExtendedStatus#getPowerState()
        */
       public T powerState(int powerState) {
@@ -77,14 +81,14 @@
       }
 
       public ServerExtendedStatus build() {
-         return new ServerExtendedStatus(this);
+         return new ServerExtendedStatus(taskState, vmState, powerState);
       }
-
+      
       public T fromServerExtendedStatus(ServerExtendedStatus in) {
          return this
-               .taskState(in.getTaskState())
-               .vmState(in.getVmState())
-               .powerState(in.getPowerState());
+                  .taskState(in.getTaskState())
+                  .vmState(in.getVmState())
+                  .powerState(in.getPowerState());
       }
    }
 
@@ -94,26 +98,23 @@
          return this;
       }
    }
-   
-   protected ServerExtendedStatus() {
-      // we want serializers like Gson to work w/o using sun.misc.Unsafe,
-      // prohibited in GAE. This also implies fields are not final.
-      // see http://code.google.com/p/jclouds/issues/detail?id=925
-   }
-  
-   @SerializedName(value=PREFIX + "task_state")
-   private String taskState;
-   @SerializedName(value=PREFIX + "vm_state")
-   private String vmState;
-   @SerializedName(value=PREFIX + "power_state")
-   private int powerState = Integer.MIN_VALUE;
 
-   protected ServerExtendedStatus(Builder<?> builder) {
-      this.taskState = builder.taskState;
-      this.vmState = builder.vmState;
-      this.powerState = builder.powerState;
+   @Named("OS-EXT-STS:task_state")
+   private final String taskState;
+   @Named("OS-EXT-STS:vm_state")
+   private final String vmState;
+   @Named("OS-EXT-STS:power_state")
+   private final int powerState;
+
+   @ConstructorProperties({
+      "OS-EXT-STS:task_state", "OS-EXT-STS:vm_state", "OS-EXT-STS:power_state"
+   })
+   protected ServerExtendedStatus(@Nullable String taskState, @Nullable String vmState, int powerState) {
+      this.taskState = taskState;
+      this.vmState = vmState;
+      this.powerState = powerState;
    }
-   
+
    @Nullable
    public String getTaskState() {
       return this.taskState;
@@ -139,22 +140,18 @@
       if (obj == null || getClass() != obj.getClass()) return false;
       ServerExtendedStatus that = ServerExtendedStatus.class.cast(obj);
       return Objects.equal(this.taskState, that.taskState)
-            && Objects.equal(this.vmState, that.vmState)
-            && Objects.equal(this.powerState, that.powerState)
-            ;
+               && Objects.equal(this.vmState, that.vmState)
+               && Objects.equal(this.powerState, that.powerState);
    }
-
+   
    protected ToStringHelper string() {
-      return Objects.toStringHelper("")
-            .add("taskState", taskState)
-            .add("vmState", vmState)
-            .add("powerState", powerState)
-            ;
+      return Objects.toStringHelper(this)
+            .add("taskState", taskState).add("vmState", vmState).add("powerState", powerState);
    }
-
+   
    @Override
    public String toString() {
       return string().toString();
    }
 
-}
\ No newline at end of file
+}
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/ServerWithSecurityGroups.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/ServerWithSecurityGroups.java
index 87f8630..02ad5cf 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/ServerWithSecurityGroups.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/ServerWithSecurityGroups.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -20,51 +20,65 @@
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
-import java.util.Collections;
+import java.beans.ConstructorProperties;
+import java.util.Date;
+import java.util.Map;
 import java.util.Set;
 
+import javax.inject.Named;
+
+import org.jclouds.javax.annotation.Nullable;
+import org.jclouds.openstack.v2_0.domain.Link;
+import org.jclouds.openstack.v2_0.domain.Resource;
+
 import com.google.common.base.Objects;
 import com.google.common.base.Objects.ToStringHelper;
 import com.google.common.collect.ImmutableSet;
-import com.google.gson.annotations.SerializedName;
+import com.google.common.collect.Multimap;
 
 /**
  * Extended server returned by ServerWithSecurityGroupsClient
- *
+ * 
  * @author Adam Lowe
  * @see <a href=
 "http://docs.openstack.org/api/openstack-compute/1.1/content/Get_Server_Details-d1e2623.html"
 />
- */
+*/
 public class ServerWithSecurityGroups extends Server {
 
-   public static Builder<?> builder() {
+   public static Builder<?> builder() { 
       return new ConcreteBuilder();
    }
-
-   public Builder<?> toBuilder() {
+   
+   public Builder<?> toBuilder() { 
       return new ConcreteBuilder().fromServerWithSecurityGroups(this);
    }
 
    public static abstract class Builder<T extends Builder<T>> extends Server.Builder<T>  {
-      private Set<String> securityGroupNames = ImmutableSet.of();
-
-      /**
+      protected Set<String> securityGroupNames = ImmutableSet.of();
+   
+      /** 
        * @see ServerWithSecurityGroups#getSecurityGroupNames()
        */
       public T securityGroupNames(Set<String> securityGroupNames) {
-         this.securityGroupNames = securityGroupNames;
+         this.securityGroupNames = ImmutableSet.copyOf(checkNotNull(securityGroupNames, "securityGroupNames"));      
          return self();
       }
 
+      public T securityGroupNames(String... in) {
+         return securityGroupNames(ImmutableSet.copyOf(in));
+      }
+
       public ServerWithSecurityGroups build() {
-         return new ServerWithSecurityGroups(this);
+         return new ServerWithSecurityGroups(id, name, links, uuid, tenantId, userId, updated, created, hostId,
+               accessIPv4, accessIPv6, status, image, flavor, keyName, configDrive, addresses,
+               metadata, extendedStatus, extendedAttributes, diskConfig, securityGroupNames);
       }
-
+      
       public T fromServerWithSecurityGroups(ServerWithSecurityGroups in) {
-         return super.fromServer(in).securityGroupNames(in.getSecurityGroupNames());
+         return super.fromServer(in)
+                  .securityGroupNames(in.getSecurityGroupNames());
       }
-
    }
 
    private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
@@ -73,25 +87,26 @@
          return this;
       }
    }
-   
-   protected ServerWithSecurityGroups() {
-      // we want serializers like Gson to work w/o using sun.misc.Unsafe,
-      // prohibited in GAE. This also implies fields are not final.
-      // see http://code.google.com/p/jclouds/issues/detail?id=925
-   }
-  
-   @SerializedName(value="security_groups")
-   private Set<String> securityGroupNames = ImmutableSet.of();
 
-   protected ServerWithSecurityGroups(Builder<?> builder) {
-      super(builder);
-      this.securityGroupNames = ImmutableSet.copyOf(checkNotNull(builder.securityGroupNames, "securityGroupNames"));
+   @Named("security_groups")
+   private final Set<String> securityGroupNames;
+
+   @ConstructorProperties({
+      "id", "name", "links", "uuid", "tenant_id", "user_id", "updated", "created", "hostId", "accessIPv4", "accessIPv6", "status", "image", "flavor", "key_name", "config_drive", "addresses", "metadata", "extendedStatus", "extendedAttributes", "OS-DCF:diskConfig", "security_groups"
+   })
+   protected ServerWithSecurityGroups(String id, @Nullable String name, Set<Link> links, @Nullable String uuid,
+                                      String tenantId, String userId, Date updated, Date created, @Nullable String hostId,
+                                      @Nullable String accessIPv4, @Nullable String accessIPv6, Server.Status status, Resource image,
+                                      Resource flavor, @Nullable String keyName, @Nullable String configDrive,
+                                      Multimap<String, Address> addresses, Map<String, String> metadata, 
+                                      @Nullable ServerExtendedStatus extendedStatus, @Nullable ServerExtendedAttributes extendedAttributes,
+                                      @Nullable String diskConfig, Set<String> securityGroupNames) {
+      super(id, name, links, uuid, tenantId, userId, updated, created, hostId, accessIPv4, accessIPv6, status, image, flavor, keyName, configDrive, addresses, metadata, extendedStatus, extendedAttributes, diskConfig);
+      this.securityGroupNames = ImmutableSet.copyOf(checkNotNull(securityGroupNames, "securityGroupNames"));      
    }
 
-   /**
-    */
    public Set<String> getSecurityGroupNames() {
-      return Collections.unmodifiableSet(this.securityGroupNames);
+      return this.securityGroupNames;
    }
 
    @Override
@@ -106,10 +121,10 @@
       ServerWithSecurityGroups that = ServerWithSecurityGroups.class.cast(obj);
       return super.equals(that) && Objects.equal(this.securityGroupNames, that.securityGroupNames);
    }
-
+   
    protected ToStringHelper string() {
       return super.string()
             .add("securityGroupNames", securityGroupNames);
    }
-
-}
\ No newline at end of file
+   
+}
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/SimpleServerUsage.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/SimpleServerUsage.java
index 0320dbd..f2a95aa 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/SimpleServerUsage.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/SimpleServerUsage.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -20,137 +20,172 @@
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import java.beans.ConstructorProperties;
 import java.util.Date;
 
+import javax.inject.Named;
+
 import org.jclouds.javax.annotation.Nullable;
 
 import com.google.common.base.Objects;
 import com.google.common.base.Objects.ToStringHelper;
-import com.google.gson.annotations.SerializedName;
 
 /**
  * Information the SimpleTenantUsage extension return data about each Server
- *
+ * 
  * @author Adam Lowe
- */
+*/
 public class SimpleServerUsage {
-   
+
+   /**
+    */
    public static enum Status {
-
+      
       UNRECOGNIZED, ACTIVE;
-
+      
       public String value() {
-         return name();
+      return name();
       }
-
+      
       public static Status fromValue(String v) {
-         try {
-            return valueOf(v.toUpperCase());
-         } catch (IllegalArgumentException e) {
-            return UNRECOGNIZED;
-         }
+      try {
+      return valueOf(v.toUpperCase());
+      } catch (IllegalArgumentException e) {
+      return UNRECOGNIZED;
       }
-
+      }
+      
    }
-   
-   public static Builder<?> builder() {
+
+   public static Builder<?> builder() { 
       return new ConcreteBuilder();
    }
-
-   public Builder<?> toBuilder() {
+   
+   public Builder<?> toBuilder() { 
       return new ConcreteBuilder().fromSimpleServerUsage(this);
    }
 
    public static abstract class Builder<T extends Builder<T>>  {
-      private String instanceName;
-      private double hours;
-      private double flavorMemoryMb;
-      private double flavorLocalGb;
-      private double flavorVcpus;
-      private String tenantId;
-      private String flavorName;
-      private Date instanceCreated;
-      private Date instanceTerminiated;
-      private Status instanceStatus;
-      private long uptime;
-
       protected abstract T self();
 
+      protected String instanceName;
+      protected double hours;
+      protected double flavorMemoryMb;
+      protected double flavorLocalGb;
+      protected double flavorVcpus;
+      protected String tenantId;
+      protected String flavorName;
+      protected Date instanceCreated;
+      protected Date instanceTerminiated;
+      protected SimpleServerUsage.Status instanceStatus;
+      protected long uptime;
+   
+      /** 
+       * @see SimpleServerUsage#getInstanceName()
+       */
       public T instanceName(String instanceName) {
          this.instanceName = instanceName;
          return self();
       }
 
+      /** 
+       * @see SimpleServerUsage#getHours()
+       */
       public T hours(double hours) {
          this.hours = hours;
          return self();
       }
 
+      /** 
+       * @see SimpleServerUsage#getFlavorMemoryMb()
+       */
       public T flavorMemoryMb(double flavorMemoryMb) {
          this.flavorMemoryMb = flavorMemoryMb;
          return self();
       }
 
+      /** 
+       * @see SimpleServerUsage#getFlavorLocalGb()
+       */
       public T flavorLocalGb(double flavorLocalGb) {
          this.flavorLocalGb = flavorLocalGb;
          return self();
       }
 
+      /** 
+       * @see SimpleServerUsage#getFlavorVcpus()
+       */
       public T flavorVcpus(double flavorVcpus) {
          this.flavorVcpus = flavorVcpus;
          return self();
       }
 
+      /** 
+       * @see SimpleServerUsage#getTenantId()
+       */
       public T tenantId(String tenantId) {
          this.tenantId = tenantId;
          return self();
       }
 
+      /** 
+       * @see SimpleServerUsage#getFlavorName()
+       */
       public T flavorName(String flavorName) {
          this.flavorName = flavorName;
          return self();
       }
 
+      /** 
+       * @see SimpleServerUsage#getInstanceCreated()
+       */
       public T instanceCreated(Date instanceCreated) {
          this.instanceCreated = instanceCreated;
          return self();
       }
 
+      /** 
+       * @see SimpleServerUsage#getInstanceTerminiated()
+       */
       public T instanceTerminiated(Date instanceTerminiated) {
          this.instanceTerminiated = instanceTerminiated;
          return self();
       }
 
-      public T instanceStatus(Status instanceStatus) {
+      /** 
+       * @see SimpleServerUsage#getInstanceStatus()
+       */
+      public T instanceStatus(SimpleServerUsage.Status instanceStatus) {
          this.instanceStatus = instanceStatus;
          return self();
       }
 
+      /** 
+       * @see SimpleServerUsage#getUptime()
+       */
       public T uptime(long uptime) {
          this.uptime = uptime;
          return self();
       }
 
       public SimpleServerUsage build() {
-         return new SimpleServerUsage(this);
+         return new SimpleServerUsage(instanceName, hours, flavorMemoryMb, flavorLocalGb, flavorVcpus, tenantId, flavorName, instanceCreated, instanceTerminiated, instanceStatus, uptime);
       }
-
-
+      
       public T fromSimpleServerUsage(SimpleServerUsage in) {
          return this
-               .instanceName(in.getInstanceName())
-               .flavorMemoryMb(in.getFlavorMemoryMb())
-               .flavorLocalGb(in.getFlavorLocalGb())
-               .flavorVcpus(in.getFlavorVcpus())
-               .tenantId(in.getTenantId())
-               .flavorName(in.getFlavorName())
-               .instanceCreated(in.getInstanceCreated())
-               .instanceTerminiated(in.getInstanceTerminiated())
-               .instanceStatus(in.getInstanceStatus())
-               .uptime(in.getUptime())
-               ;
+                  .instanceName(in.getInstanceName())
+                  .hours(in.getHours())
+                  .flavorMemoryMb(in.getFlavorMemoryMb())
+                  .flavorLocalGb(in.getFlavorLocalGb())
+                  .flavorVcpus(in.getFlavorVcpus())
+                  .tenantId(in.getTenantId())
+                  .flavorName(in.getFlavorName())
+                  .instanceCreated(in.getInstanceCreated())
+                  .instanceTerminiated(in.getInstanceTerminiated())
+                  .instanceStatus(in.getInstanceStatus())
+                  .uptime(in.getUptime());
       }
-
    }
 
    private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
@@ -159,112 +194,93 @@
          return this;
       }
    }
-   
-   protected SimpleServerUsage() {
-      // we want serializers like Gson to work w/o using sun.misc.Unsafe,
-      // prohibited in GAE. This also implies fields are not final.
-      // see http://code.google.com/p/jclouds/issues/detail?id=925
-   }
-  
-   @SerializedName("name")
-   private String instanceName;
-   private double hours;
-   @SerializedName("memory_mb")
-   private double flavorMemoryMb;
-   @SerializedName("local_gb")
-   private double flavorLocalGb;
-   @SerializedName("vcpus")
-   private double flavorVcpus;
-   @SerializedName("tenant_id")
-   private String tenantId;
-   @SerializedName("flavor")
-   private String flavorName;
-   @SerializedName("started_at")
-   private Date instanceCreated;
-   @SerializedName("ended_at")
-   private Date instanceTerminiated;
-   @SerializedName("state")
-   private Status instanceStatus;
-   private long uptime;
 
-   private SimpleServerUsage(Builder<?> builder) {
-      this.instanceName = checkNotNull(builder.instanceName, "instanceName");
-      this.hours = builder.hours;
-      this.flavorMemoryMb = builder.flavorMemoryMb;
-      this.flavorLocalGb = builder.flavorLocalGb;
-      this.flavorVcpus =  builder.flavorVcpus;
-      this.tenantId = checkNotNull(builder.tenantId, "tenantId");
-      this.flavorName = checkNotNull(builder.flavorName, "flavorName");
-      this.instanceCreated = builder.instanceCreated; //checkNotNull(builder.instanceCreated, "instanceCreated");
-      this.instanceTerminiated = builder.instanceTerminiated;
-      this.instanceStatus = checkNotNull(builder.instanceStatus, "instanceStatus");
-      this.uptime = checkNotNull(builder.uptime, "uptime");
+   @Named("name")
+   private final String instanceName;
+   private final double hours;
+   @Named("memory_mb")
+   private final double flavorMemoryMb;
+   @Named("local_gb")
+   private final double flavorLocalGb;
+   @Named("vcpus")
+   private final double flavorVcpus;
+   @Named("tenant_id")
+   private final String tenantId;
+   @Named("flavor")
+   private final String flavorName;
+   @Named("started_at")
+   private final Date instanceCreated;
+   @Named("ended_at")
+   private final Date instanceTerminiated;
+   @Named("state")
+   private final SimpleServerUsage.Status instanceStatus;
+   private final long uptime;
+
+   @ConstructorProperties({
+      "name", "hours", "memory_mb", "local_gb", "vcpus", "tenant_id", "flavor", "started_at", "ended_at", "state", "uptime"
+   })
+   protected SimpleServerUsage(String instanceName, double hours, double flavorMemoryMb, double flavorLocalGb, double flavorVcpus, String tenantId, String flavorName, Date instanceCreated, @Nullable Date instanceTerminiated, SimpleServerUsage.Status instanceStatus, long uptime) {
+      this.instanceName = checkNotNull(instanceName, "instanceName");
+      this.hours = hours;
+      this.flavorMemoryMb = flavorMemoryMb;
+      this.flavorLocalGb = flavorLocalGb;
+      this.flavorVcpus = flavorVcpus;
+      this.tenantId = checkNotNull(tenantId, "tenantId");
+      this.flavorName = checkNotNull(flavorName, "flavorName");
+      this.instanceCreated = checkNotNull(instanceCreated, "instanceCreated");
+      this.instanceTerminiated = instanceTerminiated;
+      this.instanceStatus = checkNotNull(instanceStatus, "instanceStatus");
+      this.uptime = uptime;
    }
 
-   /**
-    */
    public String getInstanceName() {
       return this.instanceName;
    }
 
-   /**
-    */
+   public double getHours() {
+      return this.hours;
+   }
+
    public double getFlavorMemoryMb() {
       return this.flavorMemoryMb;
    }
 
-   /**
-    */
    public double getFlavorLocalGb() {
       return this.flavorLocalGb;
    }
 
-   /**
-    */
    public double getFlavorVcpus() {
       return this.flavorVcpus;
    }
 
-   /**
-    */
    public String getTenantId() {
       return this.tenantId;
    }
 
-   /**
-    */
    public String getFlavorName() {
       return this.flavorName;
    }
 
-   /**
-    */
    public Date getInstanceCreated() {
       return this.instanceCreated;
    }
 
-   /**
-    */
    @Nullable
    public Date getInstanceTerminiated() {
       return this.instanceTerminiated;
    }
 
-   /**
-    */
-   public Status getInstanceStatus() {
+   public SimpleServerUsage.Status getInstanceStatus() {
       return this.instanceStatus;
    }
 
-   /**
-    */
    public long getUptime() {
       return this.uptime;
    }
 
    @Override
    public int hashCode() {
-      return Objects.hashCode(instanceName, flavorMemoryMb, flavorLocalGb, flavorVcpus, tenantId, flavorName, instanceCreated, instanceTerminiated, instanceStatus, uptime);
+      return Objects.hashCode(instanceName, hours, flavorMemoryMb, flavorLocalGb, flavorVcpus, tenantId, flavorName, instanceCreated, instanceTerminiated, instanceStatus, uptime);
    }
 
    @Override
@@ -273,33 +289,23 @@
       if (obj == null || getClass() != obj.getClass()) return false;
       SimpleServerUsage that = SimpleServerUsage.class.cast(obj);
       return Objects.equal(this.instanceName, that.instanceName)
-            && Objects.equal(this.flavorMemoryMb, that.flavorMemoryMb)
-            && Objects.equal(this.flavorLocalGb, that.flavorLocalGb)
-            && Objects.equal(this.flavorVcpus, that.flavorVcpus)
-            && Objects.equal(this.tenantId, that.tenantId)
-            && Objects.equal(this.flavorName, that.flavorName)
-            && Objects.equal(this.instanceCreated, that.instanceCreated)
-            && Objects.equal(this.instanceTerminiated, that.instanceTerminiated)
-            && Objects.equal(this.instanceStatus, that.instanceStatus)
-            && Objects.equal(this.uptime, that.uptime)
-            ;
+               && Objects.equal(this.hours, that.hours)
+               && Objects.equal(this.flavorMemoryMb, that.flavorMemoryMb)
+               && Objects.equal(this.flavorLocalGb, that.flavorLocalGb)
+               && Objects.equal(this.flavorVcpus, that.flavorVcpus)
+               && Objects.equal(this.tenantId, that.tenantId)
+               && Objects.equal(this.flavorName, that.flavorName)
+               && Objects.equal(this.instanceCreated, that.instanceCreated)
+               && Objects.equal(this.instanceTerminiated, that.instanceTerminiated)
+               && Objects.equal(this.instanceStatus, that.instanceStatus)
+               && Objects.equal(this.uptime, that.uptime);
    }
-
+   
    protected ToStringHelper string() {
-      return Objects.toStringHelper("")
-            .add("instanceName", instanceName)
-            .add("flavorMemoryMb", flavorMemoryMb)
-            .add("flavorLocalGb", flavorLocalGb)
-            .add("flavorVcpus", flavorVcpus)
-            .add("tenantId", tenantId)
-            .add("flavorName", flavorName)
-            .add("instanceCreated", instanceCreated)
-            .add("instanceTerminiated", instanceTerminiated)
-            .add("instanceStatus", instanceStatus)
-            .add("uptime", uptime)
-            ;
+      return Objects.toStringHelper(this)
+            .add("instanceName", instanceName).add("hours", hours).add("flavorMemoryMb", flavorMemoryMb).add("flavorLocalGb", flavorLocalGb).add("flavorVcpus", flavorVcpus).add("tenantId", tenantId).add("flavorName", flavorName).add("instanceCreated", instanceCreated).add("instanceTerminiated", instanceTerminiated).add("instanceStatus", instanceStatus).add("uptime", uptime);
    }
-
+   
    @Override
    public String toString() {
       return string().toString();
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/SimpleTenantUsage.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/SimpleTenantUsage.java
index 26b4bc7..3fdfb07 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/SimpleTenantUsage.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/SimpleTenantUsage.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -20,101 +20,128 @@
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
-import java.util.Collections;
+import java.beans.ConstructorProperties;
 import java.util.Date;
 import java.util.Set;
 
+import javax.inject.Named;
+
 import org.jclouds.javax.annotation.Nullable;
 
 import com.google.common.base.Objects;
 import com.google.common.base.Objects.ToStringHelper;
 import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Sets;
-import com.google.gson.annotations.SerializedName;
 
 /**
  * Information the SimpleTenantUsage extension returns data about each tenant
- *
+ * 
  * @author Adam Lowe
- */
+*/
 public class SimpleTenantUsage {
-   public static Builder<?> builder() {
+
+   public static Builder<?> builder() { 
       return new ConcreteBuilder();
    }
-
-   public Builder<?> toBuilder() {
+   
+   public Builder<?> toBuilder() { 
       return new ConcreteBuilder().fromSimpleTenantUsage(this);
    }
 
-   public static abstract class Builder<T extends Builder<T>> {
-      private String tenantId;
-      private double totalLocalGbUsage;
-      private double totalVcpusUsage;
-      private double totalMemoryMbUsage;
-      private double totalHours;
-      private Date start;
-      private Date stop;
-      private Set<SimpleServerUsage> serverUsages = Sets.newLinkedHashSet();
-
+   public static abstract class Builder<T extends Builder<T>>  {
       protected abstract T self();
 
+      protected String tenantId;
+      protected double totalLocalGbUsage;
+      protected double totalVcpusUsage;
+      protected double totalMemoryMbUsage;
+      protected double totalHours;
+      protected Date start;
+      protected Date stop;
+      protected Set<SimpleServerUsage> serverUsages = ImmutableSet.of();
+   
+      /** 
+       * @see SimpleTenantUsage#getTenantId()
+       */
       public T tenantId(String tenantId) {
          this.tenantId = tenantId;
          return self();
       }
-      
-      public T totalLocalGbUsage(double total_local_gb_usage) {
-         this.totalLocalGbUsage = total_local_gb_usage;
+
+      /** 
+       * @see SimpleTenantUsage#getTotalLocalGbUsage()
+       */
+      public T totalLocalGbUsage(double totalLocalGbUsage) {
+         this.totalLocalGbUsage = totalLocalGbUsage;
          return self();
       }
 
-      public T totalVcpusUsage(double total_vcpus_usage) {
-         this.totalVcpusUsage = total_vcpus_usage;
+      /** 
+       * @see SimpleTenantUsage#getTotalVcpusUsage()
+       */
+      public T totalVcpusUsage(double totalVcpusUsage) {
+         this.totalVcpusUsage = totalVcpusUsage;
          return self();
       }
 
-      public T totalMemoryMbUsage(double total_memory_mb_usage) {
-         this.totalMemoryMbUsage = total_memory_mb_usage;
+      /** 
+       * @see SimpleTenantUsage#getTotalMemoryMbUsage()
+       */
+      public T totalMemoryMbUsage(double totalMemoryMbUsage) {
+         this.totalMemoryMbUsage = totalMemoryMbUsage;
          return self();
       }
 
-      public T totalHours(double total_hours) {
-         this.totalHours = total_hours;
+      /** 
+       * @see SimpleTenantUsage#getTotalHours()
+       */
+      public T totalHours(double totalHours) {
+         this.totalHours = totalHours;
          return self();
       }
 
+      /** 
+       * @see SimpleTenantUsage#getStart()
+       */
       public T start(Date start) {
          this.start = start;
          return self();
       }
 
+      /** 
+       * @see SimpleTenantUsage#getStop()
+       */
       public T stop(Date stop) {
          this.stop = stop;
          return self();
       }
 
+      /** 
+       * @see SimpleTenantUsage#getServerUsages()
+       */
       public T serverUsages(Set<SimpleServerUsage> serverUsages) {
-         this.serverUsages = serverUsages;
+         this.serverUsages = ImmutableSet.copyOf(checkNotNull(serverUsages, "serverUsages"));      
          return self();
       }
 
-      public SimpleTenantUsage build() {
-         return new SimpleTenantUsage(this);
+      public T serverUsages(SimpleServerUsage... in) {
+         return serverUsages(ImmutableSet.copyOf(in));
       }
 
-
+      public SimpleTenantUsage build() {
+         return new SimpleTenantUsage(tenantId, totalLocalGbUsage, totalVcpusUsage, totalMemoryMbUsage, totalHours, start, stop, serverUsages);
+      }
+      
       public T fromSimpleTenantUsage(SimpleTenantUsage in) {
          return this
-               .totalLocalGbUsage(in.getTotalLocalGbUsage())
-               .totalVcpusUsage(in.getTotalVcpusUsage())
-               .totalMemoryMbUsage(in.getTotalMemoryMbUsage())
-               .totalHours(in.getTotalHours())
-               .start(in.getStart())
-               .stop(in.getStop())
-               .serverUsages(in.getServerUsages())
-               ;
+                  .tenantId(in.getTenantId())
+                  .totalLocalGbUsage(in.getTotalLocalGbUsage())
+                  .totalVcpusUsage(in.getTotalVcpusUsage())
+                  .totalMemoryMbUsage(in.getTotalMemoryMbUsage())
+                  .totalHours(in.getTotalHours())
+                  .start(in.getStart())
+                  .stop(in.getStop())
+                  .serverUsages(in.getServerUsages());
       }
-
    }
 
    private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
@@ -123,91 +150,73 @@
          return this;
       }
    }
-   
-   protected SimpleTenantUsage() {
-      // we want serializers like Gson to work w/o using sun.misc.Unsafe,
-      // prohibited in GAE. This also implies fields are not final.
-      // see http://code.google.com/p/jclouds/issues/detail?id=925
-   }
-  
-   @SerializedName("tenant_id")
-   private String tenantId;
-   @SerializedName("total_local_gb_usage")
-   private double totalLocalGbUsage;
-   @SerializedName("total_vcpus_usage")
-   private double totalVcpusUsage;
-   @SerializedName("total_memory_mb_usage")
-   private double totalMemoryMbUsage;
-   @SerializedName("total_hours")
-   private double totalHours;
-   private Date start;
-   private Date stop;
-   @SerializedName("server_usages")
-   private Set<SimpleServerUsage> serverUsages = ImmutableSet.of();
 
-   private SimpleTenantUsage(Builder<?> builder) {
-      this.tenantId = builder.tenantId;
-      this.totalLocalGbUsage = builder.totalLocalGbUsage;
-      this.totalVcpusUsage = builder.totalVcpusUsage;
-      this.totalMemoryMbUsage = builder.totalMemoryMbUsage;
-      this.totalHours = builder.totalHours;
-      this.start = builder.start;
-      this.stop = builder.stop;
-      this.serverUsages = ImmutableSet.copyOf(checkNotNull(builder.serverUsages, "serverUsages"));
+   @Named("tenant_id")
+   private final String tenantId;
+   @Named("total_local_gb_usage")
+   private final double totalLocalGbUsage;
+   @Named("total_vcpus_usage")
+   private final double totalVcpusUsage;
+   @Named("total_memory_mb_usage")
+   private final double totalMemoryMbUsage;
+   @Named("total_hours")
+   private final double totalHours;
+   private final Date start;
+   private final Date stop;
+   @Named("server_usages")
+   private final Set<SimpleServerUsage> serverUsages;
+
+   @ConstructorProperties({
+      "tenant_id", "total_local_gb_usage", "total_vcpus_usage", "total_memory_mb_usage", "total_hours", "start", "stop", "server_usages"
+   })
+   protected SimpleTenantUsage(String tenantId, double totalLocalGbUsage, double totalVcpusUsage, double totalMemoryMbUsage, double totalHours, @Nullable Date start, @Nullable Date stop, @Nullable Set<SimpleServerUsage> serverUsages) {
+      this.tenantId = checkNotNull(tenantId, "tenantId");
+      this.totalLocalGbUsage = totalLocalGbUsage;
+      this.totalVcpusUsage = totalVcpusUsage;
+      this.totalMemoryMbUsage = totalMemoryMbUsage;
+      this.totalHours = totalHours;
+      this.start = start;
+      this.stop = stop;
+      this.serverUsages = serverUsages == null ? ImmutableSet.<SimpleServerUsage>of() : ImmutableSet.copyOf(serverUsages);      
    }
 
    public String getTenantId() {
-      return tenantId;
+      return this.tenantId;
    }
 
-   /**
-    */
    public double getTotalLocalGbUsage() {
       return this.totalLocalGbUsage;
    }
 
-   /**
-    */
    public double getTotalVcpusUsage() {
       return this.totalVcpusUsage;
    }
 
-   /**
-    */
    public double getTotalMemoryMbUsage() {
       return this.totalMemoryMbUsage;
    }
 
-   /**
-    */
    public double getTotalHours() {
       return this.totalHours;
    }
 
-   /**
-    */
    @Nullable
    public Date getStart() {
       return this.start;
    }
 
-   /**
-    */
    @Nullable
    public Date getStop() {
       return this.stop;
    }
 
-   /**
-    */
-   @Nullable
    public Set<SimpleServerUsage> getServerUsages() {
-      return serverUsages == null ? ImmutableSet.<SimpleServerUsage>of() : Collections.unmodifiableSet(this.serverUsages);
+      return this.serverUsages;
    }
 
    @Override
    public int hashCode() {
-      return Objects.hashCode(totalLocalGbUsage, totalVcpusUsage, totalMemoryMbUsage, totalHours, start, stop, serverUsages);
+      return Objects.hashCode(tenantId, totalLocalGbUsage, totalVcpusUsage, totalMemoryMbUsage, totalHours, start, stop, serverUsages);
    }
 
    @Override
@@ -215,28 +224,21 @@
       if (this == obj) return true;
       if (obj == null || getClass() != obj.getClass()) return false;
       SimpleTenantUsage that = SimpleTenantUsage.class.cast(obj);
-      return Objects.equal(this.totalLocalGbUsage, that.totalLocalGbUsage)
-            && Objects.equal(this.totalVcpusUsage, that.totalVcpusUsage)
-            && Objects.equal(this.totalMemoryMbUsage, that.totalMemoryMbUsage)
-            && Objects.equal(this.totalHours, that.totalHours)
-            && Objects.equal(this.start, that.start)
-            && Objects.equal(this.stop, that.stop)
-            && Objects.equal(this.serverUsages, that.serverUsages)
-            ;
+      return Objects.equal(this.tenantId, that.tenantId)
+               && Objects.equal(this.totalLocalGbUsage, that.totalLocalGbUsage)
+               && Objects.equal(this.totalVcpusUsage, that.totalVcpusUsage)
+               && Objects.equal(this.totalMemoryMbUsage, that.totalMemoryMbUsage)
+               && Objects.equal(this.totalHours, that.totalHours)
+               && Objects.equal(this.start, that.start)
+               && Objects.equal(this.stop, that.stop)
+               && Objects.equal(this.serverUsages, that.serverUsages);
    }
-
+   
    protected ToStringHelper string() {
-      return Objects.toStringHelper("")
-            .add("totalLocalGbUsage", totalLocalGbUsage)
-            .add("totalVcpusUsage", totalVcpusUsage)
-            .add("totalMemoryMbUsage", totalMemoryMbUsage)
-            .add("totalHours", totalHours)
-            .add("start", start)
-            .add("stop", stop)
-            .add("serverUsages", serverUsages)
-            ;
+      return Objects.toStringHelper(this)
+            .add("tenantId", tenantId).add("totalLocalGbUsage", totalLocalGbUsage).add("totalVcpusUsage", totalVcpusUsage).add("totalMemoryMbUsage", totalMemoryMbUsage).add("totalHours", totalHours).add("start", start).add("stop", stop).add("serverUsages", serverUsages);
    }
-
+   
    @Override
    public String toString() {
       return string().toString();
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/TenantIdAndName.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/TenantIdAndName.java
index f0cb35c..1123bd0 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/TenantIdAndName.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/TenantIdAndName.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -18,50 +18,88 @@
  */
 package org.jclouds.openstack.nova.v2_0.domain;
 
-import static com.google.common.base.Objects.equal;
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import java.beans.ConstructorProperties;
+
+import javax.inject.Named;
+
 import com.google.common.base.Objects;
 import com.google.common.base.Objects.ToStringHelper;
-import com.google.gson.annotations.SerializedName;
 
 /**
+ * Class TenantIdAndName
  * 
  * @author Adrian Cole
- */
+*/
 public class TenantIdAndName {
 
-   protected TenantIdAndName() {
-      // we want serializers like Gson to work w/o using sun.misc.Unsafe,
-      // prohibited in GAE. This also implies fields are not final.
-      // see http://code.google.com/p/jclouds/issues/detail?id=925
+   public static Builder<?> builder() { 
+      return new ConcreteBuilder();
    }
-  
-   @SerializedName("tenant_id")
-   protected String tenantId;
-   protected String name;
+   
+   public Builder<?> toBuilder() { 
+      return new ConcreteBuilder().fromTenantIdAndName(this);
+   }
 
-   public TenantIdAndName(String tenantId, String name) {
+   public static abstract class Builder<T extends Builder<T>>  {
+      protected abstract T self();
+
+      protected String tenantId;
+      protected String name;
+   
+      /** 
+       * @see TenantIdAndName#getTenantId()
+       */
+      public T tenantId(String tenantId) {
+         this.tenantId = tenantId;
+         return self();
+      }
+
+      /** 
+       * @see TenantIdAndName#getName()
+       */
+      public T name(String name) {
+         this.name = name;
+         return self();
+      }
+
+      public TenantIdAndName build() {
+         return new TenantIdAndName(tenantId, name);
+      }
+      
+      public T fromTenantIdAndName(TenantIdAndName in) {
+         return this
+                  .tenantId(in.getTenantId())
+                  .name(in.getName());
+      }
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
+   }
+
+   @Named("tenant_id")
+   private final String tenantId;
+   private final String name;
+
+   @ConstructorProperties({
+      "tenant_id", "name"
+   })
+   protected TenantIdAndName(String tenantId, String name) {
       this.tenantId = checkNotNull(tenantId, "tenantId");
       this.name = checkNotNull(name, "name");
    }
 
    public String getTenantId() {
-      return tenantId;
+      return this.tenantId;
    }
 
    public String getName() {
-      return name;
-   }
-
-   @Override
-   public boolean equals(Object o) {
-      if (this == o)
-         return true;
-      if (o == null || getClass() != o.getClass())
-         return false;
-      TenantIdAndName that = TenantIdAndName.class.cast(o);
-      return equal(this.tenantId, that.tenantId) && equal(this.name, that.name);
+      return this.name;
    }
 
    @Override
@@ -70,11 +108,22 @@
    }
 
    @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      TenantIdAndName that = TenantIdAndName.class.cast(obj);
+      return Objects.equal(this.tenantId, that.tenantId)
+               && Objects.equal(this.name, that.name);
+   }
+   
+   protected ToStringHelper string() {
+      return Objects.toStringHelper(this)
+            .add("tenantId", tenantId).add("name", name);
+   }
+   
+   @Override
    public String toString() {
       return string().toString();
    }
 
-   protected ToStringHelper string() {
-      return Objects.toStringHelper("").add("tenantId", tenantId).add("name", name);
-   }
 }
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/VirtualInterface.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/VirtualInterface.java
index 9cb004c..381e67e 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/VirtualInterface.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/VirtualInterface.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -20,33 +20,36 @@
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import java.beans.ConstructorProperties;
+
+import javax.inject.Named;
+
 import com.google.common.base.Objects;
 import com.google.common.base.Objects.ToStringHelper;
-import com.google.gson.annotations.SerializedName;
 
 /**
  * Represents a Virtual Interface (VIF)
- *
+ * 
  * @author Adam Lowe
  * @see org.jclouds.openstack.nova.v2_0.extensions.VirtualInterfaceClient
- */
+*/
 public class VirtualInterface {
 
-   public static Builder<?> builder() {
+   public static Builder<?> builder() { 
       return new ConcreteBuilder();
    }
-
-   public Builder<?> toBuilder() {
+   
+   public Builder<?> toBuilder() { 
       return new ConcreteBuilder().fromVirtualInterface(this);
    }
 
    public static abstract class Builder<T extends Builder<T>>  {
       protected abstract T self();
 
-      private String id;
-      private String macAddress;
-
-      /**
+      protected String id;
+      protected String macAddress;
+   
+      /** 
        * @see VirtualInterface#getId()
        */
       public T id(String id) {
@@ -54,7 +57,7 @@
          return self();
       }
 
-      /**
+      /** 
        * @see VirtualInterface#getMacAddress()
        */
       public T macAddress(String macAddress) {
@@ -63,16 +66,14 @@
       }
 
       public VirtualInterface build() {
-         return new VirtualInterface(this);
+         return new VirtualInterface(id, macAddress);
       }
-
+      
       public T fromVirtualInterface(VirtualInterface in) {
          return this
-               .id(in.getId())
-               .macAddress(in.getMacAddress())
-               ;
+                  .id(in.getId())
+                  .macAddress(in.getMacAddress());
       }
-
    }
 
    private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
@@ -82,25 +83,22 @@
       }
    }
 
-   protected VirtualInterface() {
-      // we want serializers like Gson to work w/o using sun.misc.Unsafe,
-      // prohibited in GAE. This also implies fields are not final.
-      // see http://code.google.com/p/jclouds/issues/detail?id=925
-   }
-  
-   private String id;
-   @SerializedName(value="mac_address")
-   private String macAddress;
+   private final String id;
+   @Named("mac_address")
+   private final String macAddress;
 
-   protected VirtualInterface(Builder<?> builder) {
-      this.id = checkNotNull(builder.id, "id");
-      this.macAddress = checkNotNull(builder.macAddress, "macAddress");
+   @ConstructorProperties({
+      "id", "mac_address"
+   })
+   protected VirtualInterface(String id, String macAddress) {
+      this.id = checkNotNull(id, "id");
+      this.macAddress = checkNotNull(macAddress, "macAddress");
    }
 
    public String getId() {
       return this.id;
    }
-   
+
    public String getMacAddress() {
       return this.macAddress;
    }
@@ -116,20 +114,17 @@
       if (obj == null || getClass() != obj.getClass()) return false;
       VirtualInterface that = VirtualInterface.class.cast(obj);
       return Objects.equal(this.id, that.id)
-            && Objects.equal(this.macAddress, that.macAddress)
-            ;
+               && Objects.equal(this.macAddress, that.macAddress);
    }
-
+   
    protected ToStringHelper string() {
-      return Objects.toStringHelper("")
-            .add("id", id)
-            .add("macAddress", macAddress)
-            ;
+      return Objects.toStringHelper(this)
+            .add("id", id).add("macAddress", macAddress);
    }
-
+   
    @Override
    public String toString() {
       return string().toString();
    }
 
-}
\ No newline at end of file
+}
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/Volume.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/Volume.java
index 3f2dfb0..3309ba5 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/Volume.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/Volume.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -20,11 +20,13 @@
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
-import java.util.Collections;
+import java.beans.ConstructorProperties;
 import java.util.Date;
 import java.util.Map;
 import java.util.Set;
 
+import javax.inject.Named;
+
 import org.jclouds.javax.annotation.Nullable;
 
 import com.google.common.base.CaseFormat;
@@ -32,142 +34,167 @@
 import com.google.common.base.Objects.ToStringHelper;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
-import com.google.gson.annotations.SerializedName;
 
 /**
  * An Openstack Nova Volume
- */
+*/
 public class Volume {
 
+   /**
+    */
    public static enum Status {
       CREATING, AVAILABLE, IN_USE, DELETING, ERROR, UNRECOGNIZED;
       public String value() {
-         return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_HYPHEN, name());
+      return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_HYPHEN, name());
       }
-
+      
       @Override
       public String toString() {
-         return value();
+      return value();
       }
-
+      
       public static Status fromValue(String status) {
-         try {
-            return valueOf(CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_UNDERSCORE, checkNotNull(status, "status")));
-         } catch (IllegalArgumentException e) {
-            return UNRECOGNIZED;
-         }
+      try {
+      return valueOf(CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_UNDERSCORE, checkNotNull(status, "status")));
+      } catch (IllegalArgumentException e) {
+      return UNRECOGNIZED;
+      }
       }
    }
-   
-   public static Builder<?> builder() {
+
+   public static Builder<?> builder() { 
       return new ConcreteBuilder();
    }
-
-   public Builder<?> toBuilder() {
+   
+   public Builder<?> toBuilder() { 
       return new ConcreteBuilder().fromVolume(this);
    }
 
    public static abstract class Builder<T extends Builder<T>>  {
       protected abstract T self();
 
-      private String id;
-      private Status status;
-      private int size;
-      private String zone;
-      private Date created;
-      private Set<VolumeAttachment> attachments = Sets.newLinkedHashSet();
-      private String volumeType;
-      private String snapshotId;
-      private String name;
-      private String description;
-      private Map<String, String> metadata = Maps.newHashMap();
-
-      /** @see Volume#getId() */
+      protected String id;
+      protected Volume.Status status;
+      protected int size;
+      protected String zone;
+      protected Date created;
+      protected Set<VolumeAttachment> attachments = ImmutableSet.of();
+      protected String volumeType;
+      protected String snapshotId;
+      protected String name;
+      protected String description;
+      protected Map<String, String> metadata = ImmutableMap.of();
+   
+      /** 
+       * @see Volume#getId()
+       */
       public T id(String id) {
          this.id = id;
          return self();
       }
 
-      /** @see Volume#getStatus() */
-      public T status(Status status) {
+      /** 
+       * @see Volume#getStatus()
+       */
+      public T status(Volume.Status status) {
          this.status = status;
          return self();
       }
 
-      /** @see Volume#getSize() */
+      /** 
+       * @see Volume#getSize()
+       */
       public T size(int size) {
          this.size = size;
          return self();
       }
 
-      /** @see Volume#getZone() */
+      /** 
+       * @see Volume#getZone()
+       */
       public T zone(String zone) {
          this.zone = zone;
          return self();
       }
 
-      /** @see Volume#getCreated() */
+      /** 
+       * @see Volume#getCreated()
+       */
       public T created(Date created) {
          this.created = created;
          return self();
       }
 
-      /** @see Volume#getAttachments() */
+      /** 
+       * @see Volume#getAttachments()
+       */
       public T attachments(Set<VolumeAttachment> attachments) {
-         this.attachments = attachments;
+         this.attachments = ImmutableSet.copyOf(checkNotNull(attachments, "attachments"));      
          return self();
       }
-      
-      /** @see Volume#getVolumeType() */
+
+      public T attachments(VolumeAttachment... in) {
+         return attachments(ImmutableSet.copyOf(in));
+      }
+
+      /** 
+       * @see Volume#getVolumeType()
+       */
       public T volumeType(String volumeType) {
          this.volumeType = volumeType;
          return self();
       }
 
-      /** @see Volume#getSnapshotId() */
+      /** 
+       * @see Volume#getSnapshotId()
+       */
       public T snapshotId(String snapshotId) {
          this.snapshotId = snapshotId;
          return self();
       }
-      
-      /** @see Volume#getMetadata() */
-      public T metadata(Map<String, String> metadata) {
-         this.metadata = metadata;
-         return self();
-      }
 
-      /** @see Volume#getName() */
+      /** 
+       * @see Volume#getName()
+       */
       public T name(String name) {
          this.name = name;
          return self();
       }
 
-      /** @see Volume#getDescription() */
+      /** 
+       * @see Volume#getDescription()
+       */
       public T description(String description) {
          this.description = description;
          return self();
       }
-      
-      public Volume build() {
-         return new Volume(this);
+
+      /** 
+       * @see Volume#getMetadata()
+       */
+      public T metadata(Map<String, String> metadata) {
+         this.metadata = ImmutableMap.copyOf(checkNotNull(metadata, "metadata"));     
+         return self();
       }
 
+      public Volume build() {
+         return new Volume(id, status, size, zone, created, attachments, volumeType, snapshotId, name, description, metadata);
+      }
+      
       public T fromVolume(Volume in) {
          return this
-               .id(in.getId())
-               .status(in.getStatus())
-               .size(in.getSize())
-               .zone(in.getZone())
-               .created(in.getCreated())
-               .attachments(in.getAttachments())
-               .volumeType(in.getVolumeType())
-               .snapshotId(in.getSnapshotId())
-               .metadata(in.getMetadata())
-               ;
+                  .id(in.getId())
+                  .status(in.getStatus())
+                  .size(in.getSize())
+                  .zone(in.getZone())
+                  .created(in.getCreated())
+                  .attachments(in.getAttachments())
+                  .volumeType(in.getVolumeType())
+                  .snapshotId(in.getSnapshotId())
+                  .name(in.getName())
+                  .description(in.getDescription())
+                  .metadata(in.getMetadata());
       }
-
    }
 
    private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
@@ -177,40 +204,37 @@
       }
    }
 
-   protected Volume() {
-      // we want serializers like Gson to work w/o using sun.misc.Unsafe,
-      // prohibited in GAE. This also implies fields are not final.
-      // see http://code.google.com/p/jclouds/issues/detail?id=925
-   }
-  
-   private String id;
-   private Status status;
-   private int size;
-   @SerializedName(value="availabilityZone")
-   private String zone;
-   @SerializedName(value="createdAt")
-   private Date created;
-   private Set<VolumeAttachment> attachments = ImmutableSet.of();
-   private String volumeType;   
-   private String snapshotId;
-   @SerializedName(value="displayName")
-   private String name;
-   @SerializedName(value="displayDescription")
-   private String description;
-   private Map<String, String> metadata = ImmutableMap.of();
+   private final String id;
+   private final Volume.Status status;
+   private final int size;
+   @Named("availabilityZone")
+   private final String zone;
+   @Named("createdAt")
+   private final Date created;
+   private final Set<VolumeAttachment> attachments;
+   private final String volumeType;
+   private final String snapshotId;
+   @Named("displayName")
+   private final String name;
+   @Named("displayDescription")
+   private final String description;
+   private final Map<String, String> metadata;
 
-   protected Volume(Builder<?> builder) {
-      this.id = builder.id;
-      this.status = builder.status;
-      this.size = builder.size;
-      this.zone = builder.zone;
-      this.created = builder.created;
-      this.attachments = ImmutableSet.copyOf(checkNotNull(builder.attachments, "attachments"));
-      this.volumeType = builder.volumeType;
-      this.snapshotId = builder.snapshotId;
-      this.name = builder.name;
-      this.description = builder.description;
-      this.metadata = ImmutableMap.copyOf(checkNotNull(builder.metadata, "metadata"));
+   @ConstructorProperties({
+      "id", "status", "size", "availabilityZone", "createdAt", "attachments", "volumeType", "snapshotId", "displayName", "displayDescription", "metadata"
+   })
+   protected Volume(String id, Volume.Status status, int size, String zone, Date created, @Nullable Set<VolumeAttachment> attachments, @Nullable String volumeType, @Nullable String snapshotId, @Nullable String name, @Nullable String description, @Nullable Map<String, String> metadata) {
+      this.id = checkNotNull(id, "id");
+      this.status = checkNotNull(status, "status");
+      this.size = size;
+      this.zone = checkNotNull(zone, "zone");
+      this.created = checkNotNull(created, "created");
+      this.attachments = attachments == null ? ImmutableSet.<VolumeAttachment>of() : ImmutableSet.copyOf(attachments);      
+      this.volumeType = volumeType;
+      this.snapshotId = snapshotId;
+      this.name = name;
+      this.description = description;
+      this.metadata = metadata == null ? ImmutableMap.<String, String>of() : ImmutableMap.copyOf(metadata);      
    }
 
    /**
@@ -223,7 +247,7 @@
    /**
     * @return the status of this volume
     */
-   public Status getStatus() {
+   public Volume.Status getStatus() {
       return this.status;
    }
 
@@ -251,9 +275,8 @@
    /**
     * @return the set of attachments (to Servers)
     */
-   @Nullable
    public Set<VolumeAttachment> getAttachments() {
-      return Collections.unmodifiableSet(this.attachments);
+      return this.attachments;
    }
 
    /**
@@ -284,47 +307,42 @@
    public String getDescription() {
       return this.description;
    }
-   
-   @Nullable
+
    public Map<String, String> getMetadata() {
-      return Collections.unmodifiableMap(this.metadata);
+      return this.metadata;
    }
 
-   // keeping fields short in eq/hashCode so that minor state differences don't affect collection membership
    @Override
    public int hashCode() {
-      return Objects.hashCode(id, zone);
+      return Objects.hashCode(id, status, size, zone, created, attachments, volumeType, snapshotId, name, description, metadata);
    }
 
    @Override
    public boolean equals(Object obj) {
-      if (this == obj)
-         return true;
-      if (obj == null || getClass() != obj.getClass())
-         return false;
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
       Volume that = Volume.class.cast(obj);
-      return Objects.equal(this.id, that.id) && Objects.equal(this.zone, that.zone);
+      return Objects.equal(this.id, that.id)
+               && Objects.equal(this.status, that.status)
+               && Objects.equal(this.size, that.size)
+               && Objects.equal(this.zone, that.zone)
+               && Objects.equal(this.created, that.created)
+               && Objects.equal(this.attachments, that.attachments)
+               && Objects.equal(this.volumeType, that.volumeType)
+               && Objects.equal(this.snapshotId, that.snapshotId)
+               && Objects.equal(this.name, that.name)
+               && Objects.equal(this.description, that.description)
+               && Objects.equal(this.metadata, that.metadata);
    }
-
+   
    protected ToStringHelper string() {
-      return Objects.toStringHelper("")
-            .add("id", id)
-            .add("status", status)
-            .add("size", size)
-            .add("zone", zone)
-            .add("created", created)
-            .add("attachments", attachments)
-            .add("volumeType", volumeType)
-            .add("snapshotId", snapshotId)
-            .add("name", name)
-            .add("description", description)
-            .add("metadata", metadata)
-            ;
+      return Objects.toStringHelper(this)
+            .add("id", id).add("status", status).add("size", size).add("zone", zone).add("created", created).add("attachments", attachments).add("volumeType", volumeType).add("snapshotId", snapshotId).add("name", name).add("description", description).add("metadata", metadata);
    }
-
+   
    @Override
    public String toString() {
       return string().toString();
    }
 
-}
\ No newline at end of file
+}
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/VolumeAttachment.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/VolumeAttachment.java
index c70606f..d5c266d 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/VolumeAttachment.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/VolumeAttachment.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -20,6 +20,8 @@
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import java.beans.ConstructorProperties;
+
 import org.jclouds.javax.annotation.Nullable;
 
 import com.google.common.base.Objects;
@@ -27,62 +29,68 @@
 
 /**
  * An Openstack Nova Volume Attachment (describes how Volumes are attached to Servers)
- */
+*/
 public class VolumeAttachment {
 
-   public static Builder<?> builder() {
+   public static Builder<?> builder() { 
       return new ConcreteBuilder();
    }
-
-   public Builder<?> toBuilder() {
-      return new ConcreteBuilder().fromAttachment(this);
+   
+   public Builder<?> toBuilder() { 
+      return new ConcreteBuilder().fromVolumeAttachment(this);
    }
 
    public static abstract class Builder<T extends Builder<T>>  {
       protected abstract T self();
 
-      private String id;
-      private String volumeId;
-      private String serverId;
-      private String device;
-
-      /** @see VolumeAttachment#getId() */
+      protected String id;
+      protected String volumeId;
+      protected String serverId;
+      protected String device;
+   
+      /** 
+       * @see VolumeAttachment#getId()
+       */
       public T id(String id) {
          this.id = id;
          return self();
       }
 
-      /** @see VolumeAttachment#getVolumeId() */
+      /** 
+       * @see VolumeAttachment#getVolumeId()
+       */
       public T volumeId(String volumeId) {
          this.volumeId = volumeId;
          return self();
       }
 
-      /** @see VolumeAttachment#getServerId() */
+      /** 
+       * @see VolumeAttachment#getServerId()
+       */
       public T serverId(String serverId) {
          this.serverId = serverId;
          return self();
       }
 
-      /** @see VolumeAttachment#getDevice() */
+      /** 
+       * @see VolumeAttachment#getDevice()
+       */
       public T device(String device) {
          this.device = device;
          return self();
       }
 
       public VolumeAttachment build() {
-         return new VolumeAttachment(this);
+         return new VolumeAttachment(id, volumeId, serverId, device);
       }
-
-      public T fromAttachment(VolumeAttachment in) {
+      
+      public T fromVolumeAttachment(VolumeAttachment in) {
          return this
-               .id(in.getId())
-               .volumeId(in.getVolumeId())
-               .serverId(in.getServerId())
-               .device(in.getDevice())
-               ;
+                  .id(in.getId())
+                  .volumeId(in.getVolumeId())
+                  .serverId(in.getServerId())
+                  .device(in.getDevice());
       }
-
    }
 
    private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
@@ -92,22 +100,19 @@
       }
    }
 
-   protected VolumeAttachment() {
-      // we want serializers like Gson to work w/o using sun.misc.Unsafe,
-      // prohibited in GAE. This also implies fields are not final.
-      // see http://code.google.com/p/jclouds/issues/detail?id=925
-   }
-  
-   private String id;
-   private String volumeId;
-   private String serverId;
-   private String device;
+   private final String id;
+   private final String volumeId;
+   private final String serverId;
+   private final String device;
 
-   protected VolumeAttachment(Builder<?> builder) {
-      this.id = checkNotNull(builder.id, "id");
-      this.volumeId = checkNotNull(builder.volumeId, "volumeId");
-      this.serverId = builder.serverId;
-      this.device = builder.device;
+   @ConstructorProperties({
+      "id", "volumeId", "serverId", "device"
+   })
+   protected VolumeAttachment(String id, String volumeId, @Nullable String serverId, @Nullable String device) {
+      this.id = checkNotNull(id, "id");
+      this.volumeId = checkNotNull(volumeId, "volumeId");
+      this.serverId = serverId;
+      this.device = device;
    }
 
    /**
@@ -151,24 +156,19 @@
       if (obj == null || getClass() != obj.getClass()) return false;
       VolumeAttachment that = VolumeAttachment.class.cast(obj);
       return Objects.equal(this.id, that.id)
-            && Objects.equal(this.volumeId, that.volumeId)
-            && Objects.equal(this.serverId, that.serverId)
-            && Objects.equal(this.device, that.device)
-            ;
+               && Objects.equal(this.volumeId, that.volumeId)
+               && Objects.equal(this.serverId, that.serverId)
+               && Objects.equal(this.device, that.device);
    }
-
+   
    protected ToStringHelper string() {
-      return Objects.toStringHelper("")
-            .add("id", id)
-            .add("volumeId", volumeId)
-            .add("serverId", serverId)
-            .add("device", device)
-            ;
+      return Objects.toStringHelper(this)
+            .add("id", id).add("volumeId", volumeId).add("serverId", serverId).add("device", device);
    }
-
+   
    @Override
    public String toString() {
       return string().toString();
    }
 
-}
\ No newline at end of file
+}
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/VolumeSnapshot.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/VolumeSnapshot.java
index bc3185d..b9e71f0 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/VolumeSnapshot.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/VolumeSnapshot.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -20,96 +20,110 @@
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import java.beans.ConstructorProperties;
 import java.util.Date;
 
+import javax.inject.Named;
+
 import org.jclouds.javax.annotation.Nullable;
 
 import com.google.common.base.Objects;
 import com.google.common.base.Objects.ToStringHelper;
-import com.google.gson.annotations.SerializedName;
 
 /**
  * An Openstack Nova Volume Snapshot
- */
+*/
 public class VolumeSnapshot {
 
-   public static Builder<?> builder() {
+   public static Builder<?> builder() { 
       return new ConcreteBuilder();
    }
-
-   public Builder<?> toBuilder() {
-      return new ConcreteBuilder().fromSnapshot(this);
+   
+   public Builder<?> toBuilder() { 
+      return new ConcreteBuilder().fromVolumeSnapshot(this);
    }
 
    public static abstract class Builder<T extends Builder<T>>  {
       protected abstract T self();
 
-      private String id;
-      private String volumeId;
-      private Volume.Status status;
-      private int size;
-      private Date created;
-      private String name;
-      private String description;
-
-      /** @see VolumeSnapshot#getId() */
+      protected String id;
+      protected String volumeId;
+      protected Volume.Status status;
+      protected int size;
+      protected Date created;
+      protected String name;
+      protected String description;
+   
+      /** 
+       * @see VolumeSnapshot#getId()
+       */
       public T id(String id) {
          this.id = id;
          return self();
       }
 
-      /** @see VolumeSnapshot#getVolumeId() */
+      /** 
+       * @see VolumeSnapshot#getVolumeId()
+       */
       public T volumeId(String volumeId) {
          this.volumeId = volumeId;
          return self();
       }
 
-      /** @see VolumeSnapshot#getStatus() */
+      /** 
+       * @see VolumeSnapshot#getStatus()
+       */
       public T status(Volume.Status status) {
          this.status = status;
          return self();
       }
 
-      /** @see VolumeSnapshot#getSize() */
+      /** 
+       * @see VolumeSnapshot#getSize()
+       */
       public T size(int size) {
          this.size = size;
          return self();
       }
 
-      /** @see VolumeSnapshot#getCreated() */
+      /** 
+       * @see VolumeSnapshot#getCreated()
+       */
       public T created(Date created) {
          this.created = created;
          return self();
       }
 
-      /** @see VolumeSnapshot#getName() */
+      /** 
+       * @see VolumeSnapshot#getName()
+       */
       public T name(String name) {
          this.name = name;
          return self();
       }
 
-      /** @see VolumeSnapshot#getDescription() */
+      /** 
+       * @see VolumeSnapshot#getDescription()
+       */
       public T description(String description) {
          this.description = description;
          return self();
       }
 
       public VolumeSnapshot build() {
-         return new VolumeSnapshot(this);
+         return new VolumeSnapshot(id, volumeId, status, size, created, name, description);
       }
-
-      public T fromSnapshot(VolumeSnapshot in) {
+      
+      public T fromVolumeSnapshot(VolumeSnapshot in) {
          return this
-               .id(in.getId())
-               .volumeId(in.getVolumeId())
-               .status(in.getStatus())
-               .size(in.getSize())
-               .created(in.getCreated())
-               .name(in.getName())
-               .description(in.getDescription())
-               ;
+                  .id(in.getId())
+                  .volumeId(in.getVolumeId())
+                  .status(in.getStatus())
+                  .size(in.getSize())
+                  .created(in.getCreated())
+                  .name(in.getName())
+                  .description(in.getDescription());
       }
-
    }
 
    private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
@@ -119,31 +133,28 @@
       }
    }
 
-   protected VolumeSnapshot() {
-      // we want serializers like Gson to work w/o using sun.misc.Unsafe,
-      // prohibited in GAE. This also implies fields are not final.
-      // see http://code.google.com/p/jclouds/issues/detail?id=925
-   }
-  
-   private String id;
-   private String volumeId;
-   private Volume.Status status;
-   private int size;
-   @SerializedName(value="createdAt")
-   private Date created;
-   @SerializedName(value="displayName")
-   private String name;
-   @SerializedName(value="displayDescription")
-   private String description;
+   private final String id;
+   private final String volumeId;
+   private final Volume.Status status;
+   private final int size;
+   @Named("createdAt")
+   private final Date created;
+   @Named("displayName")
+   private final String name;
+   @Named("displayDescription")
+   private final String description;
 
-   protected VolumeSnapshot(Builder<?> builder) {
-      this.id = checkNotNull(builder.id, "id");
-      this.volumeId = checkNotNull(builder.volumeId, "volumeId");
-      this.status = checkNotNull(builder.status, "status");
-      this.size = builder.size;
-      this.created = builder.created;
-      this.name = builder.name;
-      this.description = builder.description;
+   @ConstructorProperties({
+      "id", "volumeId", "status", "size", "createdAt", "displayName", "displayDescription"
+   })
+   protected VolumeSnapshot(String id, String volumeId, Volume.Status status, int size, @Nullable Date created, @Nullable String name, @Nullable String description) {
+      this.id = checkNotNull(id, "id");
+      this.volumeId = checkNotNull(volumeId, "volumeId");
+      this.status = checkNotNull(status, "status");
+      this.size = size;
+      this.created = created;
+      this.name = name;
+      this.description = description;
    }
 
    /**
@@ -190,7 +201,6 @@
       return this.name;
    }
 
-
    /**
     * @return the description of this snapshot - as displayed in the openstack console
     */
@@ -210,30 +220,22 @@
       if (obj == null || getClass() != obj.getClass()) return false;
       VolumeSnapshot that = VolumeSnapshot.class.cast(obj);
       return Objects.equal(this.id, that.id)
-            && Objects.equal(this.volumeId, that.volumeId)
-            && Objects.equal(this.status, that.status)
-            && Objects.equal(this.size, that.size)
-            && Objects.equal(this.created, that.created)
-            && Objects.equal(this.name, that.name)
-            && Objects.equal(this.description, that.description)
-            ;
+               && Objects.equal(this.volumeId, that.volumeId)
+               && Objects.equal(this.status, that.status)
+               && Objects.equal(this.size, that.size)
+               && Objects.equal(this.created, that.created)
+               && Objects.equal(this.name, that.name)
+               && Objects.equal(this.description, that.description);
    }
-
+   
    protected ToStringHelper string() {
-      return Objects.toStringHelper("")
-            .add("id", id)
-            .add("volumeId", volumeId)
-            .add("status", status)
-            .add("size", size)
-            .add("created", created)
-            .add("name", name)
-            .add("description", description)
-            ;
+      return Objects.toStringHelper(this)
+            .add("id", id).add("volumeId", volumeId).add("status", status).add("size", size).add("created", created).add("name", name).add("description", description);
    }
-
+   
    @Override
    public String toString() {
       return string().toString();
    }
 
-}
\ No newline at end of file
+}
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/VolumeType.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/VolumeType.java
index 2b4c54a..fac9eb6 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/VolumeType.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/VolumeType.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -20,41 +20,44 @@
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
-import java.util.Collections;
+import java.beans.ConstructorProperties;
 import java.util.Date;
 import java.util.Map;
 
+import javax.inject.Named;
+
+import org.jclouds.javax.annotation.Nullable;
+
 import com.google.common.base.Objects;
-import com.google.common.base.Optional;
 import com.google.common.base.Objects.ToStringHelper;
+import com.google.common.base.Optional;
 import com.google.common.collect.ImmutableMap;
-import com.google.gson.annotations.SerializedName;
 
 /**
  * Volume Type used in the Volume Type Extension for Nova
- *
+ * 
  * @see org.jclouds.openstack.nova.v2_0.extensions.VolumeTypeClient
- */
+*/
 public class VolumeType {
 
-   public static Builder<?> builder() {
+   public static Builder<?> builder() { 
       return new ConcreteBuilder();
    }
-
-   public Builder<?> toBuilder() {
+   
+   public Builder<?> toBuilder() { 
       return new ConcreteBuilder().fromVolumeType(this);
    }
 
-   public static abstract class Builder<T extends Builder<T>> {
+   public static abstract class Builder<T extends Builder<T>>  {
       protected abstract T self();
 
-      private String id;
-      private String name;
-      private Date created = new Date();
-      private Date updated;
-      private Map<String, String> extraSpecs;
-
-      /**
+      protected String id;
+      protected String name;
+      protected Date created;
+      protected Date updated;
+      protected Map<String, String> extraSpecs = ImmutableMap.of();
+   
+      /** 
        * @see VolumeType#getId()
        */
       public T id(String id) {
@@ -62,7 +65,7 @@
          return self();
       }
 
-      /**
+      /** 
        * @see VolumeType#getName()
        */
       public T name(String name) {
@@ -70,7 +73,7 @@
          return self();
       }
 
-      /**
+      /** 
        * @see VolumeType#getCreated()
        */
       public T created(Date created) {
@@ -78,7 +81,7 @@
          return self();
       }
 
-      /**
+      /** 
        * @see VolumeType#getUpdated()
        */
       public T updated(Date updated) {
@@ -86,27 +89,26 @@
          return self();
       }
 
-      /**
+      /** 
        * @see VolumeType#getExtraSpecs()
        */
       public T extraSpecs(Map<String, String> extraSpecs) {
-         this.extraSpecs = ImmutableMap.copyOf(extraSpecs);
+         this.extraSpecs = ImmutableMap.copyOf(checkNotNull(extraSpecs, "extraSpecs"));     
          return self();
       }
 
       public VolumeType build() {
-         return new VolumeType(this);
+         return new VolumeType(id, name, created, updated, extraSpecs);
       }
-
+      
       public T fromVolumeType(VolumeType in) {
          return this
-               .id(in.getId())
-               .name(in.getName())
-               .extraSpecs(in.getExtraSpecs())
-               .created(in.getCreated())
-               .updated(in.getUpdated().orNull());
+                  .id(in.getId())
+                  .name(in.getName())
+                  .created(in.getCreated())
+                  .updated(in.getUpdated().orNull())
+                  .extraSpecs(in.getExtraSpecs());
       }
-
    }
 
    private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
@@ -116,27 +118,24 @@
       }
    }
 
-   protected VolumeType() {
-      // we want serializers like Gson to work w/o using sun.misc.Unsafe,
-      // prohibited in GAE. This also implies fields are not final.
-      // see http://code.google.com/p/jclouds/issues/detail?id=925
-   }
-  
-   private String id;
-   private String name;
-   @SerializedName("created_at")
-   private Date created;
-   @SerializedName("updated_at")
-   private Optional<Date> updated = Optional.absent();
-   @SerializedName(value = "extra_specs")
-   private Map<String, String> extraSpecs = ImmutableMap.of();
+   private final String id;
+   private final String name;
+   @Named("created_at")
+   private final Date created;
+   @Named("updated_at")
+   private final Optional<Date> updated;
+   @Named("extra_specs")
+   private final Map<String, String> extraSpecs;
 
-   protected VolumeType(Builder<?> builder) {
-      this.id = checkNotNull(builder.id, "id");
-      this.name = checkNotNull(builder.name, "name");
-      this.extraSpecs = checkNotNull(builder.extraSpecs, "extraSpecs");
-      this.created = checkNotNull(builder.created, "created");
-      this.updated = Optional.fromNullable(builder.updated);
+   @ConstructorProperties({
+      "id", "name", "created_at", "updated_at", "extra_specs"
+   })
+   protected VolumeType(String id, String name, Date created, @Nullable Date updated, Map<String, String> extraSpecs) {
+      this.id = checkNotNull(id, "id");
+      this.name = checkNotNull(name, "name");
+      this.created = checkNotNull(created, "created");
+      this.updated = Optional.fromNullable(updated);
+      this.extraSpecs = ImmutableMap.copyOf(checkNotNull(extraSpecs, "extraSpecs"));     
    }
 
    public String getId() {
@@ -147,18 +146,22 @@
       return this.name;
    }
 
-   /** The Date the VolumeType was created */
+   /**
+    * The Date the VolumeType was created
+    */
    public Date getCreated() {
-      return created;
+      return this.created;
    }
 
-   /** The Date the VolumeType as last updated - absent if no updates have taken place */
+   /**
+    * The Date the VolumeType as last updated - absent if no updates have taken place
+    */
    public Optional<Date> getUpdated() {
-      return updated;
+      return this.updated;
    }
 
    public Map<String, String> getExtraSpecs() {
-      return Collections.unmodifiableMap(this.extraSpecs);
+      return this.extraSpecs;
    }
 
    @Override
@@ -172,24 +175,20 @@
       if (obj == null || getClass() != obj.getClass()) return false;
       VolumeType that = VolumeType.class.cast(obj);
       return Objects.equal(this.id, that.id)
-            && Objects.equal(this.name, that.name)
-            && Objects.equal(this.created, that.created)
-            && Objects.equal(this.updated, that.updated)
-            && Objects.equal(this.extraSpecs, that.extraSpecs);
+               && Objects.equal(this.name, that.name)
+               && Objects.equal(this.created, that.created)
+               && Objects.equal(this.updated, that.updated)
+               && Objects.equal(this.extraSpecs, that.extraSpecs);
    }
-
+   
    protected ToStringHelper string() {
-      return Objects.toStringHelper("")
-            .add("id", id)
-            .add("name", name)
-            .add("created", created)
-            .add("updated", updated)
-            .add("extraSpecs", extraSpecs);
+      return Objects.toStringHelper(this)
+            .add("id", id).add("name", name).add("created", created).add("updated", updated).add("extraSpecs", extraSpecs);
    }
-
+   
    @Override
    public String toString() {
       return string().toString();
    }
 
-}
\ No newline at end of file
+}
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/options/CreateServerOptions.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/options/CreateServerOptions.java
index 00aa5ee..893ad42 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/options/CreateServerOptions.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/options/CreateServerOptions.java
@@ -30,11 +30,11 @@
 import java.util.Set;
 
 import javax.inject.Inject;
+import javax.inject.Named;
 
 import org.jclouds.encryption.internal.Base64;
 import org.jclouds.http.HttpRequest;
 import org.jclouds.openstack.nova.v2_0.NovaClient;
-import org.jclouds.openstack.nova.v2_0.domain.SecurityGroup;
 import org.jclouds.rest.MapBinder;
 import org.jclouds.rest.binders.BindToJsonPayload;
 import org.jclouds.util.Preconditions2;
@@ -42,11 +42,11 @@
 import com.google.common.base.Objects;
 import com.google.common.base.Objects.ToStringHelper;
 import com.google.common.base.Optional;
+import com.google.common.collect.ForwardingObject;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
-import com.google.gson.annotations.SerializedName;
 
 /**
  * 
@@ -152,7 +152,6 @@
       return string().toString();
    }
 
-   @SuppressWarnings("unused")
    private class ServerRequest {
       final String name;
       final String imageRef;
@@ -161,8 +160,8 @@
       Map<String, String> metadata;
       List<File> personality;
       String key_name;
-      @SerializedName(value = "security_groups")
-      Set<SecurityGroup> securityGroupNames;
+      @Named("security_groups")
+      Set<NamedThingy> securityGroupNames;
       String user_data;
 
       private ServerRequest(String name, String imageRef, String flavorRef) {
@@ -187,10 +186,9 @@
       if (userData != null)
           server.user_data = Base64.encodeBytes(userData);
       if (securityGroupNames.size() > 0) {
-         server.securityGroupNames = Sets.newHashSet();
+         server.securityGroupNames = Sets.newLinkedHashSet();
          for (String groupName : securityGroupNames) {
-            SecurityGroup group = SecurityGroup.builder().name(groupName).build();
-            server.securityGroupNames.add(group);
+            server.securityGroupNames.add(new NamedThingy(groupName));
          }
       }
       if (adminPass != null) {
@@ -200,6 +198,20 @@
       return bindToRequest(request, ImmutableMap.of("server", server));
    }
 
+   private static class NamedThingy extends ForwardingObject {
+      private String name;
+
+      private NamedThingy(String name) {
+         this.name = name;
+      }
+
+      @Override
+      protected Object delegate() {
+         return name;
+      }
+   }
+
+
    /**
     * You may further customize a cloud server by injecting data into the file
     * system of the cloud server itself. This is useful, for example, for
diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/NovaComputeServiceAdapterExpectTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/NovaComputeServiceAdapterExpectTest.java
index 28a95f0..a1d40cd 100644
--- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/NovaComputeServiceAdapterExpectTest.java
+++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/NovaComputeServiceAdapterExpectTest.java
@@ -64,7 +64,7 @@
                ImmutableMultimap.<String, String> builder().put("Accept", "application/json")
                      .put("X-Auth-Token", authToken).build())
          .payload(payloadFromStringWithContentType(
-                  "{\"server\":{\"name\":\"test-e92\",\"imageRef\":\"1241\",\"flavorRef\":\"100\",\"security_groups\":[{\"name\":\"group2\"},{\"name\":\"group1\"}]}}","application/json"))
+                  "{\"server\":{\"name\":\"test-e92\",\"imageRef\":\"1241\",\"flavorRef\":\"100\",\"security_groups\":[{\"name\":\"group1\"}, {\"name\":\"group2\"}]}}","application/json"))
          .build();
 
       HttpResponse createServerResponse = HttpResponse.builder().statusCode(202).message("HTTP/1.1 202 Accepted")
diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/SecurityGroupClientLiveTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/SecurityGroupClientLiveTest.java
index c92a0db..5750a7e 100644
--- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/SecurityGroupClientLiveTest.java
+++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/SecurityGroupClientLiveTest.java
@@ -37,7 +37,7 @@
  * 
  * @author Michael Arnold
  */
-@Test(groups = "live", testName = "SecurityGroupClientLiveTest")
+@Test(groups = "live", testName = "SecurityGroupClientLiveTest", singleThreaded = true)
 public class SecurityGroupClientLiveTest extends BaseNovaClientLiveTest {
 
    public static final String SECURITY_GROUP_NAME = "testsg";
diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/SimpleTenantUsageClientExpectTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/SimpleTenantUsageClientExpectTest.java
index bb18dea..7512a03 100644
--- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/SimpleTenantUsageClientExpectTest.java
+++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/SimpleTenantUsageClientExpectTest.java
@@ -87,7 +87,7 @@
       SimpleTenantUsage usage = client.getTenantUsage("test-1234");
       assertEquals(usage.getTenantId(), "f8535069c3fb404cb61c873b1a0b4921");
       
-      SimpleTenantUsage expected = SimpleTenantUsage.builder().totalHours(4.833333333333333E-7).totalLocalGbUsage(1.933333333333333E-05)
+      SimpleTenantUsage expected = SimpleTenantUsage.builder().tenantId("f8535069c3fb404cb61c873b1a0b4921").totalHours(4.833333333333333E-7).totalLocalGbUsage(1.933333333333333E-05)
             .start(dateService.iso8601DateParse("2012-04-18 13:32:07.255743")).stop(dateService.iso8601DateParse("2012-04-18 13:32:07.255743"))
             .totalMemoryMbUsage(0.0014847999999999999).totalVcpusUsage(7.249999999999999E-07).serverUsages(
             ImmutableSet.of(
diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/VolumeClientLiveTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/VolumeClientLiveTest.java
index 1dc2c06..ea3e981 100644
--- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/VolumeClientLiveTest.java
+++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/extensions/VolumeClientLiveTest.java
@@ -27,7 +27,6 @@
 import org.jclouds.openstack.nova.v2_0.domain.Volume;
 import org.jclouds.openstack.nova.v2_0.domain.VolumeAttachment;
 import org.jclouds.openstack.nova.v2_0.domain.VolumeSnapshot;
-import org.jclouds.openstack.nova.v2_0.extensions.VolumeClient;
 import org.jclouds.openstack.nova.v2_0.internal.BaseNovaClientLiveTest;
 import org.jclouds.openstack.nova.v2_0.options.CreateVolumeOptions;
 import org.jclouds.openstack.nova.v2_0.options.CreateVolumeSnapshotOptions;
@@ -128,7 +127,6 @@
       if (volumeOption.isPresent()) {
          Set<Volume> volumes = volumeOption.get().listVolumesInDetail();
          assertNotNull(volumes);
-         assertTrue(volumes.contains(testVolume));
          boolean foundIt = false;
          for (Volume vol : volumes) {
             Volume details = volumeOption.get().getVolume(vol.getId());
@@ -239,7 +237,7 @@
             assertTrue(new RetryablePredicate<VolumeClient>(new Predicate<VolumeClient>() {
                @Override
                public boolean apply(VolumeClient volumeClient) {
-                  return volumeOption.get().listAttachmentsOnServer(serverId).size() == before + 1;
+                  return volumeOption.get().listAttachmentsOnServer(serverId).size() > before;
                }
             }, 60 * 1000L).apply(volumeOption.get()));
 
diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/features/ServerClientExpectTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/features/ServerClientExpectTest.java
index 22a50d5..33d6f8f 100644
--- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/features/ServerClientExpectTest.java
+++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/features/ServerClientExpectTest.java
@@ -115,7 +115,7 @@
                ImmutableMultimap.<String, String> builder().put("Accept", "application/json")
                      .put("X-Auth-Token", authToken).build())
          .payload(payloadFromStringWithContentType(
-                  "{\"server\":{\"name\":\"test-e92\",\"imageRef\":\"1241\",\"flavorRef\":\"100\",\"security_groups\":[{\"name\":\"group2\"},{\"name\":\"group1\"}]}}","application/json"))
+                  "{\"server\":{\"name\":\"test-e92\",\"imageRef\":\"1241\",\"flavorRef\":\"100\",\"security_groups\":[{\"name\":\"group1\"},{\"name\":\"group2\"}]}}","application/json"))
          .build();
 
 
diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/parse/ParseComputeServiceTypicalSecurityGroupTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/parse/ParseComputeServiceTypicalSecurityGroupTest.java
index 01a6229..5828ad4 100644
--- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/parse/ParseComputeServiceTypicalSecurityGroupTest.java
+++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/parse/ParseComputeServiceTypicalSecurityGroupTest.java
@@ -54,17 +54,17 @@
    @Consumes(MediaType.APPLICATION_JSON)
    public SecurityGroup expected() {
 
-      Set<SecurityGroupRule> securityGroupRules = ImmutableSet.<SecurityGroupRule> of(
+      Set<SecurityGroupRule> securityGroupRules = ImmutableSet.of(
             SecurityGroupRule.builder().fromPort(22)
                   .ipProtocol(IpProtocol.TCP).toPort(22).parentGroupId("2769")
                   .ipRange("0.0.0.0/0").id("10331").build(),
-            SecurityGroupRule.builder().fromPort(22).group(new TenantIdAndName("37936628937291", "jclouds_mygroup"))
+            SecurityGroupRule.builder().fromPort(22).group(TenantIdAndName.builder().tenantId("37936628937291").name("jclouds_mygroup").build())
                   .ipProtocol(IpProtocol.TCP).toPort(22).parentGroupId("2769")
                   .id("10332").build(),
             SecurityGroupRule.builder().fromPort(8080)
                   .ipProtocol(IpProtocol.TCP).toPort(8080).parentGroupId("2769")
                   .ipRange("0.0.0.0/0").id("10333").build(),
-            SecurityGroupRule.builder().fromPort(8080).group(new TenantIdAndName("37936628937291", "jclouds_mygroup"))
+            SecurityGroupRule.builder().fromPort(8080).group(TenantIdAndName.builder().tenantId("37936628937291").name("jclouds_mygroup").build())
                   .ipProtocol(IpProtocol.TCP).toPort(8080).parentGroupId("2769")
                   .id("10334").build()                  
       );
diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/parse/ParseSecurityGroupTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/parse/ParseSecurityGroupTest.java
index b1d0571..f885fe7 100644
--- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/parse/ParseSecurityGroupTest.java
+++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/parse/ParseSecurityGroupTest.java
@@ -57,7 +57,7 @@
             SecurityGroupRule.builder().fromPort(22)
                   .ipProtocol(IpProtocol.TCP).toPort(22).parentGroupId("28")
                   .ipRange("10.2.6.0/24").id("108").build(),
-            SecurityGroupRule.builder().fromPort(22).group(new TenantIdAndName("admin", "11111"))
+            SecurityGroupRule.builder().fromPort(22).group(TenantIdAndName.builder().name("11111").tenantId("admin").build())
                   .ipProtocol(IpProtocol.TCP).toPort(22).parentGroupId("28")
                   .id("109").build());
 
diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/parse/ParseServerWithAllExtensionsTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/parse/ParseServerWithAllExtensionsTest.java
index 36418a7..9b165ac 100644
--- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/parse/ParseServerWithAllExtensionsTest.java
+++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/parse/ParseServerWithAllExtensionsTest.java
@@ -95,7 +95,7 @@
             .addresses(ImmutableMultimap.of("private", Address.createV4("10.0.0.8")))
             .diskConfig("MANUAL")
             .extendedStatus(ServerExtendedStatus.builder().vmState("paused").powerState(3).build())
-            .extraAttributes(ServerExtendedAttributes.builder().instanceName("instance-00000014").hostName("ubuntu").build())
+            .extendedAttributes(ServerExtendedAttributes.builder().instanceName("instance-00000014").hostName("ubuntu").build())
             .build();
    }
   
diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/predicates/SecurityGroupPredicatesTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/predicates/SecurityGroupPredicatesTest.java
index 666467c..23586ee 100644
--- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/predicates/SecurityGroupPredicatesTest.java
+++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/predicates/SecurityGroupPredicatesTest.java
@@ -29,7 +29,7 @@
  */
 @Test(groups = "unit", testName = "SecurityGroupPredicatesTest")
 public class SecurityGroupPredicatesTest {
-   SecurityGroup ref = SecurityGroup.builder().name("jclouds").description("description").build();
+   SecurityGroup ref = SecurityGroup.builder().id("12345").name("jclouds").description("description").build();
 
    @Test
    public void testnameEqualsWhenEqual() {
diff --git a/apis/pom.xml b/apis/pom.xml
index c8aa8f1..bde1678 100644
--- a/apis/pom.xml
+++ b/apis/pom.xml
@@ -53,5 +53,6 @@
        <module>cloudwatch</module>
        <module>cloudsigma</module>
        <module>cloudstack</module>
+       <module>rackspace-cloudidentity</module>
     </modules>
 </project>
diff --git a/labs/rackspace-cloudidentity/pom.xml b/apis/rackspace-cloudidentity/pom.xml
similarity index 98%
rename from labs/rackspace-cloudidentity/pom.xml
rename to apis/rackspace-cloudidentity/pom.xml
index 495d676..4931c8a 100644
--- a/labs/rackspace-cloudidentity/pom.xml
+++ b/apis/rackspace-cloudidentity/pom.xml
@@ -27,7 +27,7 @@
     <version>1.5.0-SNAPSHOT</version>
     <relativePath>../../project/pom.xml</relativePath>
   </parent>
-  <groupId>org.jclouds.labs</groupId>
+  <groupId>org.jclouds.api</groupId>
   <artifactId>rackspace-cloudidentity</artifactId>
   <name>jcloud rackspace cloudidentity api</name>
   <description>jclouds components to access an implementation of Rackspace Cloud Identity</description>
diff --git a/labs/rackspace-cloudidentity/src/main/java/org/jclouds/rackspace/cloudidentity/v2_0/CloudIdentityApiMetadata.java b/apis/rackspace-cloudidentity/src/main/java/org/jclouds/rackspace/cloudidentity/v2_0/CloudIdentityApiMetadata.java
similarity index 84%
rename from labs/rackspace-cloudidentity/src/main/java/org/jclouds/rackspace/cloudidentity/v2_0/CloudIdentityApiMetadata.java
rename to apis/rackspace-cloudidentity/src/main/java/org/jclouds/rackspace/cloudidentity/v2_0/CloudIdentityApiMetadata.java
index c0e5a32..b15dc69 100644
--- a/labs/rackspace-cloudidentity/src/main/java/org/jclouds/rackspace/cloudidentity/v2_0/CloudIdentityApiMetadata.java
+++ b/apis/rackspace-cloudidentity/src/main/java/org/jclouds/rackspace/cloudidentity/v2_0/CloudIdentityApiMetadata.java
@@ -28,8 +28,11 @@
 import org.jclouds.openstack.keystone.v2_0.KeystoneApiMetadata;
 import org.jclouds.openstack.keystone.v2_0.KeystoneAsyncClient;
 import org.jclouds.openstack.keystone.v2_0.KeystoneClient;
+import org.jclouds.openstack.keystone.v2_0.config.KeystoneParserModule;
+import org.jclouds.openstack.keystone.v2_0.config.KeystoneRestClientModule;
+import org.jclouds.openstack.keystone.v2_0.config.KeystoneRestClientModule.KeystoneAdminURLModule;
+import org.jclouds.rackspace.cloudidentity.v2_0.config.CloudIdentityAuthenticationModule;
 import org.jclouds.rackspace.cloudidentity.v2_0.config.CloudIdentityCredentialTypes;
-import org.jclouds.rackspace.cloudidentity.v2_0.config.CloudIdentityRestClientModule;
 import org.jclouds.rest.RestContext;
 
 import com.google.common.collect.ImmutableSet;
@@ -85,7 +88,11 @@
          .defaultProperties(CloudIdentityApiMetadata.defaultProperties())
          .context(CONTEXT_TOKEN)
          .documentation(URI.create("http://docs.rackspace.com/auth/api/v2.0/auth-client-devguide/"))
-         .defaultModules(ImmutableSet.<Class<? extends Module>>of(CloudIdentityRestClientModule.class));
+         .defaultModules(ImmutableSet.<Class<? extends Module>>builder()
+                                     .add(CloudIdentityAuthenticationModule.class)
+                                     .add(KeystoneAdminURLModule.class)
+                                     .add(KeystoneParserModule.class)
+                                     .add(KeystoneRestClientModule.class).build());
       }
       
       @Override
diff --git a/labs/rackspace-cloudidentity/src/main/java/org/jclouds/rackspace/cloudidentity/v2_0/CloudIdentityAuthenticationAsyncClient.java b/apis/rackspace-cloudidentity/src/main/java/org/jclouds/rackspace/cloudidentity/v2_0/CloudIdentityAuthenticationAsyncClient.java
similarity index 100%
rename from labs/rackspace-cloudidentity/src/main/java/org/jclouds/rackspace/cloudidentity/v2_0/CloudIdentityAuthenticationAsyncClient.java
rename to apis/rackspace-cloudidentity/src/main/java/org/jclouds/rackspace/cloudidentity/v2_0/CloudIdentityAuthenticationAsyncClient.java
diff --git a/labs/rackspace-cloudidentity/src/main/java/org/jclouds/rackspace/cloudidentity/v2_0/CloudIdentityAuthenticationClient.java b/apis/rackspace-cloudidentity/src/main/java/org/jclouds/rackspace/cloudidentity/v2_0/CloudIdentityAuthenticationClient.java
similarity index 100%
rename from labs/rackspace-cloudidentity/src/main/java/org/jclouds/rackspace/cloudidentity/v2_0/CloudIdentityAuthenticationClient.java
rename to apis/rackspace-cloudidentity/src/main/java/org/jclouds/rackspace/cloudidentity/v2_0/CloudIdentityAuthenticationClient.java
diff --git a/labs/rackspace-cloudidentity/src/main/java/org/jclouds/rackspace/cloudidentity/v2_0/config/CloudIdentityAuthenticationModule.java b/apis/rackspace-cloudidentity/src/main/java/org/jclouds/rackspace/cloudidentity/v2_0/config/CloudIdentityAuthenticationModule.java
similarity index 77%
rename from labs/rackspace-cloudidentity/src/main/java/org/jclouds/rackspace/cloudidentity/v2_0/config/CloudIdentityAuthenticationModule.java
rename to apis/rackspace-cloudidentity/src/main/java/org/jclouds/rackspace/cloudidentity/v2_0/config/CloudIdentityAuthenticationModule.java
index 3e5ac02..f0e374c 100644
--- a/labs/rackspace-cloudidentity/src/main/java/org/jclouds/rackspace/cloudidentity/v2_0/config/CloudIdentityAuthenticationModule.java
+++ b/apis/rackspace-cloudidentity/src/main/java/org/jclouds/rackspace/cloudidentity/v2_0/config/CloudIdentityAuthenticationModule.java
@@ -37,7 +37,6 @@
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.ImmutableSet.Builder;
 import com.google.inject.Injector;
-import com.google.inject.Module;
 import com.google.inject.Scopes;
 
 /**
@@ -46,34 +45,6 @@
  */
 public class CloudIdentityAuthenticationModule extends KeystoneAuthenticationModule {
 
-   public CloudIdentityAuthenticationModule() {
-      this(new RegionModule());
-   }
-
-   protected CloudIdentityAuthenticationModule(Module locationModule) {
-      super(locationModule);
-   }
-
-   public static class CloudIdentityAuthenticationModuleForRegions extends CloudIdentityAuthenticationModule {
-      public CloudIdentityAuthenticationModuleForRegions() {
-         super(new RegionModule());
-      }
-   }
-
-   public static Module forRegions() {
-      return new CloudIdentityAuthenticationModuleForRegions();
-   }
-
-   public static class CloudIdentityAuthenticationModuleForZones extends CloudIdentityAuthenticationModule {
-      public CloudIdentityAuthenticationModuleForZones() {
-         super(new ZoneModule());
-      }
-   }
-
-   public static Module forZones() {
-      return new CloudIdentityAuthenticationModuleForZones();
-   }
-
    @Override
    protected void bindAuthenticationClient() {
       // AuthenticationClient is used directly for filters and retry handlers, so let's bind it
diff --git a/labs/rackspace-cloudidentity/src/main/java/org/jclouds/rackspace/cloudidentity/v2_0/config/CloudIdentityCredentialTypes.java b/apis/rackspace-cloudidentity/src/main/java/org/jclouds/rackspace/cloudidentity/v2_0/config/CloudIdentityCredentialTypes.java
similarity index 100%
rename from labs/rackspace-cloudidentity/src/main/java/org/jclouds/rackspace/cloudidentity/v2_0/config/CloudIdentityCredentialTypes.java
rename to apis/rackspace-cloudidentity/src/main/java/org/jclouds/rackspace/cloudidentity/v2_0/config/CloudIdentityCredentialTypes.java
diff --git a/labs/rackspace-cloudidentity/src/main/java/org/jclouds/rackspace/cloudidentity/v2_0/domain/ApiKeyCredentials.java b/apis/rackspace-cloudidentity/src/main/java/org/jclouds/rackspace/cloudidentity/v2_0/domain/ApiKeyCredentials.java
similarity index 100%
rename from labs/rackspace-cloudidentity/src/main/java/org/jclouds/rackspace/cloudidentity/v2_0/domain/ApiKeyCredentials.java
rename to apis/rackspace-cloudidentity/src/main/java/org/jclouds/rackspace/cloudidentity/v2_0/domain/ApiKeyCredentials.java
diff --git a/labs/rackspace-cloudidentity/src/main/java/org/jclouds/rackspace/cloudidentity/v2_0/functions/AuthenticateApiKeyCredentials.java b/apis/rackspace-cloudidentity/src/main/java/org/jclouds/rackspace/cloudidentity/v2_0/functions/AuthenticateApiKeyCredentials.java
similarity index 100%
rename from labs/rackspace-cloudidentity/src/main/java/org/jclouds/rackspace/cloudidentity/v2_0/functions/AuthenticateApiKeyCredentials.java
rename to apis/rackspace-cloudidentity/src/main/java/org/jclouds/rackspace/cloudidentity/v2_0/functions/AuthenticateApiKeyCredentials.java
diff --git a/labs/rackspace-cloudidentity/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata b/apis/rackspace-cloudidentity/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata
similarity index 100%
rename from labs/rackspace-cloudidentity/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata
rename to apis/rackspace-cloudidentity/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata
diff --git a/labs/rackspace-cloudidentity/src/test/java/org/jclouds/rackspace/cloudidentity/v2_0/CloudIdentityApiMetadataTest.java b/apis/rackspace-cloudidentity/src/test/java/org/jclouds/rackspace/cloudidentity/v2_0/CloudIdentityApiMetadataTest.java
similarity index 100%
rename from labs/rackspace-cloudidentity/src/test/java/org/jclouds/rackspace/cloudidentity/v2_0/CloudIdentityApiMetadataTest.java
rename to apis/rackspace-cloudidentity/src/test/java/org/jclouds/rackspace/cloudidentity/v2_0/CloudIdentityApiMetadataTest.java
diff --git a/labs/rackspace-cloudidentity/src/test/java/org/jclouds/rackspace/cloudidentity/v2_0/CloudIdentityClientLiveTest.java b/apis/rackspace-cloudidentity/src/test/java/org/jclouds/rackspace/cloudidentity/v2_0/CloudIdentityClientLiveTest.java
similarity index 100%
rename from labs/rackspace-cloudidentity/src/test/java/org/jclouds/rackspace/cloudidentity/v2_0/CloudIdentityClientLiveTest.java
rename to apis/rackspace-cloudidentity/src/test/java/org/jclouds/rackspace/cloudidentity/v2_0/CloudIdentityClientLiveTest.java
diff --git a/labs/rackspace-cloudidentity/src/test/java/org/jclouds/rackspace/cloudidentity/v2_0/features/CloudIdentityServiceClientLiveTest.java b/apis/rackspace-cloudidentity/src/test/java/org/jclouds/rackspace/cloudidentity/v2_0/features/CloudIdentityServiceClientLiveTest.java
similarity index 100%
rename from labs/rackspace-cloudidentity/src/test/java/org/jclouds/rackspace/cloudidentity/v2_0/features/CloudIdentityServiceClientLiveTest.java
rename to apis/rackspace-cloudidentity/src/test/java/org/jclouds/rackspace/cloudidentity/v2_0/features/CloudIdentityServiceClientLiveTest.java
diff --git a/labs/rackspace-cloudidentity/src/test/java/org/jclouds/rackspace/cloudidentity/v2_0/features/CloudIdentityTenantClientLiveTest.java b/apis/rackspace-cloudidentity/src/test/java/org/jclouds/rackspace/cloudidentity/v2_0/features/CloudIdentityTenantClientLiveTest.java
similarity index 100%
rename from labs/rackspace-cloudidentity/src/test/java/org/jclouds/rackspace/cloudidentity/v2_0/features/CloudIdentityTenantClientLiveTest.java
rename to apis/rackspace-cloudidentity/src/test/java/org/jclouds/rackspace/cloudidentity/v2_0/features/CloudIdentityTenantClientLiveTest.java
diff --git a/labs/rackspace-cloudidentity/src/test/java/org/jclouds/rackspace/cloudidentity/v2_0/features/CloudIdentityTokenClientLiveTest.java b/apis/rackspace-cloudidentity/src/test/java/org/jclouds/rackspace/cloudidentity/v2_0/features/CloudIdentityTokenClientLiveTest.java
similarity index 100%
rename from labs/rackspace-cloudidentity/src/test/java/org/jclouds/rackspace/cloudidentity/v2_0/features/CloudIdentityTokenClientLiveTest.java
rename to apis/rackspace-cloudidentity/src/test/java/org/jclouds/rackspace/cloudidentity/v2_0/features/CloudIdentityTokenClientLiveTest.java
diff --git a/labs/rackspace-cloudidentity/src/test/java/org/jclouds/rackspace/cloudidentity/v2_0/features/CloudIdentityUserClientLiveTest.java b/apis/rackspace-cloudidentity/src/test/java/org/jclouds/rackspace/cloudidentity/v2_0/features/CloudIdentityUserClientLiveTest.java
similarity index 100%
rename from labs/rackspace-cloudidentity/src/test/java/org/jclouds/rackspace/cloudidentity/v2_0/features/CloudIdentityUserClientLiveTest.java
rename to apis/rackspace-cloudidentity/src/test/java/org/jclouds/rackspace/cloudidentity/v2_0/features/CloudIdentityUserClientLiveTest.java
diff --git a/apis/s3/src/main/java/org/jclouds/s3/binders/BindAsHostPrefixIfConfigured.java b/apis/s3/src/main/java/org/jclouds/s3/binders/BindAsHostPrefixIfConfigured.java
index 321910f..465b8c2 100644
--- a/apis/s3/src/main/java/org/jclouds/s3/binders/BindAsHostPrefixIfConfigured.java
+++ b/apis/s3/src/main/java/org/jclouds/s3/binders/BindAsHostPrefixIfConfigured.java
@@ -63,7 +63,14 @@
    @SuppressWarnings("unchecked")
    @Override
    public <R extends HttpRequest> R bindToRequest(R request, Object payload) {
-      if (isVhostStyle) {
+      // If we have a payload/bucket/container that is not all lowercase, vhost-style URLs are not an option and must be
+      // automatically converted to their path-based equivalent.  This should only be possible for AWS-S3 since it is
+      // the only S3 implementation configured to allow uppercase payload/bucket/container names.
+      //
+      // http://code.google.com/p/jclouds/issues/detail?id=992
+      String payloadAsString = payload.toString();
+
+      if (isVhostStyle && payloadAsString.equals(payloadAsString.toLowerCase())) {
          request = bindAsHostPrefix.bindToRequest(request, payload);
          String host = request.getEndpoint().getHost();
          if (request.getEndpoint().getPort() != -1) {
@@ -80,7 +87,7 @@
             indexToInsert = indexToInsert == -1 ? 0 : indexToInsert;
             indexToInsert += servicePath.length();
          }
-         path.insert(indexToInsert, "/" + payload.toString());
+         path.insert(indexToInsert, "/" + payloadAsString);
          builder.replacePath(path.toString());
          return (R) request.toBuilder().endpoint(builder.buildFromEncodedMap(ImmutableMap.<String, Object> of()))
                   .build();
diff --git a/apis/s3/src/main/java/org/jclouds/s3/filters/RequestAuthorizeSignature.java b/apis/s3/src/main/java/org/jclouds/s3/filters/RequestAuthorizeSignature.java
index 74876d1..f6a2d20 100644
--- a/apis/s3/src/main/java/org/jclouds/s3/filters/RequestAuthorizeSignature.java
+++ b/apis/s3/src/main/java/org/jclouds/s3/filters/RequestAuthorizeSignature.java
@@ -33,8 +33,8 @@
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Locale;
-import java.util.Set;
 import java.util.Map.Entry;
+import java.util.Set;
 
 import javax.annotation.Resource;
 import javax.inject.Inject;
@@ -161,8 +161,7 @@
       }
 
       appendAmzHeaders(canonicalizedHeaders, buffer);
-      if (isVhostStyle)
-         appendBucketName(request, buffer);
+      appendBucketName(request, buffer);
       appendUriPath(request, buffer);
       if (signatureWire.enabled())
          signatureWire.output(buffer.toString());
@@ -232,19 +231,14 @@
 
    @VisibleForTesting
    void appendBucketName(HttpRequest req, StringBuilder toSign) {
-      checkArgument(req instanceof GeneratedHttpRequest<?>, "this should be a generated http request");
-      GeneratedHttpRequest<?> request = GeneratedHttpRequest.class.cast(req);
+      String bucketName = getBucketName(req);
 
-      String bucketName = null;
-
-      for (int i = 0; i < request.getJavaMethod().getParameterAnnotations().length; i++) {
-         if (any(Arrays.asList(request.getJavaMethod().getParameterAnnotations()[i]), ANNOTATIONTYPE_BUCKET)) {
-            bucketName = (String) request.getArgs().get(i);
-            break;
-         }
-      }
-
-      if (bucketName != null)
+      // If we have a payload/bucket/container that is not all lowercase, vhost-style URLs are not an option and must be
+      // automatically converted to their path-based equivalent.  This should only be possible for AWS-S3 since it is
+      // the only S3 implementation configured to allow uppercase payload/bucket/container names.
+      //
+      // http://code.google.com/p/jclouds/issues/detail?id=992
+      if (isVhostStyle && bucketName!= null && bucketName.equals(bucketName.toLowerCase()))
          toSign.append(servicePath).append(bucketName);
    }
 
@@ -271,4 +265,21 @@
          }
       }
    }
+
+   private String getBucketName(HttpRequest req) {
+      checkArgument(req instanceof GeneratedHttpRequest<?>, "this should be a generated http request");
+      GeneratedHttpRequest<?> request = GeneratedHttpRequest.class.cast(req);
+
+      String bucketName = null;
+
+      for (int i = 0; i < request.getJavaMethod().getParameterAnnotations().length; i++) {
+         if (any(Arrays.asList(request.getJavaMethod().getParameterAnnotations()[i]), ANNOTATIONTYPE_BUCKET)) {
+            bucketName = (String) request.getArgs().get(i);
+            break;
+         }
+      }
+
+      return bucketName;
+   }
+
 }
diff --git a/apis/s3/src/main/java/org/jclouds/s3/handlers/ParseS3ErrorFromXmlContent.java b/apis/s3/src/main/java/org/jclouds/s3/handlers/ParseS3ErrorFromXmlContent.java
index ecf1c3f..62c9fc4 100644
--- a/apis/s3/src/main/java/org/jclouds/s3/handlers/ParseS3ErrorFromXmlContent.java
+++ b/apis/s3/src/main/java/org/jclouds/s3/handlers/ParseS3ErrorFromXmlContent.java
@@ -25,12 +25,14 @@
 import static org.jclouds.s3.reference.S3Constants.PROPERTY_S3_SERVICE_PATH;
 import static org.jclouds.s3.reference.S3Constants.PROPERTY_S3_VIRTUAL_HOST_BUCKETS;
 
+import java.net.URI;
 import java.util.List;
-
 import javax.inject.Inject;
 import javax.inject.Named;
 import javax.inject.Singleton;
 
+import com.google.common.base.Joiner;
+import com.google.common.base.Splitter;
 import org.jclouds.aws.domain.AWSError;
 import org.jclouds.aws.handlers.ParseAWSErrorFromXmlContent;
 import org.jclouds.aws.util.AWSUtils;
@@ -39,9 +41,7 @@
 import org.jclouds.http.HttpCommand;
 import org.jclouds.http.HttpResponse;
 import org.jclouds.rest.ResourceNotFoundException;
-
-import com.google.common.base.Joiner;
-import com.google.common.base.Splitter;
+import org.jclouds.s3.S3ApiMetadata;
 
 /**
  * @author Adrian Cole
@@ -54,7 +54,7 @@
    private final boolean isVhostStyle;
 
    @Inject
-   ParseS3ErrorFromXmlContent(AWSUtils utils, @Named(PROPERTY_S3_VIRTUAL_HOST_BUCKETS) boolean isVhostStyle,
+   public ParseS3ErrorFromXmlContent(AWSUtils utils, @Named(PROPERTY_S3_VIRTUAL_HOST_BUCKETS) boolean isVhostStyle,
             @Named(PROPERTY_S3_SERVICE_PATH) String servicePath) {
       super(utils);
       this.servicePath = servicePath;
@@ -66,8 +66,19 @@
       switch (response.getStatusCode()) {
          case 404:
             if (!command.getCurrentRequest().getMethod().equals("DELETE")) {
+               // If we have a payload/bucket/container that is not all lowercase, vhost-style URLs are not an option
+               // and must be automatically converted to their path-based equivalent.  This should only be possible for
+               // AWS-S3 since it is the only S3 implementation configured to allow uppercase payload/bucket/container
+               // names.
+               //
+               // http://code.google.com/p/jclouds/issues/detail?id=992
+               URI defaultS3Endpoint = URI.create(new S3ApiMetadata().getDefaultEndpoint().get());
+               URI requestEndpoint = command.getCurrentRequest().getEndpoint();
+               boolean wasPathBasedRequest = requestEndpoint.getHost().contains(defaultS3Endpoint.getHost()) &&
+                     requestEndpoint.getHost().equals(defaultS3Endpoint.getHost());
+
                exception = new ResourceNotFoundException(message, exception);
-               if (isVhostStyle) {
+               if (isVhostStyle && !wasPathBasedRequest) {
                   String container = command.getCurrentRequest().getEndpoint().getHost();
                   String key = command.getCurrentRequest().getEndpoint().getPath();
                   if (key == null || key.equals("/"))
diff --git a/apis/s3/src/main/java/org/jclouds/s3/predicates/validators/BucketNameValidator.java b/apis/s3/src/main/java/org/jclouds/s3/predicates/validators/BucketNameValidator.java
index acf473f..ed441fe 100644
--- a/apis/s3/src/main/java/org/jclouds/s3/predicates/validators/BucketNameValidator.java
+++ b/apis/s3/src/main/java/org/jclouds/s3/predicates/validators/BucketNameValidator.java
@@ -40,7 +40,7 @@
 public class BucketNameValidator extends DnsNameValidator {
 
    @Inject
-   BucketNameValidator() {
+   public BucketNameValidator() {
       super(3, 63);
    }
 
diff --git a/apis/swift/src/main/java/org/jclouds/openstack/swift/config/SwiftRestClientModule.java b/apis/swift/src/main/java/org/jclouds/openstack/swift/config/SwiftRestClientModule.java
index 983b9d8..830aafb 100644
--- a/apis/swift/src/main/java/org/jclouds/openstack/swift/config/SwiftRestClientModule.java
+++ b/apis/swift/src/main/java/org/jclouds/openstack/swift/config/SwiftRestClientModule.java
@@ -34,7 +34,7 @@
 import org.jclouds.location.suppliers.RegionIdToURISupplier;
 import org.jclouds.openstack.config.OpenStackAuthenticationModule;
 import org.jclouds.openstack.functions.URIFromAuthenticationResponseForService;
-import org.jclouds.openstack.keystone.v2_0.config.KeystoneAuthenticationModule.KeystoneAuthenticationModuleForRegions;
+import org.jclouds.openstack.keystone.v2_0.config.KeystoneAuthenticationModule;
 import org.jclouds.openstack.reference.AuthHeaders;
 import org.jclouds.openstack.services.ServiceType;
 import org.jclouds.openstack.swift.CommonSwiftAsyncClient;
@@ -62,8 +62,8 @@
 
    @SuppressWarnings("unchecked")
    public SwiftRestClientModule() {
-      this((TypeToken) TypeToken.of(SwiftClient.class), (TypeToken) TypeToken.of(SwiftAsyncClient.class), ImmutableMap
-               .<Class<?>, Class<?>> of());
+      this(TypeToken.class.cast(TypeToken.of(SwiftClient.class)), TypeToken.class.cast(TypeToken
+               .of(SwiftAsyncClient.class)), ImmutableMap.<Class<?>, Class<?>> of());
    }
 
    protected SwiftRestClientModule(TypeToken<S> syncClientType, TypeToken<A> asyncClientType,
@@ -80,7 +80,7 @@
       }
    }
 
-   public static class KeystoneStorageEndpointModule extends KeystoneAuthenticationModuleForRegions {
+   public static class KeystoneStorageEndpointModule extends KeystoneAuthenticationModule {
 
       @Provides
       @Singleton
diff --git a/apis/vcloud/src/main/java/org/jclouds/vcloud/compute/config/VCloudComputeServiceDependenciesModule.java b/apis/vcloud/src/main/java/org/jclouds/vcloud/compute/config/VCloudComputeServiceDependenciesModule.java
index 0a0d712..a2824a3 100644
--- a/apis/vcloud/src/main/java/org/jclouds/vcloud/compute/config/VCloudComputeServiceDependenciesModule.java
+++ b/apis/vcloud/src/main/java/org/jclouds/vcloud/compute/config/VCloudComputeServiceDependenciesModule.java
@@ -31,6 +31,7 @@
 import org.jclouds.compute.options.TemplateOptions;
 import org.jclouds.domain.Location;
 import org.jclouds.functions.IdentityFunction;
+import org.jclouds.util.Suppliers2;
 import org.jclouds.vcloud.compute.functions.HardwareForVApp;
 import org.jclouds.vcloud.compute.functions.HardwareForVAppTemplate;
 import org.jclouds.vcloud.compute.functions.ImageForVAppTemplate;
@@ -51,7 +52,6 @@
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Function;
 import com.google.common.base.Supplier;
-import com.google.common.base.Suppliers;
 import com.google.common.collect.ImmutableMap;
 import com.google.inject.AbstractModule;
 import com.google.inject.Provides;
@@ -143,7 +143,7 @@
    @Singleton
    public Supplier<NetworkConfig> networkConfig(@Network Supplier<ReferenceType> network,
          final FenceMode defaultFenceMode) {
-      return Suppliers.compose(new Function<ReferenceType, NetworkConfig>() {
+      return Suppliers2.compose(new Function<ReferenceType, NetworkConfig>() {
 
          @Override
          public NetworkConfig apply(ReferenceType input) {
diff --git a/apis/vcloud/src/main/java/org/jclouds/vcloud/compute/strategy/VCloudComputeServiceAdapter.java b/apis/vcloud/src/main/java/org/jclouds/vcloud/compute/strategy/VCloudComputeServiceAdapter.java
index cb508a3..7b779f5 100644
--- a/apis/vcloud/src/main/java/org/jclouds/vcloud/compute/strategy/VCloudComputeServiceAdapter.java
+++ b/apis/vcloud/src/main/java/org/jclouds/vcloud/compute/strategy/VCloudComputeServiceAdapter.java
@@ -36,6 +36,7 @@
 import org.jclouds.domain.Location;
 import org.jclouds.logging.Logger;
 import org.jclouds.ovf.Envelope;
+import org.jclouds.util.Throwables2;
 import org.jclouds.vcloud.TaskInErrorStateException;
 import org.jclouds.vcloud.TaskStillRunningException;
 import org.jclouds.vcloud.VCloudClient;
@@ -109,6 +110,14 @@
             } catch (IllegalArgumentException e){
                logger.warn("Unsupported: "+ e.getMessage());
                return false;
+            } catch (RuntimeException e) {
+               IllegalArgumentException e2 = Throwables2.getFirstThrowableOfType(e, IllegalArgumentException.class);
+               if (e2 != null) {
+                  logger.warn("Unsupported: "+ e2.getMessage());
+                  return false;
+               } else {
+                  throw e;
+               }
             }
             return true;
          }
diff --git a/apis/vcloud/src/main/java/org/jclouds/vcloud/config/DefaultOrgForUser.java b/apis/vcloud/src/main/java/org/jclouds/vcloud/config/DefaultOrgForUser.java
index 143e9d6..f0ce1b6 100644
--- a/apis/vcloud/src/main/java/org/jclouds/vcloud/config/DefaultOrgForUser.java
+++ b/apis/vcloud/src/main/java/org/jclouds/vcloud/config/DefaultOrgForUser.java
@@ -26,6 +26,7 @@
 import javax.inject.Singleton;
 
 import org.jclouds.config.ValueOfConfigurationKeyOrNull;
+import org.jclouds.util.Suppliers2;
 import org.jclouds.vcloud.domain.ReferenceType;
 import org.jclouds.vcloud.domain.VCloudSession;
 import org.jclouds.vcloud.endpoints.Org;
@@ -34,7 +35,6 @@
 import com.google.common.base.Function;
 import com.google.common.base.Predicate;
 import com.google.common.base.Supplier;
-import com.google.common.base.Suppliers;
 
 /**
  * 
@@ -57,7 +57,7 @@
 
    @Override
    public Supplier<ReferenceType> apply(final String user) {
-      return Suppliers.compose(new Function<VCloudSession, ReferenceType>() {
+      return Suppliers2.compose(new Function<VCloudSession, ReferenceType>() {
 
          @Override
          public ReferenceType apply(VCloudSession session) {
diff --git a/apis/vcloud/src/main/java/org/jclouds/vcloud/config/DefaultVCloudReferencesModule.java b/apis/vcloud/src/main/java/org/jclouds/vcloud/config/DefaultVCloudReferencesModule.java
index fe8c408..099d4e4 100644
--- a/apis/vcloud/src/main/java/org/jclouds/vcloud/config/DefaultVCloudReferencesModule.java
+++ b/apis/vcloud/src/main/java/org/jclouds/vcloud/config/DefaultVCloudReferencesModule.java
@@ -27,6 +27,7 @@
 
 import org.jclouds.logging.Logger;
 import org.jclouds.rest.annotations.Identity;
+import org.jclouds.util.Suppliers2;
 import org.jclouds.vcloud.domain.Catalog;
 import org.jclouds.vcloud.domain.ReferenceType;
 
@@ -72,7 +73,7 @@
    @Singleton
    protected Supplier<ReferenceType> provideDefaultTasksList(DefaultTasksListForOrg defaultTasksListURIForOrg,
             @org.jclouds.vcloud.endpoints.Org Supplier<ReferenceType> defaultOrg) {
-      return Suppliers.compose(defaultTasksListURIForOrg, defaultOrg);
+      return Suppliers2.compose(defaultTasksListURIForOrg, defaultOrg);
    }
 
    @Provides
@@ -80,7 +81,7 @@
    @Singleton
    protected Supplier<ReferenceType> provideDefaultCatalog(DefaultCatalogForOrg defaultCatalogURIForOrg,
             @org.jclouds.vcloud.endpoints.Org Supplier<ReferenceType> defaultOrg) {
-      return Suppliers.compose(defaultCatalogURIForOrg, defaultOrg);
+      return Suppliers2.compose(defaultCatalogURIForOrg, defaultOrg);
    }
 
    @Provides
@@ -146,7 +147,7 @@
    @Singleton
    protected Supplier<ReferenceType> provideDefaultVDC(DefaultVDCForOrg defaultVDCURIForOrg,
             @org.jclouds.vcloud.endpoints.Org Supplier<ReferenceType> defaultOrg) {
-      return Suppliers.compose(defaultVDCURIForOrg, defaultOrg);
+      return Suppliers2.compose(defaultVDCURIForOrg, defaultOrg);
    }
 
    @Provides
@@ -161,7 +162,7 @@
    @Singleton
    protected Supplier<ReferenceType> provideDefaultNetwork(DefaultNetworkForVDC defaultNetworkURIForVDC,
             @org.jclouds.vcloud.endpoints.VDC Supplier<ReferenceType> defaultVDC) {
-      return Suppliers.compose(defaultNetworkURIForVDC, defaultVDC);
+      return Suppliers2.compose(defaultNetworkURIForVDC, defaultVDC);
    }
 
    @Provides
diff --git a/apis/vcloud/src/main/java/org/jclouds/vcloud/config/VCloudRestClientModule.java b/apis/vcloud/src/main/java/org/jclouds/vcloud/config/VCloudRestClientModule.java
index c84b1a3..1040931 100644
--- a/apis/vcloud/src/main/java/org/jclouds/vcloud/config/VCloudRestClientModule.java
+++ b/apis/vcloud/src/main/java/org/jclouds/vcloud/config/VCloudRestClientModule.java
@@ -21,7 +21,6 @@
 import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.base.Preconditions.checkState;
 import static com.google.common.base.Predicates.notNull;
-import static com.google.common.base.Suppliers.compose;
 import static com.google.common.collect.Iterables.concat;
 import static com.google.common.collect.Iterables.filter;
 import static com.google.common.collect.Iterables.getLast;
@@ -35,8 +34,9 @@
 
 import java.net.URI;
 import java.util.Map;
-import java.util.SortedMap;
 import java.util.Map.Entry;
+import java.util.SortedMap;
+import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicReference;
 
 import javax.inject.Inject;
@@ -58,6 +58,7 @@
 import org.jclouds.rest.annotations.ApiVersion;
 import org.jclouds.rest.config.RestClientModule;
 import org.jclouds.rest.suppliers.MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier;
+import org.jclouds.util.Suppliers2;
 import org.jclouds.vcloud.VCloudAsyncClient;
 import org.jclouds.vcloud.VCloudClient;
 import org.jclouds.vcloud.VCloudToken;
@@ -110,15 +111,15 @@
 import org.jclouds.vcloud.xml.ovf.VCloudResourceAllocationSettingDataHandler;
 
 import com.google.common.base.Function;
+import com.google.common.base.Objects;
 import com.google.common.base.Predicate;
 import com.google.common.base.Supplier;
-import com.google.common.base.Suppliers;
 import com.google.common.cache.CacheBuilder;
 import com.google.common.cache.CacheLoader;
 import com.google.common.cache.LoadingCache;
 import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Lists;
 import com.google.common.collect.ImmutableMap.Builder;
+import com.google.common.collect.Lists;
 import com.google.inject.Injector;
 import com.google.inject.Provides;
 import com.google.inject.Scopes;
@@ -151,15 +152,19 @@
    @Singleton
    protected Supplier<VCloudSession> provideVCloudTokenCache(@Named(PROPERTY_SESSION_INTERVAL) long seconds,
             AtomicReference<AuthorizationException> authException, final VCloudLoginClient login) {
-      return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier<VCloudSession>(authException, seconds,
+      return MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(authException,
                new Supplier<VCloudSession>() {
 
                   @Override
                   public VCloudSession get() {
                      return login.login();
                   }
-
-               });
+                  
+                  @Override
+                  public String toString() {
+                     return Objects.toStringHelper(login).add("method", "login").toString();
+                  }
+               }, seconds, TimeUnit.SECONDS);
    }
 
    @Override
@@ -219,7 +224,7 @@
    @Singleton
    @org.jclouds.vcloud.endpoints.VDC
    protected Supplier<Map<String, String>> provideVDCtoORG(Supplier<Map<String, Org>> orgNameToOrgSuppier) {
-      return compose(new Function<Map<String, Org>, Map<String, String>>() {
+      return Suppliers2.compose(new Function<Map<String, Org>, Map<String, String>>() {
 
          @Override
          public Map<String, String> apply(Map<String, Org> arg0) {
@@ -239,15 +244,15 @@
    @Singleton
    protected Supplier<Map<String, Org>> provideOrgMapCache(@Named(PROPERTY_SESSION_INTERVAL) long seconds,
             AtomicReference<AuthorizationException> authException, OrgMapSupplier supplier) {
-      return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier<Map<String, Org>>(authException, seconds,
-               supplier);
+      return MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(authException, supplier, seconds,
+               TimeUnit.SECONDS);
    }
 
    @Provides
    @Singleton
    @OrgList
    protected Supplier<URI> provideOrgListURI(Supplier<VCloudSession> sessionSupplier) {
-      return Suppliers.compose(new Function<VCloudSession, URI>() {
+      return Suppliers2.compose(new Function<VCloudSession, URI>() {
 
          @Override
          public URI apply(VCloudSession arg0) {
@@ -318,7 +323,7 @@
    @Provides
    @Singleton
    Supplier<String> provideVCloudToken(Supplier<VCloudSession> cache) {
-      return Suppliers.compose(new Function<VCloudSession, String>() {
+      return Suppliers2.compose(new Function<VCloudSession, String>() {
 
          @Override
          public String apply(VCloudSession input) {
@@ -332,8 +337,8 @@
    @Singleton
    protected Supplier<Map<String, ReferenceType>> provideVDCtoORG(@Named(PROPERTY_SESSION_INTERVAL) long seconds,
             AtomicReference<AuthorizationException> authException, OrgNameToOrgSupplier supplier) {
-      return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier<Map<String, ReferenceType>>(
-               authException, seconds, supplier);
+      return MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(authException, supplier, seconds,
+               TimeUnit.SECONDS);
    }
 
    @Provides
@@ -341,8 +346,8 @@
    protected Supplier<Map<URI, VDC>> provideURIToVDC(
             @Named(PROPERTY_SESSION_INTERVAL) long seconds, AtomicReference<AuthorizationException> authException,
             URItoVDC supplier) {
-      return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier<Map<URI, VDC>>(
-               authException, seconds, supplier);
+      return MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(authException, supplier, seconds,
+               TimeUnit.SECONDS);
    }
 
    @Singleton
@@ -411,7 +416,6 @@
    private static class OrgNameToOrgSupplier implements Supplier<Map<String, ReferenceType>> {
       private final Supplier<VCloudSession> sessionSupplier;
 
-      @SuppressWarnings("unused")
       @Inject
       OrgNameToOrgSupplier(Supplier<VCloudSession> sessionSupplier) {
          this.sessionSupplier = sessionSupplier;
@@ -428,7 +432,7 @@
    @Singleton
    protected Supplier<Org> provideOrg(final Supplier<Map<String, Org>> orgSupplier,
          @org.jclouds.vcloud.endpoints.Org Supplier<ReferenceType> defaultOrg) {
-      return Suppliers.compose(new Function<ReferenceType, Org>() {
+      return Suppliers2.compose(new Function<ReferenceType, Org>() {
 
          @Override
          public Org apply(ReferenceType input) {
@@ -450,8 +454,8 @@
    protected Supplier<Map<String, Map<String, Catalog>>> provideOrgCatalogItemMapSupplierCache(
             @Named(PROPERTY_SESSION_INTERVAL) long seconds, AtomicReference<AuthorizationException> authException,
             OrgCatalogSupplier supplier) {
-      return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier<Map<String, Map<String, Catalog>>>(
-               authException, seconds, supplier);
+      return MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(authException, supplier, seconds,
+               TimeUnit.SECONDS);
    }
 
    @Provides
@@ -459,8 +463,8 @@
    protected Supplier<Map<String, Map<String, VDC>>> provideOrgVDCSupplierCache(
             @Named(PROPERTY_SESSION_INTERVAL) long seconds, AtomicReference<AuthorizationException> authException,
             OrgVDCSupplier supplier) {
-      return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier<Map<String, Map<String, VDC>>>(
-               authException, seconds, supplier);
+      return MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(authException, supplier, seconds,
+               TimeUnit.SECONDS);
    }
 
    @Singleton
@@ -533,8 +537,8 @@
    protected Supplier<Map<String, Map<String, Map<String, CatalogItem>>>> provideOrgCatalogItemSupplierCache(
             @Named(PROPERTY_SESSION_INTERVAL) long seconds, AtomicReference<AuthorizationException> authException,
             OrgCatalogItemSupplier supplier) {
-      return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier<Map<String, Map<String, Map<String, CatalogItem>>>>(
-               authException, seconds, supplier);
+      return MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(authException, supplier, seconds,
+               TimeUnit.SECONDS);
    }
 
    @Provides
diff --git a/apis/vcloud/src/main/java/org/jclouds/vcloud/xml/CatalogHandler.java b/apis/vcloud/src/main/java/org/jclouds/vcloud/xml/CatalogHandler.java
index d2ec855..b3e5d35 100644
--- a/apis/vcloud/src/main/java/org/jclouds/vcloud/xml/CatalogHandler.java
+++ b/apis/vcloud/src/main/java/org/jclouds/vcloud/xml/CatalogHandler.java
@@ -70,13 +70,13 @@
    @Override
    public void startElement(String uri, String localName, String qName, Attributes attrs) throws SAXException {
       Map<String, String> attributes = SaxUtils.cleanseAttributes(attrs);
-      if (qName.equals("Catalog")) {
+      if (SaxUtils.equalsOrSuffix(qName, "Catalog")) {
          catalog = newReferenceType(attributes, VCloudMediaType.CATALOG_XML);
-      } else if (qName.equals("CatalogItem")) {
+      } else if (SaxUtils.equalsOrSuffix(qName, "CatalogItem")) {
          putReferenceType(contents, attributes);
-      } else if (qName.equals("Link") && "up".equals(attributes.get("rel"))) {
+      } else if (SaxUtils.equalsOrSuffix(qName, "Link") && "up".equals(attributes.get("rel"))) {
          org = newReferenceType(attributes);
-      } else if (qName.equals("Link") && "add".equals(attributes.get("rel"))) {
+      } else if (SaxUtils.equalsOrSuffix(qName, "Link") && "add".equals(attributes.get("rel"))) {
          readOnly = false;
       } else {
          taskHandler.startElement(uri, localName, qName, attrs);
@@ -85,11 +85,11 @@
 
    public void endElement(String uri, String name, String qName) {
       taskHandler.endElement(uri, name, qName);
-      if (qName.equals("Task")) {
+      if (SaxUtils.equalsOrSuffix(qName, "Task")) {
          this.tasks.add(taskHandler.getResult());
-      } else if (qName.equals("Description")) {
+      } else if (SaxUtils.equalsOrSuffix(qName, "Description")) {
          description = currentOrNull();
-      } else if (qName.equals("IsPublished")) {
+      } else if (SaxUtils.equalsOrSuffix(qName, "IsPublished")) {
          published = Boolean.parseBoolean(currentOrNull());
       }
       currentText = new StringBuilder();
diff --git a/apis/vcloud/src/main/java/org/jclouds/vcloud/xml/CatalogItemHandler.java b/apis/vcloud/src/main/java/org/jclouds/vcloud/xml/CatalogItemHandler.java
index 6f544d7..ee4c606 100644
--- a/apis/vcloud/src/main/java/org/jclouds/vcloud/xml/CatalogItemHandler.java
+++ b/apis/vcloud/src/main/java/org/jclouds/vcloud/xml/CatalogItemHandler.java
@@ -53,19 +53,19 @@
    @Override
    public void startElement(String uri, String localName, String qName, Attributes attrs) throws SAXException {
       Map<String, String> attributes = SaxUtils.cleanseAttributes(attrs);
-      if (qName.equals("CatalogItem")) {
+      if (SaxUtils.equalsOrSuffix(qName, "CatalogItem")) {
          catalogItem = newReferenceType(attributes);
-      } else if (qName.equals("Entity")) {
+      } else if (SaxUtils.equalsOrSuffix(qName, ("Entity"))) {
          entity = newReferenceType(attributes);
-      } else if (qName.equals("Property")) {
+      } else if (SaxUtils.equalsOrSuffix(qName, ("Property"))) {
          key = attributes.get("key");
       }
    }
 
    public void endElement(String uri, String name, String qName) {
-      if (qName.equals("Description")) {
+      if (SaxUtils.equalsOrSuffix(qName, ("Description"))) {
          description = currentOrNull();
-      } else if (qName.equals("Property")) {
+      } else if (SaxUtils.equalsOrSuffix(qName, ("Property"))) {
          properties.put(key, currentOrNull());
          key = null;
       }
diff --git a/apis/vcloud/src/main/java/org/jclouds/vcloud/xml/ErrorHandler.java b/apis/vcloud/src/main/java/org/jclouds/vcloud/xml/ErrorHandler.java
index 2122c6a..2ad6a9b 100644
--- a/apis/vcloud/src/main/java/org/jclouds/vcloud/xml/ErrorHandler.java
+++ b/apis/vcloud/src/main/java/org/jclouds/vcloud/xml/ErrorHandler.java
@@ -40,7 +40,7 @@
    @Override
    public void startElement(String uri, String localName, String qName, Attributes attrs) throws SAXException {
       Map<String, String> attributes = SaxUtils.cleanseAttributes(attrs);
-      if (qName.equals("Error")) {
+      if (SaxUtils.equalsOrSuffix(qName, "Error")) {
          error = Utils.newError(attributes);
       }
    }
diff --git a/apis/vcloud/src/main/java/org/jclouds/vcloud/xml/OrgNetworkHandler.java b/apis/vcloud/src/main/java/org/jclouds/vcloud/xml/OrgNetworkHandler.java
index 539dc8f..3cf0723 100644
--- a/apis/vcloud/src/main/java/org/jclouds/vcloud/xml/OrgNetworkHandler.java
+++ b/apis/vcloud/src/main/java/org/jclouds/vcloud/xml/OrgNetworkHandler.java
@@ -145,13 +145,13 @@
    @Override
    public void startElement(String uri, String localName, String qName, Attributes attrs) throws SAXException {
       Map<String, String> attributes = SaxUtils.cleanseAttributes(attrs);
-      if (qName.equals("OrgNetwork")) {
+      if (SaxUtils.equalsOrSuffix(qName, "OrgNetwork")) {
          network = newReferenceType(attributes);
-      } else if (qName.equals("FirewallRule")) {
+      } else if (SaxUtils.equalsOrSuffix(qName, "FirewallRule")) {
          this.inFirewallRule = true;
-      } else if (qName.equals("ParentNetwork")) {
+      } else if (SaxUtils.equalsOrSuffix(qName, "ParentNetwork")) {
          parentNetwork = newReferenceType(attributes);
-      } else if (qName.equals("Link") && "up".equals(attributes.get("rel"))) {
+      } else if (SaxUtils.equalsOrSuffix(qName, "Link") && "up".equals(attributes.get("rel"))) {
          org = newReferenceType(attributes);
       } else {
          taskHandler.startElement(uri, localName, qName, attrs);
@@ -166,38 +166,38 @@
 
    public void endElement(String uri, String name, String qName) {
       taskHandler.endElement(uri, name, qName);
-      if (qName.equals("Task")) {
+      if (SaxUtils.equalsOrSuffix(qName, "Task")) {
          this.tasks.add(taskHandler.getResult());
-      } else if (qName.equals("Description")) {
+      } else if (SaxUtils.equalsOrSuffix(qName, "Description")) {
          if (inFirewallRule)
             firewallRuleDescription = currentOrNull();
          else
             orgDescription = currentOrNull();
-      } else if (qName.equals("FenceMode")) {
+      } else if (SaxUtils.equalsOrSuffix(qName, "FenceMode")) {
          fenceMode = FenceMode.fromValue(currentOrNull());
-      } else if (qName.equals("StartAddress")) {
+      } else if (SaxUtils.equalsOrSuffix(qName, "StartAddress")) {
          startAddress = currentOrNull();
-      } else if (qName.equals("EndAddress")) {
+      } else if (SaxUtils.equalsOrSuffix(qName, "EndAddress")) {
          endAddress = currentOrNull();
-      } else if (qName.equals("AllocatedIpAddress")) {
+      } else if (SaxUtils.equalsOrSuffix(qName, "AllocatedIpAddress")) {
          allocatedIpAddresses.add(currentOrNull());
-      } else if (qName.equals("IpRange")) {
+      } else if (SaxUtils.equalsOrSuffix(qName, "IpRange")) {
          ipRanges.add(new IpRange(startAddress, endAddress));
          this.startAddress = null;
          this.endAddress = null;
-      } else if (qName.equals("IsInherited")) {
+      } else if (SaxUtils.equalsOrSuffix(qName, "IsInherited")) {
          inherited = Boolean.parseBoolean(currentOrNull());
-      } else if (qName.equals("Gateway")) {
+      } else if (SaxUtils.equalsOrSuffix(qName, "Gateway")) {
          gateway = currentOrNull();
-      } else if (qName.equals("Netmask")) {
+      } else if (SaxUtils.equalsOrSuffix(qName, "Netmask")) {
          netmask = currentOrNull();
-      } else if (qName.equals("Dns1")) {
+      } else if (SaxUtils.equalsOrSuffix(qName, "Dns1")) {
          dns1 = currentOrNull();
-      } else if (qName.equals("Dns2")) {
+      } else if (SaxUtils.equalsOrSuffix(qName, "Dns2")) {
          dns2 = currentOrNull();
-      } else if (qName.equals("DnsSuffix")) {
+      } else if (SaxUtils.equalsOrSuffix(qName, "DnsSuffix")) {
          dnsSuffix = currentOrNull();
-      } else if (qName.equals("IpScope")) {
+      } else if (SaxUtils.equalsOrSuffix(qName, "IpScope")) {
          ipScope = new IpScope(inherited, gateway, netmask, dns1, dns2, dnsSuffix, ipRanges, allocatedIpAddresses);
          this.inherited = false;
          this.gateway = null;
@@ -207,38 +207,38 @@
          this.dnsSuffix = null;
          this.ipRanges = Sets.newLinkedHashSet();
          this.allocatedIpAddresses = Sets.newLinkedHashSet();
-      } else if (qName.equals("IsEnabled")) {
+      } else if (SaxUtils.equalsOrSuffix(qName, "IsEnabled")) {
          if (inFirewallRule)
             firewallRuleEnabled = Boolean.parseBoolean(currentOrNull());
          else
             serviceEnabled = Boolean.parseBoolean(currentOrNull());
-      } else if (qName.equals("DefaultLeaseTime")) {
+      } else if (SaxUtils.equalsOrSuffix(qName, "DefaultLeaseTime")) {
          defaultLeaseTime = Integer.parseInt(currentOrNull());
-      } else if (qName.equals("MaxLeaseTime")) {
+      } else if (SaxUtils.equalsOrSuffix(qName, "MaxLeaseTime")) {
          maxLeaseTime = Integer.parseInt(currentOrNull());
-      } else if (qName.equals("DhcpService")) {
+      } else if (SaxUtils.equalsOrSuffix(qName, "DhcpService")) {
          this.dhcpService = new DhcpService(serviceEnabled, defaultLeaseTime, maxLeaseTime, Iterables
                   .getOnlyElement(ipRanges));
          this.serviceEnabled = false;
          this.defaultLeaseTime = null;
          this.maxLeaseTime = null;
          this.ipRanges = Sets.newLinkedHashSet();
-      } else if (qName.equals("Policy")) {
+      } else if (SaxUtils.equalsOrSuffix(qName, "Policy")) {
          if (inFirewallRule)
             firewallPolicy = FirewallPolicy.fromValue(currentOrNull());
          else
             natPolicy = NatPolicy.fromValue(currentOrNull());
-      } else if (qName.equals("Tcp")) {
+      } else if (SaxUtils.equalsOrSuffix(qName, "Tcp")) {
          tcp = Boolean.parseBoolean(currentOrNull());
-      } else if (qName.equals("Udp")) {
+      } else if (SaxUtils.equalsOrSuffix(qName, "Udp")) {
          udp = Boolean.parseBoolean(currentOrNull());
-      } else if (qName.equals("Protocols")) {
+      } else if (SaxUtils.equalsOrSuffix(qName, "Protocols")) {
          this.protocols = new FirewallProtocols(tcp, udp);
          this.tcp = false;
          this.udp = false;
-      } else if (qName.equals("DestinationIp")) {
+      } else if (SaxUtils.equalsOrSuffix(qName, "DestinationIp")) {
          this.destinationIp = currentOrNull();
-      } else if (qName.equals("FirewallRule")) {
+      } else if (SaxUtils.equalsOrSuffix(qName, "FirewallRule")) {
          this.inFirewallRule = false;
          this.firewallRules.add(new FirewallRule(firewallRuleEnabled, firewallRuleDescription, firewallPolicy,
                   protocols, port, destinationIp));
@@ -248,13 +248,13 @@
          this.protocols = null;
          this.port = -1;
          this.destinationIp = null;
-      } else if (qName.equals("FirewallService")) {
+      } else if (SaxUtils.equalsOrSuffix(qName, "FirewallService")) {
          firewallService = new FirewallService(serviceEnabled, firewallRules);
          this.serviceEnabled = false;
          this.firewallRules = Lists.newArrayList();
-      } else if (qName.equals("NatType")) {
+      } else if (SaxUtils.equalsOrSuffix(qName, "NatType")) {
          natType = NatType.fromValue(currentOrNull());
-      } else if (qName.equals("MappingMode")) {
+      } else if (SaxUtils.equalsOrSuffix(qName, "MappingMode")) {
          mappingMode = MappingMode.fromValue(currentOrNull());
       } else if (qName.equalsIgnoreCase("ExternalIP")) {
          externalIP = currentOrNull();
@@ -264,7 +264,7 @@
          vAppScopedLocalId = currentOrNull();
       } else if (qName.equalsIgnoreCase("vmNicId")) {
          vmNicId = Integer.parseInt(currentOrNull());
-      } else if (qName.equals("OneToOneVmRule")) {
+      } else if (SaxUtils.equalsOrSuffix(qName, "OneToOneVmRule")) {
          natRules.add(new OneToOneVmRule(mappingMode, externalIP, vAppScopedVmId, vmNicId));
          this.mappingMode = null;
          this.externalIP = null;
@@ -278,14 +278,14 @@
          internalPort = Integer.parseInt(currentOrNull());
       } else if (equalsOrSuffix(qName, "Protocol")) {
          natProtocol = NatProtocol.valueOf(currentOrNull());
-      } else if (qName.equals("PortForwardingRule")) {
+      } else if (SaxUtils.equalsOrSuffix(qName, "PortForwardingRule")) {
          natRules.add(new PortForwardingRule(externalIP, externalPort, internalIP, internalPort, natProtocol));
          this.externalIP = null;
          this.externalPort = -1;
          this.internalIP = null;
          this.internalPort = -1;
          this.natProtocol = null;
-      } else if (qName.equals("VmRule")) {
+      } else if (SaxUtils.equalsOrSuffix(qName, "VmRule")) {
          natRules.add(new VmRule(externalIP, externalPort, vAppScopedLocalId, vmNicId, internalPort, natProtocol));
          this.externalIP = null;
          this.externalPort = -1;
@@ -293,24 +293,24 @@
          this.vmNicId = -1;
          this.internalPort = -1;
          this.natProtocol = null;
-      } else if (qName.equals("NatService")) {
+      } else if (SaxUtils.equalsOrSuffix(qName, "NatService")) {
          this.natService = new NatService(serviceEnabled, natType, natPolicy, natRules);
          this.serviceEnabled = false;
          this.natType = null;
          this.natPolicy = null;
          this.natRules = Lists.newArrayList();
-      } else if (qName.equals("Features")) {
+      } else if (SaxUtils.equalsOrSuffix(qName, "Features")) {
          this.features = new Features(dhcpService, firewallService, natService);
          this.dhcpService = null;
          this.firewallService = null;
          this.natService = null;
-      } else if (qName.equals("Configuration")) {
+      } else if (SaxUtils.equalsOrSuffix(qName, "Configuration")) {
          configuration = new OrgNetworkImpl.ConfigurationImpl(ipScope, parentNetwork, fenceMode, features);
          this.ipScope = null;
          this.parentNetwork = null;
          this.fenceMode = null;
          this.features = null;
-      } else if (qName.equals("AllowedExternalIpAddress")) {
+      } else if (SaxUtils.equalsOrSuffix(qName, "AllowedExternalIpAddress")) {
          allowedExternalIpAddresses.add(currentOrNull());
       }
       currentText = new StringBuilder();
diff --git a/apis/vcloud/src/main/java/org/jclouds/vcloud/xml/SupportedVersionsHandler.java b/apis/vcloud/src/main/java/org/jclouds/vcloud/xml/SupportedVersionsHandler.java
index 7d131e6..8a9ef79 100644
--- a/apis/vcloud/src/main/java/org/jclouds/vcloud/xml/SupportedVersionsHandler.java
+++ b/apis/vcloud/src/main/java/org/jclouds/vcloud/xml/SupportedVersionsHandler.java
@@ -22,6 +22,7 @@
 import java.util.SortedMap;
 
 import org.jclouds.http.functions.ParseSax;
+import org.jclouds.util.SaxUtils;
 
 import com.google.common.collect.Maps;
 
@@ -40,11 +41,11 @@
    }
 
    public void endElement(String uri, String name, String qName) {
-      if (qName.equals("Version")) {
+      if (SaxUtils.equalsOrSuffix(qName, "Version")) {
          version = currentOrNull();
-      } else if (qName.equals("LoginUrl")) {
+      } else if (SaxUtils.equalsOrSuffix(qName, "LoginUrl")) {
          location = URI.create(currentOrNull());
-      } else if (qName.equals("VersionInfo")) {
+      } else if (SaxUtils.equalsOrSuffix(qName, "VersionInfo")) {
          contents.put(version, location);
       }
       currentText = new StringBuilder();
diff --git a/apis/vcloud/src/main/java/org/jclouds/vcloud/xml/TasksListHandler.java b/apis/vcloud/src/main/java/org/jclouds/vcloud/xml/TasksListHandler.java
index d01cbda..3a314b0 100644
--- a/apis/vcloud/src/main/java/org/jclouds/vcloud/xml/TasksListHandler.java
+++ b/apis/vcloud/src/main/java/org/jclouds/vcloud/xml/TasksListHandler.java
@@ -56,9 +56,9 @@
    @Override
    public void startElement(String uri, String localName, String qName, Attributes attrs) throws SAXException {
       Map<String, String> attributes = SaxUtils.cleanseAttributes(attrs);
-      if (qName.equals("TasksList")) {
+      if (SaxUtils.equalsOrSuffix(qName, "TasksList")) {
          resource = Utils.newReferenceType(attributes);
-      } else if (qName.equals("Link") && "self".equals(attributes.get("rel"))) {
+      } else if (SaxUtils.equalsOrSuffix(qName, "Link") && "self".equals(attributes.get("rel"))) {
          resource = Utils.newReferenceType(attributes);
       } else {
          taskHandler.startElement(uri, localName, qName, attrs);
@@ -68,7 +68,7 @@
    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException {
       taskHandler.endElement(uri, localName, qName);
-      if (qName.equals("Task")) {
+      if (SaxUtils.equalsOrSuffix(qName, "Task")) {
          this.tasks.add(taskHandler.getResult());
       }
    }
diff --git a/apis/vcloud/src/main/java/org/jclouds/vcloud/xml/VmHandler.java b/apis/vcloud/src/main/java/org/jclouds/vcloud/xml/VmHandler.java
index 04dd74f..dcf1236 100644
--- a/apis/vcloud/src/main/java/org/jclouds/vcloud/xml/VmHandler.java
+++ b/apis/vcloud/src/main/java/org/jclouds/vcloud/xml/VmHandler.java
@@ -113,12 +113,12 @@
          guestCustomizationHandler.startElement(uri, localName, qName, attrs);
       } else if (inTasks) {
          taskHandler.startElement(uri, localName, qName, attrs);
-      } else if (qName.equals("Vm")) {
+      } else if (SaxUtils.equalsOrSuffix(qName, "Vm")) {
          vm = newReferenceType(attributes);
          String status = attributes.get("status");
          if (status != null)
             this.status = Status.fromValue(Integer.parseInt(status));
-      } else if (qName.equals("Link") && "up".equals(attributes.get("rel"))) {
+      } else if (SaxUtils.equalsOrSuffix(qName, "Link") && "up".equals(attributes.get("rel"))) {
          vdc = newReferenceType(attributes);
       }
    }
@@ -150,9 +150,9 @@
          networkConnectionSectionHandler.endElement(uri, name, qName);
       } else if (inTasks) {
          taskHandler.endElement(uri, name, qName);
-      } else if (qName.equals("Description")) {
+      } else if (SaxUtils.equalsOrSuffix(qName, "Description")) {
          description = currentOrNull();
-      } else if (qName.equals("VAppScopedLocalId")) {
+      } else if (SaxUtils.equalsOrSuffix(qName, "VAppScopedLocalId")) {
          vAppScopedLocalId = currentOrNull();
       }
       currentText = new StringBuilder();
diff --git a/apis/vcloud/src/test/java/org/jclouds/vcloud/compute/BaseVCloudComputeServiceExpectTest.java b/apis/vcloud/src/test/java/org/jclouds/vcloud/compute/BaseVCloudComputeServiceExpectTest.java
index 3dca265..73ac7b9 100644
--- a/apis/vcloud/src/test/java/org/jclouds/vcloud/compute/BaseVCloudComputeServiceExpectTest.java
+++ b/apis/vcloud/src/test/java/org/jclouds/vcloud/compute/BaseVCloudComputeServiceExpectTest.java
@@ -131,6 +131,10 @@
             .message("HTTP/1.1 200 OK").payload(payloadFromResourceWithContentType("/template1.0-vcd15.xml",  VCloudMediaType.VAPPTEMPLATE_XML +";version=1.0"))
             .build();   
 
+   protected HttpResponse successfulVersion1_0GetVAppTemplateResponseFromVCD1_5WithMultipleVMsAndVDCParent = HttpResponse.builder().statusCode(200)
+            .message("HTTP/1.1 200 OK").payload(payloadFromResourceWithContentType("/template1.0-vcd15-multi-vm.xml",  VCloudMediaType.VAPPTEMPLATE_XML +";version=1.0"))
+            .build();   
+
    protected HttpRequest version1_0GetOVFForVAppTemplateRequest = HttpRequest.builder().method("GET").endpoint(
             URI.create(ENDPOINT + "/v1.0/vAppTemplate/" + templateId + "/ovf"))
             .headers(ImmutableMultimap.<String, String> builder()
@@ -141,6 +145,10 @@
             .message("HTTP/1.1 200 OK").payload(payloadFromResourceWithContentType("/ovf-ubuntu64.xml",  MediaType.TEXT_XML +";version=1.0"))
             .build();   
 
+   protected HttpResponse successfulVersion1_0GetOVFForVAppTemplateResponseFromVCD1_5WithMultipleVMs = HttpResponse.builder().statusCode(200)
+            .message("HTTP/1.1 200 OK").payload(payloadFromResourceWithContentType("/ovf-multi-vm.xml",  MediaType.TEXT_XML +";version=1.0"))
+            .build();   
+
    public BaseVCloudComputeServiceExpectTest() {
       provider = "vcloud";
    }
diff --git a/apis/vcloud/src/test/java/org/jclouds/vcloud/compute/strategy/VCloudComputeServiceAdapterTest.java b/apis/vcloud/src/test/java/org/jclouds/vcloud/compute/strategy/VCloudComputeServiceAdapterTest.java
new file mode 100644
index 0000000..8bf2bc9
--- /dev/null
+++ b/apis/vcloud/src/test/java/org/jclouds/vcloud/compute/strategy/VCloudComputeServiceAdapterTest.java
@@ -0,0 +1,74 @@
+package org.jclouds.vcloud.compute.strategy;
+
+import static org.testng.Assert.assertEquals;
+
+import java.net.URI;
+import java.util.Collections;
+
+import org.jclouds.compute.ComputeService;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.vcloud.compute.BaseVCloudComputeServiceExpectTest;
+import org.jclouds.vcloud.domain.VAppTemplate;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+
+public class VCloudComputeServiceAdapterTest extends BaseVCloudComputeServiceExpectTest {
+
+   @Test
+   public void testListHardwareProfiles() throws Exception {
+      ComputeService compute = requestsSendResponses(ImmutableMap.<HttpRequest, HttpResponse> builder()
+               .put(versionsRequest, versionsResponseFromVCD1_5)
+               .put(version1_0LoginRequest, successfulVersion1_0LoginResponseFromVCD1_5WithSingleOrg)
+               .put(version1_0GetOrgRequest, successfulVersion1_0GetOrgResponseFromVCD1_5WithSingleTasksListVDCAndNetwork)
+               .put(version1_0GetCatalogRequest, successfulVersion1_0GetCatalogResponseFromVCD1_5WithSingleTemplate)
+               .put(version1_0GetCatalogItemRequest, successfulVersion1_0GetCatalogItemResponseFromVCD1_5ForTemplate)
+               .put(version1_0GetVDCRequest, successfulVersion1_0GetVDCResponseFromVCD1_5WithSingleTemplateAndNetwork)
+               .put(version1_0GetVAppTemplateRequest, successfulVersion1_0GetVAppTemplateResponseFromVCD1_5WithSingleVMAndVDCParent)
+               .put(version1_0GetOVFForVAppTemplateRequest, successfulVersion1_0GetOVFForVAppTemplateResponseFromVCD1_5WithSingleVM)
+               .build());
+
+      VCloudComputeServiceAdapter adapter = compute.getContext()
+               .utils().injector().getInstance(VCloudComputeServiceAdapter.class);
+
+      Iterable<VAppTemplate> hardwareProfiles = adapter.listHardwareProfiles();
+      
+      Iterable<URI> hardwareProfileRefs = Iterables.transform(ImmutableList.copyOf(hardwareProfiles), new Function<VAppTemplate,URI>() {
+         @Override public URI apply(VAppTemplate input) {
+            return input.getHref();
+         }
+      });
+      assertEquals(ImmutableSet.copyOf(hardwareProfileRefs), ImmutableSet.of(URI.create("https://zone.myvcloud.com/api/v1.0/vAppTemplate/vappTemplate-51891b97-c5dd-47dc-a687-aabae354f728")));
+   }
+   
+   /**
+    * For issue 994. In BaseEnvelopeHandler when it encounters VirtualSystemCollection, it throws IllegalArgumentException
+    * (cannot currently create envelopes with multiple virtual systems).
+    * Thus we do not include the VM in the supported set, but we do return without propagating the exception.
+    */
+   @Test
+   public void testListHardwareProfilesWithUnsupportedTemplate() throws Exception {
+      ComputeService compute = requestsSendResponses(ImmutableMap.<HttpRequest, HttpResponse> builder()
+               .put(versionsRequest, versionsResponseFromVCD1_5)
+               .put(version1_0LoginRequest, successfulVersion1_0LoginResponseFromVCD1_5WithSingleOrg)
+               .put(version1_0GetOrgRequest, successfulVersion1_0GetOrgResponseFromVCD1_5WithSingleTasksListVDCAndNetwork)
+               .put(version1_0GetCatalogRequest, successfulVersion1_0GetCatalogResponseFromVCD1_5WithSingleTemplate)
+               .put(version1_0GetCatalogItemRequest, successfulVersion1_0GetCatalogItemResponseFromVCD1_5ForTemplate)
+               .put(version1_0GetVDCRequest, successfulVersion1_0GetVDCResponseFromVCD1_5WithSingleTemplateAndNetwork)
+               .put(version1_0GetVAppTemplateRequest, successfulVersion1_0GetVAppTemplateResponseFromVCD1_5WithMultipleVMsAndVDCParent)
+               .put(version1_0GetOVFForVAppTemplateRequest, successfulVersion1_0GetOVFForVAppTemplateResponseFromVCD1_5WithMultipleVMs)
+               .build());
+
+      VCloudComputeServiceAdapter adapter = compute.getContext()
+               .utils().injector().getInstance(VCloudComputeServiceAdapter.class);
+
+      Iterable<VAppTemplate> hardwareProfiles = adapter.listHardwareProfiles();
+      
+      assertEquals(ImmutableSet.copyOf(hardwareProfiles), Collections.emptySet());
+   }
+}
diff --git a/apis/vcloud/src/test/java/org/jclouds/vcloud/xml/CatalogItemHandlerTest.java b/apis/vcloud/src/test/java/org/jclouds/vcloud/xml/CatalogItemHandlerTest.java
index 17c2b8a..b5050e5 100644
--- a/apis/vcloud/src/test/java/org/jclouds/vcloud/xml/CatalogItemHandlerTest.java
+++ b/apis/vcloud/src/test/java/org/jclouds/vcloud/xml/CatalogItemHandlerTest.java
@@ -59,4 +59,30 @@
                )));
 
    }
+   
+   public void testApplyInputStreamWithNamespaceUsingVcloud() {
+      InputStream is = getClass().getResourceAsStream("/catalogItem-carrenza-with-vcloud-namespace.xml");
+      Injector injector = Guice.createInjector(new SaxParserModule());
+      Factory factory = injector.getInstance(ParseSax.Factory.class);
+      CatalogItem result = factory.create(injector.getInstance(CatalogItemHandler.class)).parse(is);
+
+      assertEquals(result, new CatalogItemImpl("ubuntu10.10x64", 
+               URI.create("https://myvdc.carrenza.net/api/v1.0/catalogItem/ecd4d3a0-0d12-4195-a6d2-14cdf9f925a3"), 
+               null, new ReferenceTypeImpl("ubuntu10.10x64", "application/vnd.vmware.vcloud.vAppTemplate+xml", 
+                        URI.create("https://myvdc.carrenza.net/api/v1.0/vAppTemplate/vappTemplate-123766ea-2b55-482c-8adf-735ab1952834")),
+               ImmutableSortedMap.<String,String>of()));
+   }
+   
+   public void testApplyInputStreamWithNamespaceUsingDefault() {
+      InputStream is = getClass().getResourceAsStream("/catalogItem-carrenza-with-default-namespace.xml");
+      Injector injector = Guice.createInjector(new SaxParserModule());
+      Factory factory = injector.getInstance(ParseSax.Factory.class);
+      CatalogItem result = factory.create(injector.getInstance(CatalogItemHandler.class)).parse(is);
+
+      assertEquals(result, new CatalogItemImpl("ubuntu10.10x64", 
+               URI.create("https://myvdc.carrenza.net/api/v1.0/catalogItem/ecd4d3a0-0d12-4195-a6d2-14cdf9f925a3"), 
+               null, new ReferenceTypeImpl("ubuntu10.10x64", "application/vnd.vmware.vcloud.vAppTemplate+xml", 
+                        URI.create("https://myvdc.carrenza.net/api/v1.0/vAppTemplate/vappTemplate-123766ea-2b55-482c-8adf-735ab1952834")),
+               ImmutableSortedMap.<String,String>of()));
+   }
 }
diff --git a/apis/vcloud/src/test/resources/catalogItem-carrenza-with-default-namespace.xml b/apis/vcloud/src/test/resources/catalogItem-carrenza-with-default-namespace.xml
new file mode 100644
index 0000000..88fec20
--- /dev/null
+++ b/apis/vcloud/src/test/resources/catalogItem-carrenza-with-default-namespace.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<CatalogItem xmlns="http://www.vmware.com/vcloud/v1" name="ubuntu10.10x64" type="application/vnd.vmware.vcloud.catalogItem+xml" href="https://myvdc.carrenza.net/api/v1.0/catalogItem/ecd4d3a0-0d12-4195-a6d2-14cdf9f925a3" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.vmware.com/vcloud/v1 http://myvdc.carrenza.net/api/v1.0/schema/master.xsd">
+    <Link rel="up" type="application/vnd.vmware.vcloud.catalog+xml" href="https://myvdc.carrenza.net/api/v1.0/catalog/5d2c147a-d26d-487a-9a05-577ee175186b"/>
+    <Link rel="edit" type="application/vnd.vmware.vcloud.catalogItem+xml" href="https://myvdc.carrenza.net/api/v1.0/catalogItem/ecd4d3a0-0d12-4195-a6d2-14cdf9f925a3"/>
+    <Link rel="remove" href="https://myvdc.carrenza.net/api/v1.0/catalogItem/ecd4d3a0-0d12-4195-a6d2-14cdf9f925a3"/>
+    <Description> </Description>
+    <Entity type="application/vnd.vmware.vcloud.vAppTemplate+xml" name="ubuntu10.10x64" href="https://myvdc.carrenza.net/api/v1.0/vAppTemplate/vappTemplate-123766ea-2b55-482c-8adf-735ab1952834"/>
+</CatalogItem>
diff --git a/apis/vcloud/src/test/resources/catalogItem-carrenza-with-vcloud-namespace.xml b/apis/vcloud/src/test/resources/catalogItem-carrenza-with-vcloud-namespace.xml
new file mode 100644
index 0000000..917c143
--- /dev/null
+++ b/apis/vcloud/src/test/resources/catalogItem-carrenza-with-vcloud-namespace.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<vcloud:CatalogItem xmlns:vcloud="http://www.vmware.com/vcloud/v1" name="ubuntu10.10x64" type="application/vnd.vmware.vcloud.catalogItem+xml" href="https://myvdc.carrenza.net/api/v1.0/catalogItem/ecd4d3a0-0d12-4195-a6d2-14cdf9f925a3" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.vmware.com/vcloud/v1 http://myvdc.carrenza.net/api/v1.0/schema/master.xsd">
+    <vcloud:Link rel="up" type="application/vnd.vmware.vcloud.catalog+xml" href="https://myvdc.carrenza.net/api/v1.0/catalog/5d2c147a-d26d-487a-9a05-577ee175186b"/>
+    <vcloud:Link rel="edit" type="application/vnd.vmware.vcloud.catalogItem+xml" href="https://myvdc.carrenza.net/api/v1.0/catalogItem/ecd4d3a0-0d12-4195-a6d2-14cdf9f925a3"/>
+    <vcloud:Link rel="remove" href="https://myvdc.carrenza.net/api/v1.0/catalogItem/ecd4d3a0-0d12-4195-a6d2-14cdf9f925a3"/>
+    <vcloud:Description> </vcloud:Description>
+    <vcloud:Entity type="application/vnd.vmware.vcloud.vAppTemplate+xml" name="ubuntu10.10x64" href="https://myvdc.carrenza.net/api/v1.0/vAppTemplate/vappTemplate-123766ea-2b55-482c-8adf-735ab1952834"/>
+</vcloud:CatalogItem>
diff --git a/apis/vcloud/src/test/resources/ovf-multi-vm.xml b/apis/vcloud/src/test/resources/ovf-multi-vm.xml
new file mode 100644
index 0000000..fa4bb37
--- /dev/null
+++ b/apis/vcloud/src/test/resources/ovf-multi-vm.xml
@@ -0,0 +1,285 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ovf:Envelope xmlns:ovf="http://schemas.dmtf.org/ovf/envelope/1" xmlns:vcloud="http://www.vmware.com/vcloud/v1" xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData" xmlns:vssd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2.22.0/CIM_VirtualSystemSettingData.xsd http://schemas.dmtf.org/ovf/envelope/1 http://schemas.dmtf.org/ovf/envelope/1/dsp8023_1.1.0.xsd http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2.22.0/CIM_ResourceAllocationSettingData.xsd http://www.vmware.com/vcloud/v1 http://zone.myvcloud.com/api/v1.0/schema/master.xsd">
+    <ovf:References/>
+    <ovf:NetworkSection>
+        <ovf:Info>The list of logical networks</ovf:Info>
+        <ovf:Network ovf:name="vapp net">
+            <ovf:Description/>
+        </ovf:Network>
+    </ovf:NetworkSection>
+    <vcloud:NetworkConfigSection ovf:required="false">
+        <ovf:Info>The configuration parameters for logical networks</ovf:Info>
+        <vcloud:NetworkConfig networkName="vapp net">
+            <vcloud:Description/>
+            <vcloud:Configuration>                
+                <vcloud:IpScope>
+                    <vcloud:IsInherited>false</vcloud:IsInherited>
+                    <vcloud:Gateway>192.168.2.1</vcloud:Gateway>
+                    <vcloud:Netmask>255.255.255.0</vcloud:Netmask>
+                    <vcloud:Dns1>195.225.219.131</vcloud:Dns1>
+                    <vcloud:IpRanges>
+                        <vcloud:IpRange>
+                            <vcloud:StartAddress>192.168.2.100</vcloud:StartAddress>
+                            <vcloud:EndAddress>192.168.2.199</vcloud:EndAddress>
+                        </vcloud:IpRange>
+                    </vcloud:IpRanges>
+                </vcloud:IpScope>
+                <vcloud:FenceMode>isolated</vcloud:FenceMode>
+                <vcloud:Features>
+                    <vcloud:DhcpService>
+                        <vcloud:IsEnabled>false</vcloud:IsEnabled>
+                        <vcloud:DefaultLeaseTime>7200</vcloud:DefaultLeaseTime>
+                        <vcloud:MaxLeaseTime>7200</vcloud:MaxLeaseTime>
+                        <vcloud:IpRange/>
+                    </vcloud:DhcpService>
+                </vcloud:Features>
+            </vcloud:Configuration>
+            <vcloud:IsDeployed>false</vcloud:IsDeployed>
+        </vcloud:NetworkConfig>
+    </vcloud:NetworkConfigSection>
+    <vcloud:LeaseSettingsSection ovf:required="false">
+        <ovf:Info>Lease settings section</ovf:Info>
+        <vcloud:DeploymentLeaseInSeconds>0</vcloud:DeploymentLeaseInSeconds>
+        <vcloud:StorageLeaseInSeconds>0</vcloud:StorageLeaseInSeconds>
+    </vcloud:LeaseSettingsSection>
+    <vcloud:CustomizationSection ovf:required="false">
+        <ovf:Info>VApp template customization section</ovf:Info>
+        <vcloud:CustomizeOnInstantiate>true</vcloud:CustomizeOnInstantiate>
+    </vcloud:CustomizationSection>
+    <ovf:VirtualSystemCollection ovf:id="centos-web/db-5.5">
+        <ovf:Info>A collection of virtual machines: </ovf:Info>
+        <ovf:Name>centos-web/db-5.5</ovf:Name>
+        <ovf:StartupSection>
+            <ovf:Info>VApp startup section</ovf:Info>
+            <ovf:Item ovf:stopDelay="0" ovf:stopAction="powerOff" ovf:startDelay="0" ovf:startAction="powerOn" ovf:order="0" ovf:id="centos-web"/>
+            <ovf:Item ovf:stopDelay="0" ovf:stopAction="powerOff" ovf:startDelay="0" ovf:startAction="powerOn" ovf:order="0" ovf:id="centos-db"/>
+        </ovf:StartupSection>
+        <ovf:VirtualSystem ovf:id="centos-db">
+            <ovf:Info>A virtual machine: </ovf:Info>
+            <ovf:Name>centos-db</ovf:Name>
+            <ovf:OperatingSystemSection xmlns:vmw="http://www.vmware.com/schema/ovf" ovf:id="100" vmw:osType="other26xLinux64Guest">
+                <ovf:Info>Specifies the operating system installed</ovf:Info>
+                <ovf:Description>Other 2.6x Linux (64-bit)</ovf:Description>
+            </ovf:OperatingSystemSection>
+            <ovf:VirtualHardwareSection>
+                <ovf:Info>Virtual hardware requirements</ovf:Info>
+                <ovf:System>
+                    <vssd:ElementName>Virtual Hardware Family</vssd:ElementName>
+                    <vssd:InstanceID>0</vssd:InstanceID>
+                    <vssd:VirtualSystemIdentifier>centos-db</vssd:VirtualSystemIdentifier>
+                    <vssd:VirtualSystemType>vmx-07</vssd:VirtualSystemType>
+                </ovf:System>
+                <ovf:Item>
+                    <rasd:Address>00:50:56:01:06:81</rasd:Address>
+                    <rasd:AddressOnParent>0</rasd:AddressOnParent>
+                    <rasd:AutomaticAllocation>true</rasd:AutomaticAllocation>
+                    <rasd:Connection vcloud:ipAddress="192.168.2.100" vcloud:primaryNetworkConnection="true" vcloud:ipAddressingMode="POOL">vapp net</rasd:Connection>
+
+                    <rasd:Description>PCNet32 ethernet adapter</rasd:Description>
+                    <rasd:ElementName>Network adapter 0</rasd:ElementName>
+                    <rasd:InstanceID>1</rasd:InstanceID>
+                    <rasd:ResourceSubType>PCNet32</rasd:ResourceSubType>
+                    <rasd:ResourceType>10</rasd:ResourceType>
+                </ovf:Item>
+                <ovf:Item>
+                    <rasd:Address>0</rasd:Address>
+                    <rasd:Description>SCSI Controller</rasd:Description>
+                    <rasd:ElementName>SCSI Controller 0</rasd:ElementName>
+                    <rasd:InstanceID>2</rasd:InstanceID>
+                    <rasd:ResourceSubType>lsilogic</rasd:ResourceSubType>
+                    <rasd:ResourceType>6</rasd:ResourceType>
+                </ovf:Item>
+                <ovf:Item>
+                    <rasd:AddressOnParent>0</rasd:AddressOnParent>
+                    <rasd:Description>Hard disk</rasd:Description>
+                    <rasd:ElementName>Hard disk 1</rasd:ElementName>
+                    <rasd:HostResource vcloud:capacity="8192" vcloud:busType="6" vcloud:busSubType="lsilogic"/>
+                    <rasd:InstanceID>2000</rasd:InstanceID>
+                    <rasd:Parent>2</rasd:Parent>
+                    <rasd:ResourceType>17</rasd:ResourceType>
+                </ovf:Item>
+                <ovf:Item>
+                    <rasd:Address>0</rasd:Address>
+                    <rasd:Description>IDE Controller</rasd:Description>
+                    <rasd:ElementName>IDE Controller 0</rasd:ElementName>
+                    <rasd:InstanceID>3</rasd:InstanceID>
+                    <rasd:ResourceType>5</rasd:ResourceType>
+                </ovf:Item>
+                <ovf:Item>
+                    <rasd:AddressOnParent>0</rasd:AddressOnParent>
+                    <rasd:AutomaticAllocation>false</rasd:AutomaticAllocation>
+                    <rasd:Description>CD/DVD Drive</rasd:Description>
+                    <rasd:ElementName>CD/DVD Drive 1</rasd:ElementName>
+                    <rasd:HostResource/>
+                    <rasd:InstanceID>3000</rasd:InstanceID>
+                    <rasd:Parent>3</rasd:Parent>
+                    <rasd:ResourceType>15</rasd:ResourceType>
+                </ovf:Item>
+                <ovf:Item>
+                    <rasd:AddressOnParent>0</rasd:AddressOnParent>
+                    <rasd:AutomaticAllocation>false</rasd:AutomaticAllocation>
+                    <rasd:Description>Floppy Drive</rasd:Description>
+                    <rasd:ElementName>Floppy Drive 1</rasd:ElementName>
+                    <rasd:HostResource/>
+                    <rasd:InstanceID>8000</rasd:InstanceID>
+                    <rasd:ResourceType>14</rasd:ResourceType>
+                </ovf:Item>
+                <ovf:Item>
+                    <rasd:AllocationUnits>hertz * 10^6</rasd:AllocationUnits>
+                    <rasd:Description>Number of Virtual CPUs</rasd:Description>
+                    <rasd:ElementName>1 virtual CPU(s)</rasd:ElementName>
+                    <rasd:InstanceID>4</rasd:InstanceID>
+                    <rasd:Reservation>0</rasd:Reservation>
+                    <rasd:ResourceType>3</rasd:ResourceType>
+                    <rasd:VirtualQuantity>1</rasd:VirtualQuantity>
+                    <rasd:Weight>0</rasd:Weight>
+                </ovf:Item>
+                <ovf:Item>
+                    <rasd:AllocationUnits>byte * 2^20</rasd:AllocationUnits>
+                    <rasd:Description>Memory Size</rasd:Description>
+                    <rasd:ElementName>2048 MB of memory</rasd:ElementName>
+                    <rasd:InstanceID>5</rasd:InstanceID>
+                    <rasd:Reservation>0</rasd:Reservation>
+                    <rasd:ResourceType>4</rasd:ResourceType>
+                    <rasd:VirtualQuantity>2048</rasd:VirtualQuantity>
+                    <rasd:Weight>0</rasd:Weight>
+                </ovf:Item>
+            </ovf:VirtualHardwareSection>
+            <vcloud:NetworkConnectionSection ovf:required="false">
+                <ovf:Info>Specifies the available VM network connections</ovf:Info>
+                <vcloud:PrimaryNetworkConnectionIndex>0</vcloud:PrimaryNetworkConnectionIndex>
+                <vcloud:NetworkConnection network="vapp net">
+                    <vcloud:NetworkConnectionIndex>0</vcloud:NetworkConnectionIndex>
+                    <vcloud:IpAddress>192.168.2.100</vcloud:IpAddress>
+                    <vcloud:IsConnected>true</vcloud:IsConnected>
+                    <vcloud:MACAddress>00:50:56:01:06:81</vcloud:MACAddress>
+                    <vcloud:IpAddressAllocationMode>POOL</vcloud:IpAddressAllocationMode>
+                </vcloud:NetworkConnection>
+            </vcloud:NetworkConnectionSection>
+            <vcloud:GuestCustomizationSection ovf:required="false">
+                <ovf:Info>Specifies Guest OS Customization Settings</ovf:Info>
+                <vcloud:Enabled>true</vcloud:Enabled>
+                <vcloud:ChangeSid>false</vcloud:ChangeSid>
+                <vcloud:JoinDomainEnabled>false</vcloud:JoinDomainEnabled>
+                <vcloud:UseOrgSettings>false</vcloud:UseOrgSettings>
+                <vcloud:AdminPasswordEnabled>false</vcloud:AdminPasswordEnabled>
+                <vcloud:AdminPasswordAuto>true</vcloud:AdminPasswordAuto>
+                <vcloud:ResetPasswordRequired>false</vcloud:ResetPasswordRequired>
+                <vcloud:ComputerName>centos-db</vcloud:ComputerName>
+            </vcloud:GuestCustomizationSection>
+        </ovf:VirtualSystem>
+        <ovf:VirtualSystem ovf:id="centos-web">
+            <ovf:Info>A virtual machine: </ovf:Info>
+            <ovf:Name>centos-web</ovf:Name>
+            <ovf:OperatingSystemSection xmlns:vmw="http://www.vmware.com/schema/ovf" ovf:id="100" vmw:osType="other26xLinux64Guest">
+                <ovf:Info>Specifies the operating system installed</ovf:Info>
+                <ovf:Description>Other 2.6x Linux (64-bit)</ovf:Description>
+            </ovf:OperatingSystemSection>
+            <ovf:VirtualHardwareSection>
+                <ovf:Info>Virtual hardware requirements</ovf:Info>
+                <ovf:System>
+                    <vssd:ElementName>Virtual Hardware Family</vssd:ElementName>
+                    <vssd:InstanceID>0</vssd:InstanceID>
+                    <vssd:VirtualSystemIdentifier>centos-web</vssd:VirtualSystemIdentifier>
+                    <vssd:VirtualSystemType>vmx-07</vssd:VirtualSystemType>
+                </ovf:System>
+                <ovf:Item>
+                    <rasd:Address>00:50:56:01:06:82</rasd:Address>                    <rasd:AddressOnParent>0</rasd:AddressOnParent>
+                    <rasd:AutomaticAllocation>true</rasd:AutomaticAllocation>
+                    <rasd:Connection vcloud:ipAddress="192.168.2.101" vcloud:primaryNetworkConnection="true" vcloud:ipAddressingMode="POOL">vapp net</rasd:Connection>
+
+                    <rasd:Description>PCNet32 ethernet adapter</rasd:Description>
+                    <rasd:ElementName>Network adapter 0</rasd:ElementName>
+                    <rasd:InstanceID>1</rasd:InstanceID>
+                    <rasd:ResourceSubType>PCNet32</rasd:ResourceSubType>
+                    <rasd:ResourceType>10</rasd:ResourceType>
+                </ovf:Item>
+                <ovf:Item>
+                    <rasd:Address>0</rasd:Address>
+                    <rasd:Description>SCSI Controller</rasd:Description>
+                    <rasd:ElementName>SCSI Controller 0</rasd:ElementName>
+                    <rasd:InstanceID>2</rasd:InstanceID>
+                    <rasd:ResourceSubType>lsilogic</rasd:ResourceSubType>
+                    <rasd:ResourceType>6</rasd:ResourceType>
+                </ovf:Item>
+                <ovf:Item>
+                    <rasd:AddressOnParent>0</rasd:AddressOnParent>
+                    <rasd:Description>Hard disk</rasd:Description>
+                    <rasd:ElementName>Hard disk 1</rasd:ElementName>
+                    <rasd:HostResource vcloud:capacity="8192" vcloud:busType="6" vcloud:busSubType="lsilogic"/>
+                    <rasd:InstanceID>2000</rasd:InstanceID>
+                    <rasd:Parent>2</rasd:Parent>
+                    <rasd:ResourceType>17</rasd:ResourceType>
+                </ovf:Item>
+                <ovf:Item>
+                    <rasd:Address>0</rasd:Address>
+                    <rasd:Description>IDE Controller</rasd:Description>
+                    <rasd:ElementName>IDE Controller 0</rasd:ElementName>
+                    <rasd:InstanceID>3</rasd:InstanceID>
+                    <rasd:ResourceType>5</rasd:ResourceType>
+                </ovf:Item>
+                <ovf:Item>
+                    <rasd:AddressOnParent>0</rasd:AddressOnParent>
+                    <rasd:AutomaticAllocation>false</rasd:AutomaticAllocation>
+                    <rasd:Description>CD/DVD Drive</rasd:Description>
+                    <rasd:ElementName>CD/DVD Drive 1</rasd:ElementName>
+                    <rasd:HostResource/>
+                    <rasd:InstanceID>3000</rasd:InstanceID>
+                    <rasd:Parent>3</rasd:Parent>
+                    <rasd:ResourceType>15</rasd:ResourceType>
+                </ovf:Item>
+                <ovf:Item>
+                    <rasd:AddressOnParent>0</rasd:AddressOnParent>
+                    <rasd:AutomaticAllocation>false</rasd:AutomaticAllocation>
+                    <rasd:Description>Floppy Drive</rasd:Description>
+                    <rasd:ElementName>Floppy Drive 1</rasd:ElementName>
+                    <rasd:HostResource/>
+                    <rasd:InstanceID>8000</rasd:InstanceID>
+                    <rasd:ResourceType>14</rasd:ResourceType>
+                </ovf:Item>
+                <ovf:Item>
+                    <rasd:AllocationUnits>hertz * 10^6</rasd:AllocationUnits>
+                    <rasd:Description>Number of Virtual CPUs</rasd:Description>
+                    <rasd:ElementName>1 virtual CPU(s)</rasd:ElementName>
+                    <rasd:InstanceID>4</rasd:InstanceID>
+                    <rasd:Reservation>0</rasd:Reservation>
+                    <rasd:ResourceType>3</rasd:ResourceType>
+                    <rasd:VirtualQuantity>1</rasd:VirtualQuantity>
+                    <rasd:Weight>0</rasd:Weight>
+                </ovf:Item>
+                <ovf:Item>
+                    <rasd:AllocationUnits>byte * 2^20</rasd:AllocationUnits>
+                    <rasd:Description>Memory Size</rasd:Description>
+                    <rasd:ElementName>2048 MB of memory</rasd:ElementName>
+                    <rasd:InstanceID>5</rasd:InstanceID>
+                    <rasd:Reservation>0</rasd:Reservation>
+                    <rasd:ResourceType>4</rasd:ResourceType>
+                    <rasd:VirtualQuantity>2048</rasd:VirtualQuantity>
+                    <rasd:Weight>0</rasd:Weight>
+                </ovf:Item>
+            </ovf:VirtualHardwareSection>
+            <vcloud:NetworkConnectionSection ovf:required="false">
+                <ovf:Info>Specifies the available VM network connections</ovf:Info>
+                <vcloud:PrimaryNetworkConnectionIndex>0</vcloud:PrimaryNetworkConnectionIndex>
+                <vcloud:NetworkConnection network="vapp net">
+                    <vcloud:NetworkConnectionIndex>0</vcloud:NetworkConnectionIndex>
+                    <vcloud:IpAddress>192.168.2.101</vcloud:IpAddress>
+                    <vcloud:IsConnected>true</vcloud:IsConnected>
+                    <vcloud:MACAddress>00:50:56:01:06:82</vcloud:MACAddress>
+                    <vcloud:IpAddressAllocationMode>POOL</vcloud:IpAddressAllocationMode>
+                </vcloud:NetworkConnection>
+            </vcloud:NetworkConnectionSection>
+            <vcloud:GuestCustomizationSection ovf:required="false">
+                <ovf:Info>Specifies Guest OS Customization Settings</ovf:Info>
+                <vcloud:Enabled>true</vcloud:Enabled>
+                <vcloud:ChangeSid>false</vcloud:ChangeSid>
+                <vcloud:JoinDomainEnabled>false</vcloud:JoinDomainEnabled>
+                <vcloud:UseOrgSettings>false</vcloud:UseOrgSettings>
+                <vcloud:AdminPasswordEnabled>false</vcloud:AdminPasswordEnabled>
+                <vcloud:AdminPasswordAuto>true</vcloud:AdminPasswordAuto>
+                <vcloud:ResetPasswordRequired>false</vcloud:ResetPasswordRequired>
+                <vcloud:ComputerName>centos-web</vcloud:ComputerName>
+            </vcloud:GuestCustomizationSection>
+        </ovf:VirtualSystem>
+    </ovf:VirtualSystemCollection>
+</ovf:Envelope>
diff --git a/apis/vcloud/src/test/resources/template1.0-vcd15-multi-vm.xml b/apis/vcloud/src/test/resources/template1.0-vcd15-multi-vm.xml
new file mode 100644
index 0000000..46b3c83
--- /dev/null
+++ b/apis/vcloud/src/test/resources/template1.0-vcd15-multi-vm.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<VAppTemplate xmlns="http://www.vmware.com/vcloud/v1" xmlns:ovf="http://schemas.dmtf.org/ovf/envelope/1" ovfDescriptorUploaded="true" status="8" name="Windows Server 2008 R2" type="application/vnd.vmware.vcloud.vAppTemplate+xml" href="https://zone.myvcloud.com/api/v1.0/vAppTemplate/vappTemplate-51891b97-c5dd-47dc-a687-aabae354f728" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://schemas.dmtf.org/ovf/envelope/1 http://schemas.dmtf.org/ovf/envelope/1/dsp8023_1.1.0.xsd http://www.vmware.com/vcloud/v1 http://zone.myvcloud.com/api/v1.0/schema/master.xsd">
+    <Link rel="up" type="application/vnd.vmware.vcloud.vdc+xml" href="https://zone.myvcloud.com/api/v1.0/vdc/cf6a0068-b7c4-425c-9e80-46ffef58782a"/>
+    <Link rel="catalogItem" type="application/vnd.vmware.vcloud.catalogItem+xml" href="https://zone.myvcloud.com/api/v1.0/catalogItem/9c45dbbd-910d-45c8-a811-d1e0b01e9e82"/>
+    <Link rel="remove" href="https://zone.myvcloud.com/api/v1.0/vAppTemplate/vappTemplate-51891b97-c5dd-47dc-a687-aabae354f728"/>
+    <Link rel="edit" type="application/vnd.vmware.vcloud.vAppTemplate+xml" href="https://zone.myvcloud.com/api/v1.0/vAppTemplate/vappTemplate-51891b97-c5dd-47dc-a687-aabae354f728"/>
+    <Link rel="enable" href="https://zone.myvcloud.com/api/v1.0/vAppTemplate/vappTemplate-51891b97-c5dd-47dc-a687-aabae354f728/action/enableDownload"/>
+    <Link rel="disable" href="https://zone.myvcloud.com/api/v1.0/vAppTemplate/vappTemplate-51891b97-c5dd-47dc-a687-aabae354f728/action/disableDownload"/>
+    <Link rel="ovf" type="text/xml" href="https://zone.myvcloud.com/api/v1.0/vAppTemplate/vappTemplate-51891b97-c5dd-47dc-a687-aabae354f728/ovf"/>
+    <Link rel="down" type="application/vnd.vmware.vcloud.owner+xml" href="https://zone.myvcloud.com/api/v1.0/vAppTemplate/vappTemplate-51891b97-c5dd-47dc-a687-aabae354f728/owner"/>
+    <Description/>
+    <Children>
+        <Vm name="Windows Server 2008 R2" type="application/vnd.vmware.vcloud.vm+xml" href="https://zone.myvcloud.com/api/v1.0/vAppTemplate/vm-24fc1b05-10f7-423e-a644-fd3368f9d0cd">
+            <Link rel="up" type="application/vnd.vmware.vcloud.vAppTemplate+xml" href="https://zone.myvcloud.com/api/v1.0/vAppTemplate/vappTemplate-51891b97-c5dd-47dc-a687-aabae354f728"/>
+            <Description/>
+            <NetworkConnectionSection type="application/vnd.vmware.vcloud.networkConnectionSection+xml" href="https://zone.myvcloud.com/api/v1.0/vAppTemplate/vm-24fc1b05-10f7-423e-a644-fd3368f9d0cd/networkConnectionSection/" ovf:required="false">
+                <ovf:Info>Specifies the available VM network connections</ovf:Info>
+                <PrimaryNetworkConnectionIndex>0</PrimaryNetworkConnectionIndex>
+                <NetworkConnection network="none">
+                    <NetworkConnectionIndex>0</NetworkConnectionIndex>
+                    <IsConnected>false</IsConnected>
+                    <MACAddress>00:50:56:b5:09:dc</MACAddress>
+                    <IpAddressAllocationMode>NONE</IpAddressAllocationMode>
+                </NetworkConnection>
+            </NetworkConnectionSection>
+            <GuestCustomizationSection type="application/vnd.vmware.vcloud.guestCustomizationSection+xml" href="https://zone.myvcloud.com/api/v1.0/vAppTemplate/vm-24fc1b05-10f7-423e-a644-fd3368f9d0cd/guestCustomizationSection/" ovf:required="false">
+                <ovf:Info>Specifies Guest OS Customization Settings</ovf:Info>
+                <Enabled>true</Enabled>
+                <ChangeSid>false</ChangeSid>
+                <JoinDomainEnabled>false</JoinDomainEnabled>
+                <UseOrgSettings>false</UseOrgSettings>
+                <AdminPasswordEnabled>true</AdminPasswordEnabled>
+                <AdminPasswordAuto>true</AdminPasswordAuto>
+                <ResetPasswordRequired>true</ResetPasswordRequired>                <ComputerName>WindowsServ-001</ComputerName>
+            </GuestCustomizationSection>
+            <VAppScopedLocalId>Windows Server 2008 R2</VAppScopedLocalId>
+        </Vm>
+    </Children>
+    <ovf:NetworkSection xmlns:vcloud="http://www.vmware.com/vcloud/v1" vcloud:href="https://zone.myvcloud.com/api/v1.0/vAppTemplate/vappTemplate-51891b97-c5dd-47dc-a687-aabae354f728/networkSection/" vcloud:type="application/vnd.vmware.vcloud.networkSection+xml">        <ovf:Info>The list of logical networks</ovf:Info>
+        <ovf:Network ovf:name="none">
+            <ovf:Description>This is a special place-holder used for disconnected network interfaces.</ovf:Description>
+        </ovf:Network>    </ovf:NetworkSection>
+    <NetworkConfigSection type="application/vnd.vmware.vcloud.networkConfigSection+xml" href="https://zone.myvcloud.com/api/v1.0/vAppTemplate/vappTemplate-51891b97-c5dd-47dc-a687-aabae354f728/networkConfigSection/" ovf:required="false">
+        <ovf:Info>The configuration parameters for logical networks</ovf:Info>
+        <NetworkConfig networkName="none">
+            <Description>This is a special place-holder used for disconnected network interfaces.</Description>
+            <Configuration>
+                <IpScope>
+                    <IsInherited>false</IsInherited>
+                    <Gateway>196.254.254.254</Gateway>
+                    <Netmask>255.255.0.0</Netmask>
+                    <Dns1>196.254.254.254</Dns1>
+                </IpScope>
+                <FenceMode>isolated</FenceMode>
+            </Configuration>
+            <IsDeployed>false</IsDeployed>
+        </NetworkConfig>
+    </NetworkConfigSection>    
+    <LeaseSettingsSection type="application/vnd.vmware.vcloud.leaseSettingsSection+xml" href="https://zone.myvcloud.com/api/v1.0/vAppTemplate/vappTemplate-51891b97-c5dd-47dc-a687-aabae354f728/leaseSettingsSection/" ovf:required="false">
+        <ovf:Info>Lease settings section</ovf:Info>        <Link rel="edit" type="application/vnd.vmware.vcloud.leaseSettingsSection+xml" href="https://zone.myvcloud.com/api/v1.0/vAppTemplate/vappTemplate-51891b97-c5dd-47dc-a687-aabae354f728/leaseSettingsSection/"/>
+        <StorageLeaseInSeconds>0</StorageLeaseInSeconds>
+    </LeaseSettingsSection>    
+        <CustomizationSection type="application/vnd.vmware.vcloud.customizationSection+xml" href="https://zone.myvcloud.com/api/v1.0/vAppTemplate/vappTemplate-51891b97-c5dd-47dc-a687-aabae354f728/customizationSection/" ovf:required="false">
+        <ovf:Info>VApp template customization section</ovf:Info>
+        <CustomizeOnInstantiate>true</CustomizeOnInstantiate>        
+        <Link rel="edit" type="application/vnd.vmware.vcloud.customizationSection+xml" href="https://zone.myvcloud.com/api/v1.0/vAppTemplate/vappTemplate-51891b97-c5dd-47dc-a687-aabae354f728/customizationSection/"/>
+    </CustomizationSection>
+</VAppTemplate>
diff --git a/common/openstack/src/main/java/org/jclouds/openstack/domain/Link.java b/common/openstack/src/main/java/org/jclouds/openstack/domain/Link.java
index 0a7ca38..7d4df5c 100644
--- a/common/openstack/src/main/java/org/jclouds/openstack/domain/Link.java
+++ b/common/openstack/src/main/java/org/jclouds/openstack/domain/Link.java
@@ -23,12 +23,14 @@
 import static com.google.common.base.Objects.toStringHelper;
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import java.beans.ConstructorProperties;
 import java.net.URI;
 
+import javax.inject.Named;
+
 import org.jclouds.javax.annotation.Nullable;
 
 import com.google.common.base.Objects;
-import com.google.gson.annotations.SerializedName;
 
 /**
  * For convenience, resources contain links to themselves. This allows a client to easily obtain a
@@ -134,17 +136,12 @@
       }
    }
   
-   protected Link() {
-      // we want serializers like Gson to work w/o using sun.misc.Unsafe,
-      // prohibited in GAE. This also implies fields are not final.
-      // see http://code.google.com/p/jclouds/issues/detail?id=925
-   }
-
-   @SerializedName("rel")
+   @Named("rel")
    protected Relation relation;
    protected String type;
    protected URI href;
 
+   @ConstructorProperties({"rel", "type", "href"})
    protected Link(Relation relation, @Nullable String type, URI href) {
       this.relation = checkNotNull(relation, "relation");
       this.type = type;
diff --git a/common/openstack/src/main/java/org/jclouds/openstack/domain/Resource.java b/common/openstack/src/main/java/org/jclouds/openstack/domain/Resource.java
index 60dceef..83fe7ed 100644
--- a/common/openstack/src/main/java/org/jclouds/openstack/domain/Resource.java
+++ b/common/openstack/src/main/java/org/jclouds/openstack/domain/Resource.java
@@ -94,8 +94,7 @@
          return this
                .id(in.getId())
                .name(in.getName())
-               .links(in.getLinks())
-               ;
+               .links(in.getLinks());
       }
    }
 
@@ -105,17 +104,18 @@
          return this;
       }
    }
-   
-   protected Resource() {
-      // we want serializers like Gson to work w/o using sun.misc.Unsafe,
-      // prohibited in GAE. This also implies fields are not final.
-      // see http://code.google.com/p/jclouds/issues/detail?id=925
-   }
 
    private String id;
    private String name;
    private Set<Link> links = ImmutableSet.of();
 
+   protected Resource(String id, @Nullable String name, @Nullable Set<Link> links) {
+      this.id = checkNotNull(id);
+      this.name = name;
+      this.links = links == null ? ImmutableSet.<Link>of() : ImmutableSet.copyOf(links);
+   }
+   
+   @Deprecated
    protected Resource(Builder<?> builder) {
       this.id = checkNotNull(builder.id, "id");
       this.name = builder.name;
diff --git a/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/config/DefaultOrgForUser.java b/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/config/DefaultOrgForUser.java
index 448489b..eeb50c9 100644
--- a/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/config/DefaultOrgForUser.java
+++ b/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/config/DefaultOrgForUser.java
@@ -30,11 +30,11 @@
 import org.jclouds.trmk.vcloud_0_8.domain.VCloudSession;
 import org.jclouds.trmk.vcloud_0_8.endpoints.Org;
 import org.jclouds.trmk.vcloud_0_8.suppliers.OnlyReferenceTypeFirstWithNameMatchingConfigurationKeyOrDefault;
+import org.jclouds.util.Suppliers2;
 
 import com.google.common.base.Function;
 import com.google.common.base.Predicate;
 import com.google.common.base.Supplier;
-import com.google.common.base.Suppliers;
 
 /**
  * 
@@ -57,7 +57,7 @@
 
    @Override
    public Supplier<ReferenceType> apply(final String user) {
-      return Suppliers.compose(new Function<VCloudSession, ReferenceType>() {
+      return Suppliers2.compose(new Function<VCloudSession, ReferenceType>() {
 
          @Override
          public ReferenceType apply(VCloudSession session) {
diff --git a/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/config/DefaultVCloudReferencesModule.java b/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/config/DefaultVCloudReferencesModule.java
index e37f5eb..c2b2efd 100644
--- a/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/config/DefaultVCloudReferencesModule.java
+++ b/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/config/DefaultVCloudReferencesModule.java
@@ -26,6 +26,7 @@
 import org.jclouds.rest.annotations.Identity;
 import org.jclouds.trmk.vcloud_0_8.domain.Catalog;
 import org.jclouds.trmk.vcloud_0_8.domain.ReferenceType;
+import org.jclouds.util.Suppliers2;
 
 import com.google.common.base.Function;
 import com.google.common.base.Predicate;
@@ -69,7 +70,7 @@
    @Singleton
    protected Supplier<ReferenceType> provideDefaultTasksList(DefaultTasksListForOrg defaultTasksListURIForOrg,
          @org.jclouds.trmk.vcloud_0_8.endpoints.Org Supplier<ReferenceType> defaultOrg) {
-      return Suppliers.compose(defaultTasksListURIForOrg, defaultOrg);
+      return Suppliers2.compose(defaultTasksListURIForOrg, defaultOrg);
    }
 
    @Provides
@@ -84,7 +85,7 @@
    @Singleton
    protected Supplier<ReferenceType> provideDefaultCatalog(DefaultCatalogForOrg defaultCatalogURIForOrg,
          @org.jclouds.trmk.vcloud_0_8.endpoints.Org Supplier<ReferenceType> defaultOrg) {
-      return Suppliers.compose(defaultCatalogURIForOrg, defaultOrg);
+      return Suppliers2.compose(defaultCatalogURIForOrg, defaultOrg);
    }
 
    @Provides
@@ -121,7 +122,7 @@
    @Singleton
    protected Supplier<ReferenceType> provideDefaultVDC(DefaultVDCForOrg defaultVDCURIForOrg,
          @org.jclouds.trmk.vcloud_0_8.endpoints.Org Supplier<ReferenceType> defaultOrg) {
-      return Suppliers.compose(defaultVDCURIForOrg, defaultOrg);
+      return Suppliers2.compose(defaultVDCURIForOrg, defaultOrg);
    }
 
    @Provides
@@ -136,7 +137,7 @@
    @Singleton
    protected Supplier<ReferenceType> provideDefaultNetwork(DefaultNetworkForVDC defaultNetworkURIForVDC,
          @org.jclouds.trmk.vcloud_0_8.endpoints.VDC Supplier<ReferenceType> defaultVDC) {
-      return Suppliers.compose(defaultNetworkURIForVDC, defaultVDC);
+      return Suppliers2.compose(defaultNetworkURIForVDC, defaultVDC);
    }
 
    @Provides
diff --git a/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/config/TerremarkVCloudRestClientModule.java b/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/config/TerremarkVCloudRestClientModule.java
index 848eab3..0a5e86b 100644
--- a/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/config/TerremarkVCloudRestClientModule.java
+++ b/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/config/TerremarkVCloudRestClientModule.java
@@ -32,8 +32,9 @@
 import java.io.IOException;
 import java.net.URI;
 import java.util.Map;
-import java.util.SortedMap;
 import java.util.Map.Entry;
+import java.util.SortedMap;
+import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicReference;
 
 import javax.inject.Inject;
@@ -81,14 +82,15 @@
 import org.jclouds.trmk.vcloud_0_8.location.OrgAndVDCToLocationSupplier;
 import org.jclouds.trmk.vcloud_0_8.predicates.TaskSuccess;
 import org.jclouds.util.Strings2;
+import org.jclouds.util.Suppliers2;
 
 import com.google.common.base.Function;
+import com.google.common.base.Objects;
 import com.google.common.base.Predicate;
 import com.google.common.base.Supplier;
-import com.google.common.base.Suppliers;
 import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Maps;
 import com.google.common.collect.ImmutableMap.Builder;
+import com.google.common.collect.Maps;
 import com.google.inject.Injector;
 import com.google.inject.Provides;
 import com.google.inject.Scopes;
@@ -141,7 +143,7 @@
    @org.jclouds.trmk.vcloud_0_8.endpoints.VDC
    protected Supplier<Map<String, String>> provideVDCtoORG(
          Supplier<Map<String, ? extends org.jclouds.trmk.vcloud_0_8.domain.Org>> orgNameToOrgSuppier) {
-      return Suppliers.compose(
+      return Suppliers2.compose(
             new Function<Map<String, ? extends org.jclouds.trmk.vcloud_0_8.domain.Org>, Map<String, String>>() {
 
                @Override
@@ -163,15 +165,15 @@
    protected Supplier<Map<String, ? extends org.jclouds.trmk.vcloud_0_8.domain.Org>> provideOrgMapCache(
          @Named(PROPERTY_SESSION_INTERVAL) long seconds, AtomicReference<AuthorizationException> authException,
          OrgMapSupplier supplier) {
-      return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier<Map<String, ? extends org.jclouds.trmk.vcloud_0_8.domain.Org>>(
-            authException, seconds, supplier);
+      return MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(authException, supplier, seconds,
+               TimeUnit.SECONDS);
    }
 
    @Provides
    @Singleton
    @OrgList
    protected Supplier<URI> provideOrgListURI(Supplier<VCloudSession> sessionSupplier) {
-      return Suppliers.compose(new Function<VCloudSession, URI>() {
+      return Suppliers2.compose(new Function<VCloudSession, URI>() {
 
          @Override
          public URI apply(VCloudSession arg0) {
@@ -250,8 +252,8 @@
    @Singleton
    protected Supplier<Map<String, ReferenceType>> provideVDCtoORG(@Named(PROPERTY_SESSION_INTERVAL) long seconds,
          AtomicReference<AuthorizationException> authException, OrgNameToOrgSupplier supplier) {
-      return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier<Map<String, ReferenceType>>(
-            authException, seconds, supplier);
+      return MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(authException, supplier, seconds,
+               TimeUnit.SECONDS);
    }
 
    @Provides
@@ -259,8 +261,8 @@
    protected Supplier<Map<URI, ? extends org.jclouds.trmk.vcloud_0_8.domain.VDC>> provideURIToVDC(
          @Named(PROPERTY_SESSION_INTERVAL) long seconds, AtomicReference<AuthorizationException> authException,
          URItoVDC supplier) {
-      return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier<Map<URI, ? extends org.jclouds.trmk.vcloud_0_8.domain.VDC>>(
-            authException, seconds, supplier);
+      return MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(authException, supplier, seconds,
+               TimeUnit.SECONDS);
    }
 
    @Singleton
@@ -333,7 +335,6 @@
    private static class OrgNameToOrgSupplier implements Supplier<Map<String, ReferenceType>> {
       private final Supplier<VCloudSession> sessionSupplier;
 
-      @SuppressWarnings("unused")
       @Inject
       OrgNameToOrgSupplier(Supplier<VCloudSession> sessionSupplier) {
          this.sessionSupplier = sessionSupplier;
@@ -351,7 +352,7 @@
    protected Supplier<org.jclouds.trmk.vcloud_0_8.domain.Org> provideOrg(
          final Supplier<Map<String, ? extends org.jclouds.trmk.vcloud_0_8.domain.Org>> orgSupplier,
          @org.jclouds.trmk.vcloud_0_8.endpoints.Org Supplier<ReferenceType> defaultOrg) {
-      return Suppliers.compose(new Function<ReferenceType, org.jclouds.trmk.vcloud_0_8.domain.Org>() {
+      return Suppliers2.compose(new Function<ReferenceType, org.jclouds.trmk.vcloud_0_8.domain.Org>() {
 
          @Override
          public org.jclouds.trmk.vcloud_0_8.domain.Org apply(ReferenceType input) {
@@ -373,8 +374,8 @@
    protected Supplier<Map<String, Map<String, ? extends org.jclouds.trmk.vcloud_0_8.domain.Catalog>>> provideOrgCatalogItemMapSupplierCache(
          @Named(PROPERTY_SESSION_INTERVAL) long seconds, AtomicReference<AuthorizationException> authException,
          OrgCatalogSupplier supplier) {
-      return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier<Map<String, Map<String, ? extends org.jclouds.trmk.vcloud_0_8.domain.Catalog>>>(
-            authException, seconds, supplier);
+      return MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(authException, supplier, seconds,
+               TimeUnit.SECONDS);
    }
 
    @Provides
@@ -382,8 +383,8 @@
    protected Supplier<Map<String, Map<String, ? extends org.jclouds.trmk.vcloud_0_8.domain.VDC>>> provideOrgVDCSupplierCache(
          @Named(PROPERTY_SESSION_INTERVAL) long seconds, AtomicReference<AuthorizationException> authException,
          OrgVDCSupplier supplier) {
-      return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier<Map<String, Map<String, ? extends org.jclouds.trmk.vcloud_0_8.domain.VDC>>>(
-            authException, seconds, supplier);
+      return MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(authException, supplier, seconds,
+               TimeUnit.SECONDS);
    }
 
    @Singleton
@@ -460,8 +461,8 @@
    protected Supplier<Map<String, Map<String, Map<String, ? extends org.jclouds.trmk.vcloud_0_8.domain.CatalogItem>>>> provideOrgCatalogItemSupplierCache(
          @Named(PROPERTY_SESSION_INTERVAL) long seconds, AtomicReference<AuthorizationException> authException,
          OrgCatalogItemSupplier supplier) {
-      return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier<Map<String, Map<String, Map<String, ? extends org.jclouds.trmk.vcloud_0_8.domain.CatalogItem>>>>(
-            authException, seconds, supplier);
+      return MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(authException, supplier, seconds,
+               TimeUnit.SECONDS);
    }
 
    /**
@@ -479,15 +480,19 @@
    @Singleton
    protected Supplier<VCloudSession> provideVCloudTokenCache(@Named(PROPERTY_SESSION_INTERVAL) long seconds,
             AtomicReference<AuthorizationException> authException, final TerremarkVCloudLoginClient login) {
-      return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier<VCloudSession>(authException, seconds,
+      return MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(authException,
                new Supplier<VCloudSession>() {
 
                   @Override
                   public VCloudSession get() {
                      return login.login();
                   }
-
-               });
+                  
+                  @Override
+                  public String toString() {
+                     return Objects.toStringHelper(login).add("method", "login").toString();
+                  }
+               }, seconds, TimeUnit.SECONDS);
    }
 
    @Singleton
@@ -541,8 +546,8 @@
    protected Supplier<Map<String, ReferenceType>> provideOrgToKeysListCache(
          @Named(PROPERTY_SESSION_INTERVAL) long seconds, AtomicReference<AuthorizationException> authException,
          OrgNameToKeysListSupplier supplier) {
-      return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier<Map<String, ReferenceType>>(
-            authException, seconds, supplier);
+      return MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(authException, supplier, seconds,
+               TimeUnit.SECONDS);
    }
 
    @Singleton
diff --git a/compute/src/main/java/org/jclouds/compute/config/BaseComputeServiceContextModule.java b/compute/src/main/java/org/jclouds/compute/config/BaseComputeServiceContextModule.java
index 3c95c02..fbe346d 100644
--- a/compute/src/main/java/org/jclouds/compute/config/BaseComputeServiceContextModule.java
+++ b/compute/src/main/java/org/jclouds/compute/config/BaseComputeServiceContextModule.java
@@ -26,6 +26,7 @@
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.Callable;
+import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicReference;
 
 import javax.inject.Named;
@@ -61,11 +62,11 @@
 import org.jclouds.rest.suppliers.MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier;
 import org.jclouds.scriptbuilder.domain.Statement;
 import org.jclouds.ssh.SshClient;
+import org.jclouds.util.Suppliers2;
 
 import com.google.common.base.Function;
 import com.google.common.base.Optional;
 import com.google.common.base.Supplier;
-import com.google.common.base.Suppliers;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Maps;
 import com.google.inject.AbstractModule;
@@ -209,7 +210,7 @@
    @Provides
    @Singleton
    protected Supplier<Map<String, ? extends Image>> provideImageMap(@Memoized Supplier<Set<? extends Image>> images) {
-      return Suppliers.compose(new Function<Set<? extends Image>, Map<String, ? extends Image>>() {
+      return Suppliers2.compose(new Function<Set<? extends Image>, Map<String, ? extends Image>>() {
 
          @Override
          public Map<String, ? extends Image> apply(Set<? extends Image> from) {
@@ -244,13 +245,8 @@
 
    protected Supplier<Set<? extends Image>> supplyImageCache(AtomicReference<AuthorizationException> authException, @Named(PROPERTY_SESSION_INTERVAL) long seconds,
          final Supplier<Set<? extends Image>> imageSupplier) {
-      return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier<Set<? extends Image>>(authException,
-            seconds, new Supplier<Set<? extends Image>>() {
-               @Override
-               public Set<? extends Image> get() {
-                  return imageSupplier.get();
-               }
-            });
+      return MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(authException, imageSupplier, seconds,
+               TimeUnit.SECONDS);
    }
 
    /**
@@ -264,7 +260,7 @@
    @Provides
    @Singleton
    protected Supplier<Map<String, ? extends Hardware>> provideSizeMap(@Memoized Supplier<Set<? extends Hardware>> sizes) {
-      return Suppliers.compose(new Function<Set<? extends Hardware>, Map<String, ? extends Hardware>>() {
+      return Suppliers2.compose(new Function<Set<? extends Hardware>, Map<String, ? extends Hardware>>() {
 
          @Override
          public Map<String, ? extends Hardware> apply(Set<? extends Hardware> from) {
@@ -286,13 +282,8 @@
    @Memoized
    protected Supplier<Set<? extends Hardware>> supplySizeCache(AtomicReference<AuthorizationException> authException, @Named(PROPERTY_SESSION_INTERVAL) long seconds,
          final Supplier<Set<? extends Hardware>> hardwareSupplier) {
-      return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier<Set<? extends Hardware>>(authException,
-            seconds, new Supplier<Set<? extends Hardware>>() {
-               @Override
-               public Set<? extends Hardware> get() {
-                  return hardwareSupplier.get();
-               }
-            });
+      return MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(authException, hardwareSupplier,
+               seconds, TimeUnit.SECONDS);
    }
 
    @Provides
diff --git a/compute/src/main/java/org/jclouds/compute/config/ComputeServiceAdapterContextModule.java b/compute/src/main/java/org/jclouds/compute/config/ComputeServiceAdapterContextModule.java
index cdeb42d..e088529 100644
--- a/compute/src/main/java/org/jclouds/compute/config/ComputeServiceAdapterContextModule.java
+++ b/compute/src/main/java/org/jclouds/compute/config/ComputeServiceAdapterContextModule.java
@@ -48,6 +48,7 @@
 import org.jclouds.location.suppliers.LocationsSupplier;
 
 import com.google.common.base.Function;
+import com.google.common.base.Objects;
 import com.google.common.base.Supplier;
 import com.google.common.collect.ImmutableSet;
 import com.google.inject.AbstractModule;
@@ -88,6 +89,11 @@
                Iterable<L> locations = filter(adapter.listLocations(), notNull());
                return ImmutableSet.<Location> copyOf(transform(locations, transformer));
             }
+
+            @Override
+            public String toString() {
+               return Objects.toStringHelper(adapter).add("method", "listLocations").toString();
+            }
          };
       }
    }
@@ -102,7 +108,12 @@
          public Iterable<H> get() {
             return adapter.listHardwareProfiles();
          }
-
+         
+         @Override
+         public String toString() {
+            return Objects.toStringHelper(adapter).add("method", "listHardwareProfiles").toString();
+         }
+         
       }, transformer);
    }
 
@@ -116,7 +127,12 @@
          public Iterable<I> get() {
             return filter(adapter.listImages(), notNull());
          }
-
+         
+         @Override
+         public String toString() {
+            return Objects.toStringHelper(adapter).add("method", "listImages").toString();
+         }
+         
       }, compose(addDefaultCredentialsToImage, transformer));
    }
 
@@ -139,7 +155,7 @@
 
       @Override
       public String toString() {
-         return "addDefaultCredentialsToImage()";
+         return Objects.toStringHelper(this).add("credsForImage", credsForImage).toString();
       }
    }
 
diff --git a/compute/src/main/java/org/jclouds/compute/config/ComputeServiceTimeoutsModule.java b/compute/src/main/java/org/jclouds/compute/config/ComputeServiceTimeoutsModule.java
index 39a8195..adb01e7 100644
--- a/compute/src/main/java/org/jclouds/compute/config/ComputeServiceTimeoutsModule.java
+++ b/compute/src/main/java/org/jclouds/compute/config/ComputeServiceTimeoutsModule.java
@@ -57,7 +57,7 @@
    @Singleton
    @Named(TIMEOUT_NODE_RUNNING)
    protected Predicate<AtomicReference<NodeMetadata>> nodeRunning(AtomicNodeRunning statusRunning, Timeouts timeouts) {
-      return timeouts.nodeRunning == 0 ? statusRunning : new RetryablePredicate<AtomicReference<NodeMetadata>>(statusRunning,
+      return timeouts.nodeRunning == 0 ? statusRunning : new RetryablePredicateGuardingNull<NodeMetadata>(statusRunning,
             timeouts.nodeRunning);
    }
 
@@ -74,7 +74,7 @@
    @Singleton
    @Named(TIMEOUT_NODE_SUSPENDED)
    protected Predicate<AtomicReference<NodeMetadata>> serverSuspended(AtomicNodeSuspended statusSuspended, Timeouts timeouts) {
-      return timeouts.nodeSuspended == 0 ? statusSuspended : new RetryablePredicate<AtomicReference<NodeMetadata>>(statusSuspended,
+      return timeouts.nodeSuspended == 0 ? statusSuspended : new RetryablePredicateGuardingNull<NodeMetadata>(statusSuspended,
             timeouts.nodeSuspended);
    }
    
@@ -90,7 +90,7 @@
    @Singleton
    @Named(TIMEOUT_IMAGE_AVAILABLE)
    protected Predicate<AtomicReference<Image>> imageAvailable(AtomicImageAvailable statusAvailable, Timeouts timeouts) {
-      return timeouts.imageAvailable == 0 ? statusAvailable : new RetryablePredicate<AtomicReference<Image>>(statusAvailable,
+      return timeouts.imageAvailable == 0 ? statusAvailable : new RetryablePredicateGuardingNull<Image>(statusAvailable,
             timeouts.imageAvailable);
    }
 
@@ -106,4 +106,46 @@
    protected void configure() {
 
    }
-}
\ No newline at end of file
+
+   /**
+    * Avoids "losing" the ComputeMetadata if client returns null temporarily (issue #989).
+    * Ensures we always pass a non-null to the wrapped predicate, but will propagate the null to
+    * the caller qt the end.
+    * 
+    * @author Aled Sage
+    */
+   private static class RetryablePredicateGuardingNull<T> implements Predicate<AtomicReference<T>> {
+      private class AtomicRefAndOrig {
+         private final T orig;
+         private final AtomicReference<T> ref;
+         
+         AtomicRefAndOrig(T orig, AtomicReference<T> ref) {
+            this.orig = orig;
+            this.ref = ref;
+         }
+      }
+      
+      private final RetryablePredicate<AtomicRefAndOrig> retryablePredicate;
+      
+      public RetryablePredicateGuardingNull(final Predicate<AtomicReference<T>> predicate, long maxWait) {
+         Predicate<AtomicRefAndOrig> nonNullThingPredicate = new Predicate<AtomicRefAndOrig>() {
+            @Override
+            public boolean apply(AtomicRefAndOrig input) {
+               AtomicReference<T> ref = (input.ref.get() != null) ? input.ref : new AtomicReference<T>(input.orig);
+               try {
+                  return predicate.apply(ref);
+               } finally {
+                  input.ref.set(ref.get());
+               }
+            }
+         };
+         retryablePredicate = new RetryablePredicate<AtomicRefAndOrig>(nonNullThingPredicate, maxWait);
+      }
+
+      @Override
+      public boolean apply(AtomicReference<T> input) {
+         AtomicRefAndOrig refAndOrig = new AtomicRefAndOrig(input.get(), input);
+         return retryablePredicate.apply(refAndOrig);
+      }
+   }
+}
diff --git a/compute/src/main/java/org/jclouds/compute/domain/internal/TemplateBuilderImpl.java b/compute/src/main/java/org/jclouds/compute/domain/internal/TemplateBuilderImpl.java
index e75fc7d..bbf3fa8 100644
--- a/compute/src/main/java/org/jclouds/compute/domain/internal/TemplateBuilderImpl.java
+++ b/compute/src/main/java/org/jclouds/compute/domain/internal/TemplateBuilderImpl.java
@@ -19,6 +19,7 @@
 package org.jclouds.compute.domain.internal;
 
 import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
 import static com.google.common.base.Predicates.and;
 import static com.google.common.collect.Iterables.filter;
 import static com.google.common.collect.Iterables.find;
@@ -140,6 +141,29 @@
       this.defaultTemplateProvider = defaultTemplateProvider;
    }
 
+   static Predicate<Hardware> supportsImagesPredicate(final Iterable<? extends Image> images) {
+      return new Predicate<Hardware>() {
+         @Override
+         public boolean apply(final Hardware hardware) {
+            return Iterables.any(images, new Predicate<Image>() {
+
+               @Override
+               public boolean apply(Image input) {
+                  return hardware.supportsImage().apply(input);
+               }
+
+               @Override
+               public String toString() {
+                  return "hardware(" + hardware + ").supportsImage()";
+               }
+
+            });
+
+         }
+
+      };
+   } 
+
    final Predicate<ComputeMetadata> locationPredicate = new NullEqualToIsParentOrIsGrandparentOfCurrentLocation(new Supplier<Location>(){
 
       @Override
@@ -627,7 +651,9 @@
          options = optionsProvider.get();
       logger.debug(">> searching params(%s)", this);
       Set<? extends Image> images = getImages();
+      checkState(images.size() > 0, "no images present!");
       Set<? extends Hardware> hardwaresToSearch = hardwares.get();
+      checkState(hardwaresToSearch.size() > 0, "no hardware profiles present!");
 
       Image image = null;
       if (imageId != null) {
@@ -705,44 +731,36 @@
          logger.warn(exception, "hardware ids that didn't match: %s", transform(hardwares, hardwareToId));
       throw exception;
    }
-   
+
    protected Hardware resolveHardware(Set<? extends Hardware> hardwarel, final Iterable<? extends Image> images) {
       Ordering<Hardware> hardwareOrdering = hardwareSorter();
-      Iterable<? extends Hardware> hardwaresThatAreCompatibleWithOurImages = ImmutableSet.of();
-      try {
-         hardwaresThatAreCompatibleWithOurImages = filter(hardwarel, new Predicate<Hardware>() {
-            @Override
-            public boolean apply(final Hardware hardware) {
-               return Iterables.any(images, new Predicate<Image>() {
+      
+      Iterable<Predicate<Image>> supportsImagePredicates = Iterables.transform(hardwarel,
+               new Function<Hardware, Predicate<Image>>() {
 
                   @Override
-                  public boolean apply(Image input) {
-                     return hardware.supportsImage().apply(input);
-                  }
-
-                  @Override
-                  public String toString() {
-                     return "hardware(" + hardware + ").supportsImage()";
+                  public Predicate<Image> apply(Hardware input) {
+                     return input.supportsImage();
                   }
 
                });
+      
+      Predicate<Image> supportsImagePredicate = Iterables.size(supportsImagePredicates) == 1 ? Iterables
+               .getOnlyElement(supportsImagePredicates) : Predicates.<Image>or(supportsImagePredicates);
 
-            }
-         });
-      } catch (NoSuchElementException exception) {
+      if (!Iterables.any(images, supportsImagePredicate)) {
+         String message = format("no hardware profiles support images matching params: %s", supportsImagePredicate);
+         throw throwNoSuchElementExceptionAfterLoggingHardwareIds(message, hardwarel);
+      }
 
-      }
-      if (size(hardwaresThatAreCompatibleWithOurImages) == 0) {
-         String message = format("no hardware profiles support images matching params: %s", toString());
-         throw throwNoSuchElementExceptionAfterLoggingHardwareIds(message, hardwaresThatAreCompatibleWithOurImages);
-      }
+      Iterable<? extends Hardware> hardwareCompatibleWithOurImages = filter(hardwarel, supportsImagesPredicate(images));
       Predicate<Hardware> hardwarePredicate = buildHardwarePredicate();
       Hardware hardware;
       try {
-         hardware = hardwareOrdering.max(filter(hardwaresThatAreCompatibleWithOurImages, hardwarePredicate));
+         hardware = hardwareOrdering.max(filter(hardwareCompatibleWithOurImages, hardwarePredicate));
       } catch (NoSuchElementException exception) {
          String message = format("no hardware profiles match params: %s", hardwarePredicate);
-         throw throwNoSuchElementExceptionAfterLoggingHardwareIds(message, hardwaresThatAreCompatibleWithOurImages);
+         throw throwNoSuchElementExceptionAfterLoggingHardwareIds(message, hardwareCompatibleWithOurImages);
       }
       logger.trace("<<   matched hardware(%s)", hardware.getId());
       return hardware;
diff --git a/compute/src/main/java/org/jclouds/compute/strategy/impl/ReturnCredentialsBoundToImage.java b/compute/src/main/java/org/jclouds/compute/strategy/impl/ReturnCredentialsBoundToImage.java
index 2e51b9b..a049e83 100644
--- a/compute/src/main/java/org/jclouds/compute/strategy/impl/ReturnCredentialsBoundToImage.java
+++ b/compute/src/main/java/org/jclouds/compute/strategy/impl/ReturnCredentialsBoundToImage.java
@@ -33,6 +33,8 @@
 import org.jclouds.domain.LoginCredentials;
 import org.jclouds.javax.annotation.Nullable;
 
+import com.google.common.base.Objects;
+
 /**
  * @author Adrian Cole
  */
@@ -67,4 +69,9 @@
          return LoginCredentials.builder().user("root").build();
       }
    }
+   
+   @Override
+   public String toString() {
+      return Objects.toStringHelper(this).toString();
+   }
 }
diff --git a/compute/src/test/java/org/jclouds/compute/domain/internal/TemplateBuilderImplTest.java b/compute/src/test/java/org/jclouds/compute/domain/internal/TemplateBuilderImplTest.java
index 1f4bb92..e7fa698 100644
--- a/compute/src/test/java/org/jclouds/compute/domain/internal/TemplateBuilderImplTest.java
+++ b/compute/src/test/java/org/jclouds/compute/domain/internal/TemplateBuilderImplTest.java
@@ -58,58 +58,51 @@
  */
 @Test(groups = "unit", singleThreaded = true, testName = "TemplateBuilderImplTest")
 public class TemplateBuilderImplTest {
-   Location provider = new LocationBuilder().scope(LocationScope.PROVIDER).id("aws-ec2").description("aws-ec2").build();
+   protected Location provider = new LocationBuilder().scope(LocationScope.PROVIDER).id("aws-ec2").description("aws-ec2").build();
 
-   protected Location region = new LocationBuilder().scope(LocationScope.REGION).id("us-east-1").description("us-east-1")
-         .parent(provider).build();
-   
-   protected Location region2 = new LocationBuilder().scope(LocationScope.REGION).id("us-east-2").description("us-east-2")
-         .parent(provider).build();
+   protected Location region = new LocationBuilder().scope(LocationScope.REGION).id("us-east-1")
+            .description("us-east-1").parent(provider).build();
+
+   protected Location region2 = new LocationBuilder().scope(LocationScope.REGION).id("us-east-2")
+            .description("us-east-2").parent(provider).build();
+
+   protected OperatingSystem os = OperatingSystem.builder().name("osName").version("osVersion")
+            .description("osDescription").arch("X86_32").build();
+
+   protected Image image = new ImageBuilder().id("us-east-1/imageId").providerId("imageId").name("imageName")
+            .description("imageDescription").version("imageVersion").operatingSystem(os).status(Image.Status.AVAILABLE)
+            .location(region).build();
+
+   protected Image image2 = ImageBuilder.fromImage(image).operatingSystem(os.toBuilder().arch("X86_64").build()).build();
    
    @SuppressWarnings("unchecked")
    public void testLocationPredicateWhenComputeMetadataIsNotLocationBound() {
-      Image image = createMock(Image.class);
-      OperatingSystem os = createMock(OperatingSystem.class);
 
       Hardware hardware = new HardwareBuilder().id("hardwareId").build();
 
       Supplier<Set<? extends Location>> locations = Suppliers.<Set<? extends Location>> ofInstance(ImmutableSet
                .<Location> of(region));
-      Supplier<Set<? extends Image>> images = Suppliers.<Set<? extends Image>> ofInstance(ImmutableSet.<Image> of(
-               image));
+      Supplier<Set<? extends Image>> images = Suppliers.<Set<? extends Image>> ofInstance(ImmutableSet.<Image> of(image));
       Supplier<Set<? extends Hardware>> hardwares = Suppliers.<Set<? extends Hardware>> ofInstance(ImmutableSet
                .<Hardware> of(hardware));
       Provider<TemplateOptions> optionsProvider = createMock(Provider.class);
       Provider<TemplateBuilder> templateBuilderProvider = createMock(Provider.class);
       TemplateBuilder defaultTemplate = createMock(TemplateBuilder.class);
 
-      expect(image.getLocation()).andReturn(region).anyTimes();
-      expect(image.getProviderId()).andReturn("imageId").anyTimes();
 
-      replay(image);
-      replay(os);
-      replay(defaultTemplate);
-      replay(optionsProvider);
-      replay(templateBuilderProvider);
+      replay(defaultTemplate, optionsProvider, templateBuilderProvider);
 
       TemplateBuilderImpl template = createTemplateBuilder(null, locations, images, hardwares, region,
                optionsProvider, templateBuilderProvider);
       assert template.locationPredicate.apply(hardware);
 
-      verify(image);
-      verify(os);
-      verify(defaultTemplate);
-      verify(optionsProvider);
-      verify(templateBuilderProvider);
+      verify(defaultTemplate, optionsProvider, templateBuilderProvider);
    }
    
    @SuppressWarnings("unchecked")
    @Test
    public void testResolveImages() {
-      Image image = createMock(Image.class);
-      OperatingSystem os = createMock(OperatingSystem.class);
-      Image image2 = createMock(Image.class);
-      OperatingSystem os2 = createMock(OperatingSystem.class);
+
 
       Hardware hardware = new HardwareBuilder().id("hardwareId").build();
 
@@ -123,56 +116,19 @@
       Provider<TemplateBuilder> templateBuilderProvider = createMock(Provider.class);
       TemplateBuilder defaultTemplate = createMock(TemplateBuilder.class);
 
-      expect(image.getName()).andReturn("imageName");
-      expect(image2.getName()).andReturn("imageName");
-      expect(image.getDescription()).andReturn("imageDescription");
-      expect(image2.getDescription()).andReturn("imageDescription");
-      expect(image.getVersion()).andReturn("imageVersion");
-      expect(image2.getVersion()).andReturn("imageVersion");
-      expect(image.getOperatingSystem()).andReturn(os).atLeastOnce();
-      expect(image2.getOperatingSystem()).andReturn(os2).atLeastOnce();
-      expect(image.getLocation()).andReturn(region).anyTimes();
-      expect(image2.getLocation()).andReturn(region).anyTimes();
-      expect(image.getProviderId()).andReturn("imageId").anyTimes();
-      expect(image2.getProviderId()).andReturn("imageId2").anyTimes();
-      expect(os.getName()).andReturn("osName");
-      expect(os2.getName()).andReturn("osName");
-      expect(os.getVersion()).andReturn("osVersion");
-      expect(os2.getVersion()).andReturn("osVersion");
-      expect(os.getDescription()).andReturn("osDescription");
-      expect(os2.getDescription()).andReturn("osDescription");
-      expect(os.getArch()).andReturn("X86_64").atLeastOnce();
-      expect(os2.getArch()).andReturn("X86_64").atLeastOnce();
-      
-      replay(image);
-      replay(image2);
-      replay(os);
-      replay(os2);
-      replay(defaultTemplate);
-      replay(optionsProvider);
-      replay(templateBuilderProvider);
+      replay(defaultTemplate, optionsProvider, templateBuilderProvider);
 
       TemplateBuilderImpl template = createTemplateBuilder(null, locations, images, hardwares, region,
                optionsProvider, templateBuilderProvider);
 
       assertEquals(template.resolveImage(hardware, images.get()), image2);
 
-      verify(image);
-      verify(image2);
-      verify(os);
-      verify(os2);
-      verify(defaultTemplate);
-      verify(optionsProvider);
-      verify(templateBuilderProvider);
+      verify(defaultTemplate, optionsProvider, templateBuilderProvider);
    }
 
    @SuppressWarnings("unchecked")
    @Test
    public void testArchWins() {
-      Image image = createMock(Image.class);
-      Image image2 = createMock(Image.class);
-      OperatingSystem os = createMock(OperatingSystem.class);
-      OperatingSystem os2 = createMock(OperatingSystem.class);
 
       Hardware hardware = new HardwareBuilder().id("hardwareId").build();
 
@@ -188,45 +144,19 @@
 
       expect(optionsProvider.get()).andReturn(new TemplateOptions());
 
-      
-      expect(image.getLocation()).andReturn(region).atLeastOnce();
-      expect(image2.getLocation()).andReturn(region).atLeastOnce();
-      expect(image.getOperatingSystem()).andReturn(os).atLeastOnce();
-      expect(image2.getOperatingSystem()).andReturn(os2).atLeastOnce();
-      expect(image.getId()).andReturn("us-east-1/1").atLeastOnce();
-      expect(image.getProviderId()).andReturn("1").anyTimes();
-      expect(image2.getProviderId()).andReturn("2").anyTimes();
-
-      expect(os.getArch()).andReturn("X86_32").atLeastOnce();
-      expect(os2.getArch()).andReturn("X86_64").atLeastOnce();
-
-      replay(image);
-      replay(image2);
-      replay(os);
-      replay(os2);
-      replay(defaultTemplate);
-      replay(optionsProvider);
-      replay(templateBuilderProvider);
+      replay(defaultTemplate, optionsProvider, templateBuilderProvider);
 
       TemplateBuilderImpl template = createTemplateBuilder(null, locations, images, hardwares, region,
                optionsProvider, templateBuilderProvider);
 
       assertEquals(template.smallest().osArchMatches("X86_32").build().getImage(), image);
 
-      verify(image);
-      verify(image2);
-      verify(os);
-      verify(os2);
-      verify(defaultTemplate);
-      verify(optionsProvider);
-      verify(templateBuilderProvider);
+      verify(defaultTemplate, optionsProvider, templateBuilderProvider);
    }
 
    @SuppressWarnings("unchecked")
    @Test
    public void testHardwareWithImageIdPredicateOnlyAcceptsImage() {
-      Image image = createMock(Image.class);
-      OperatingSystem os = createMock(OperatingSystem.class);
 
       Hardware hardware = new HardwareBuilder().id("hardwareId").supportsImage(ImagePredicates.idEquals("us-east-1/imageId"))
                .build();
@@ -242,25 +172,7 @@
       TemplateBuilder defaultTemplate = createMock(TemplateBuilder.class);
 
       expect(optionsProvider.get()).andReturn(new TemplateOptions());
-      expect(image.getId()).andReturn("us-east-1/imageId").atLeastOnce();
-      expect(image.getLocation()).andReturn(region).atLeastOnce();
-      expect(image.getName()).andReturn(null).atLeastOnce();
-      expect(image.getDescription()).andReturn(null).atLeastOnce();
-      expect(image.getVersion()).andReturn(null).atLeastOnce();
-      expect(image.getOperatingSystem()).andReturn(os).atLeastOnce();
-      expect(image.getProviderId()).andReturn("imageId").anyTimes();
-      
 
-      expect(os.getName()).andReturn(null).atLeastOnce();
-      expect(os.getVersion()).andReturn(null).atLeastOnce();
-      expect(os.getFamily()).andReturn(null).atLeastOnce();
-      expect(os.getDescription()).andReturn(null).atLeastOnce();
-      expect(os.getArch()).andReturn(null).atLeastOnce();
-      expect(os.is64Bit()).andReturn(false).atLeastOnce();
-
-
-      replay(image);
-      replay(os);
       replay(defaultTemplate);
       replay(optionsProvider);
       replay(templateBuilderProvider);
@@ -270,8 +182,6 @@
 
       template.imageId("us-east-1/imageId").build();
 
-      verify(image);
-      verify(os);
       verify(defaultTemplate);
       verify(optionsProvider);
       verify(templateBuilderProvider);
@@ -280,8 +190,6 @@
    @SuppressWarnings("unchecked")
    @Test
    public void testHardwareWithImageIdPredicateOnlyAcceptsImageWhenLocationNull() {
-      Image image = createMock(Image.class);
-      OperatingSystem os = createMock(OperatingSystem.class);
 
       Hardware hardware = new HardwareBuilder().id("hardwareId").supportsImage(ImagePredicates.idEquals("us-east-1/imageId"))
                .build();
@@ -297,37 +205,21 @@
       TemplateBuilder defaultTemplate = createMock(TemplateBuilder.class);
 
       expect(optionsProvider.get()).andReturn(new TemplateOptions());
-      expect(image.getId()).andReturn("us-east-1/imageId").atLeastOnce();
-      expect(image.getLocation()).andReturn(null).atLeastOnce();
-      expect(image.getName()).andReturn(null).atLeastOnce();
-      expect(image.getDescription()).andReturn(null).atLeastOnce();
-      expect(image.getVersion()).andReturn(null).atLeastOnce();
-      expect(image.getOperatingSystem()).andReturn(os).atLeastOnce();
-      expect(image.getProviderId()).andReturn("imageId").anyTimes();
-      
-      expect(os.getName()).andReturn(null).atLeastOnce();
-      expect(os.getVersion()).andReturn(null).atLeastOnce();
-      expect(os.getFamily()).andReturn(null).atLeastOnce();
-      expect(os.getDescription()).andReturn(null).atLeastOnce();
-      expect(os.getArch()).andReturn(null).atLeastOnce();
-      expect(os.is64Bit()).andReturn(false).atLeastOnce();
 
-      replay(image, os, defaultTemplate, optionsProvider, templateBuilderProvider);
+      replay(defaultTemplate, optionsProvider, templateBuilderProvider);
 
       TemplateBuilderImpl template = createTemplateBuilder(null, locations, images, hardwares, region,
                optionsProvider, templateBuilderProvider);
 
       template.imageId("us-east-1/imageId").build();
 
-      verify(image, os, defaultTemplate, optionsProvider, templateBuilderProvider);
+      verify(defaultTemplate, optionsProvider, templateBuilderProvider);
 
    }
 
    @SuppressWarnings("unchecked")
    @Test
    public void testHardwareWithImageIdPredicateOnlyDoesntImage() {
-      Image image = createMock(Image.class);
-      OperatingSystem os = createMock(OperatingSystem.class);
 
       Hardware hardware = new HardwareBuilder().id("hardwareId").supportsImage(ImagePredicates.idEquals("differentImageId"))
                .build();
@@ -343,43 +235,18 @@
       TemplateBuilder defaultTemplate = createMock(TemplateBuilder.class);
 
       expect(optionsProvider.get()).andReturn(new TemplateOptions());
-      expect(image.getId()).andReturn("us-east-1/imageId").atLeastOnce();
-      expect(image.getLocation()).andReturn(region).atLeastOnce();
-      expect(image.getOperatingSystem()).andReturn(os).atLeastOnce();
-      expect(image.getName()).andReturn(null).atLeastOnce();
-      expect(image.getDescription()).andReturn(null).atLeastOnce();
-      expect(image.getVersion()).andReturn(null).atLeastOnce();
-      expect(image.getProviderId()).andReturn("imageId").anyTimes();
       
-
-      expect(os.getName()).andReturn(null).atLeastOnce();
-      expect(os.getVersion()).andReturn(null).atLeastOnce();
-      expect(os.getFamily()).andReturn(null).atLeastOnce();
-      expect(os.getDescription()).andReturn(null).atLeastOnce();
-      expect(os.getArch()).andReturn(null).atLeastOnce();
-      expect(os.is64Bit()).andReturn(false).atLeastOnce();
-
-      replay(image);
-      replay(os);
-      replay(defaultTemplate);
-      replay(optionsProvider);
-      replay(templateBuilderProvider);
-
+      replay(defaultTemplate, optionsProvider, templateBuilderProvider);
+      
       TemplateBuilderImpl template = createTemplateBuilder(image, locations, images, hardwares, region,
                optionsProvider, templateBuilderProvider);
       try {
          template.imageId("us-east-1/imageId").build();
          assert false;
       } catch (NoSuchElementException e) {
-         // make sure big data is not in the exception message
-         assertEquals(
-                  e.getMessage(),
-                  "no hardware profiles support images matching params: {imageId=us-east-1/imageId, locationId=us-east-1, os64Bit=false}");
-         verify(image);
-         verify(os);
-         verify(defaultTemplate);
-         verify(optionsProvider);
-         verify(templateBuilderProvider);
+         // make sure message is succinct
+         assertEquals(e.getMessage(), "no hardware profiles support images matching params: idEquals(differentImageId)");
+         verify(defaultTemplate, optionsProvider, templateBuilderProvider);
       }
    }
 
@@ -468,80 +335,47 @@
    public void testSuppliedImageLocationWiderThanDefault() {
       TemplateOptions from = provideTemplateOptions();
 
-      Image image = createMock(Image.class);
-
-      Hardware hardware = new HardwareBuilder().id("hardwareId").supportsImage(ImagePredicates.idEquals("us-east-1/foo")).build();
+      Hardware hardware = new HardwareBuilder().id("hardwareId").supportsImage(ImagePredicates.idEquals(image.getId())).build();
 
       Supplier<Set<? extends Location>> locations = Suppliers.<Set<? extends Location>> ofInstance(ImmutableSet
-               .<Location> of(region));
+               .<Location> of(provider, region));
       Supplier<Set<? extends Image>> images = Suppliers.<Set<? extends Image>> ofInstance(ImmutableSet
                .<Image> of(image));
       Supplier<Set<? extends Hardware>> hardwares = Suppliers.<Set<? extends Hardware>> ofInstance(ImmutableSet
                .<Hardware> of(hardware));
-      Location imageLocation = createMock(Location.class);
-      OperatingSystem os = createMock(OperatingSystem.class);
 
       Provider<TemplateOptions> optionsProvider = createMock(Provider.class);
       Provider<TemplateBuilder> templateBuilderProvider = createMock(Provider.class);
       TemplateOptions defaultOptions = createMock(TemplateOptions.class);
       expect(optionsProvider.get()).andReturn(from).atLeastOnce();
 
-
-      expect(image.getId()).andReturn("us-east-1/foo").atLeastOnce();
-      expect(image.getLocation()).andReturn(region).atLeastOnce();
-      expect(image.getOperatingSystem()).andReturn(os).atLeastOnce();
-      expect(image.getName()).andReturn(null).atLeastOnce();
-      expect(image.getDescription()).andReturn(null).atLeastOnce();
-      expect(image.getVersion()).andReturn(null).atLeastOnce();
-      expect(image.getProviderId()).andReturn("foo").anyTimes();
-      
-      expect(os.getName()).andReturn(null).atLeastOnce();
-      expect(os.getVersion()).andReturn(null).atLeastOnce();
-      expect(os.getFamily()).andReturn(null).atLeastOnce();
-      expect(os.getDescription()).andReturn(null).atLeastOnce();
-      expect(os.getArch()).andReturn(null).atLeastOnce();
-      expect(os.is64Bit()).andReturn(false).atLeastOnce();
-
-
-      replay(defaultOptions);
-      replay(imageLocation);
-      replay(image);
-      replay(os);
-      replay(optionsProvider);
-      replay(templateBuilderProvider);
+      replay(defaultOptions, optionsProvider, templateBuilderProvider);
 
       TemplateBuilderImpl template = createTemplateBuilder(null, locations, images, hardwares, region,
                optionsProvider, templateBuilderProvider);
 
-      assertEquals(template.imageId("us-east-1/foo").locationId(region.getId()).build().getLocation(), region);
+      assertEquals(template.imageId(image.getId()).locationId(provider.getId()).build().getLocation(), region);
 
-      verify(defaultOptions);
-      verify(imageLocation);
-      verify(image);
-      verify(os);
-      verify(optionsProvider);
-      verify(templateBuilderProvider);
+      verify(defaultOptions, optionsProvider, templateBuilderProvider);
    }
 
    @SuppressWarnings("unchecked")
    @Test
    public void testSuppliedLocationWithNoOptions() {
       Supplier<Set<? extends Location>> locations = Suppliers.<Set<? extends Location>> ofInstance(ImmutableSet
-               .<Location> of());
+               .<Location> of(region));
       Supplier<Set<? extends Image>> images = Suppliers.<Set<? extends Image>> ofInstance(ImmutableSet.<Image> of());
       Supplier<Set<? extends Hardware>> hardwares = Suppliers.<Set<? extends Hardware>> ofInstance(ImmutableSet
                .<Hardware> of());
-      Location defaultLocation = createMock(Location.class);
       Provider<TemplateOptions> optionsProvider = createMock(Provider.class);
       Provider<TemplateBuilder> templateBuilderProvider = createMock(Provider.class);
       TemplateOptions defaultOptions = createMock(TemplateOptions.class);
 
       replay(defaultOptions);
-      replay(defaultLocation);
       replay(optionsProvider);
       replay(templateBuilderProvider);
 
-      TemplateBuilderImpl template = createTemplateBuilder(null, locations, images, hardwares, defaultLocation,
+      TemplateBuilderImpl template = createTemplateBuilder(null, locations, images, hardwares, region,
                optionsProvider, templateBuilderProvider);
 
       try {
@@ -552,7 +386,6 @@
       }
 
       verify(defaultOptions);
-      verify(defaultLocation);
       verify(optionsProvider);
       verify(templateBuilderProvider);
    }
@@ -563,21 +396,19 @@
       TemplateOptions from = provideTemplateOptions();
 
       Supplier<Set<? extends Location>> locations = Suppliers.<Set<? extends Location>> ofInstance(ImmutableSet
-               .<Location> of());
+               .<Location> of(region));
       Supplier<Set<? extends Image>> images = Suppliers.<Set<? extends Image>> ofInstance(ImmutableSet.<Image> of());
       Supplier<Set<? extends Hardware>> hardwares = Suppliers.<Set<? extends Hardware>> ofInstance(ImmutableSet
                .<Hardware> of());
-      Location defaultLocation = createMock(Location.class);
       Provider<TemplateOptions> optionsProvider = createMock(Provider.class);
       Provider<TemplateBuilder> templateBuilderProvider = createMock(Provider.class);
 
       expect(optionsProvider.get()).andReturn(from).atLeastOnce();
 
-      replay(defaultLocation);
       replay(optionsProvider);
       replay(templateBuilderProvider);
 
-      TemplateBuilderImpl template = createTemplateBuilder(null, locations, images, hardwares, defaultLocation,
+      TemplateBuilderImpl template = createTemplateBuilder(null, locations, images, hardwares, region,
                optionsProvider, templateBuilderProvider);
 
       try {
@@ -587,83 +418,124 @@
 
       }
 
-      verify(defaultLocation);
       verify(optionsProvider);
       verify(templateBuilderProvider);
    }
 
    @SuppressWarnings("unchecked")
+   public void testImagesMustBePresentWhenQuerying() {
+      Supplier<Set<? extends Location>> locations = Suppliers.<Set<? extends Location>> ofInstance(ImmutableSet
+               .<Location> of(region));
+      Supplier<Set<? extends Image>> images = Suppliers.<Set<? extends Image>> ofInstance(ImmutableSet.<Image> of());
+      Supplier<Set<? extends Hardware>> hardwares = Suppliers.<Set<? extends Hardware>> ofInstance(ImmutableSet
+               .<Hardware> of(createMock(Hardware.class)));
+      Provider<TemplateOptions> optionsProvider = createMock(Provider.class);
+      Provider<TemplateBuilder> templateBuilderProvider = createMock(Provider.class);
+      TemplateOptions defaultOptions = createMock(TemplateOptions.class);
+
+      expect(optionsProvider.get()).andReturn(defaultOptions);
+
+      replay(defaultOptions, optionsProvider, templateBuilderProvider);
+
+      TemplateBuilderImpl template = createTemplateBuilder(null, locations, images, hardwares, region,
+               optionsProvider, templateBuilderProvider);
+
+      try {
+         template.os64Bit(true).build();
+         assert false;
+      } catch (IllegalStateException e) {
+         assertEquals(e.getMessage(), "no images present!");
+      }
+
+      verify(defaultOptions, optionsProvider, templateBuilderProvider);
+   }
+
+   @SuppressWarnings("unchecked")
+   public void testHardwareProfilesMustBePresentWhenQuerying() {
+      Supplier<Set<? extends Location>> locations = Suppliers.<Set<? extends Location>> ofInstance(ImmutableSet
+               .<Location> of(region));
+      Supplier<Set<? extends Image>> images = Suppliers.<Set<? extends Image>> ofInstance(ImmutableSet.<Image> of(image));
+      Supplier<Set<? extends Hardware>> hardwares = Suppliers.<Set<? extends Hardware>> ofInstance(ImmutableSet
+               .<Hardware> of());
+      Provider<TemplateOptions> optionsProvider = createMock(Provider.class);
+      Provider<TemplateBuilder> templateBuilderProvider = createMock(Provider.class);
+      TemplateOptions defaultOptions = createMock(TemplateOptions.class);
+
+      expect(optionsProvider.get()).andReturn(defaultOptions);
+
+      replay(defaultOptions, optionsProvider, templateBuilderProvider);
+
+      TemplateBuilderImpl template = createTemplateBuilder(null, locations, images, hardwares, region,
+               optionsProvider, templateBuilderProvider);
+
+      try {
+         template.os64Bit(true).build();
+         assert false;
+      } catch (IllegalStateException e) {
+         assertEquals(e.getMessage(), "no hardware profiles present!");
+      }
+
+      verify(defaultOptions, optionsProvider, templateBuilderProvider);
+   }
+
+   @SuppressWarnings("unchecked")
    @Test
    public void testDefaultLocationWithNoOptionsNoSuchElement() {
       Supplier<Set<? extends Location>> locations = Suppliers.<Set<? extends Location>> ofInstance(ImmutableSet
-               .<Location> of());
-      Supplier<Set<? extends Image>> images = Suppliers.<Set<? extends Image>> ofInstance(ImmutableSet.<Image> of());
+               .<Location> of(region));
+      Supplier<Set<? extends Image>> images = Suppliers.<Set<? extends Image>> ofInstance(ImmutableSet.<Image> of(image));
       Supplier<Set<? extends Hardware>> hardwares = Suppliers.<Set<? extends Hardware>> ofInstance(ImmutableSet
-               .<Hardware> of());
-      Location defaultLocation = createMock(Location.class);
+               .<Hardware> of(createMock(Hardware.class)));
       Provider<TemplateOptions> optionsProvider = createMock(Provider.class);
       Provider<TemplateBuilder> templateBuilderProvider = createMock(Provider.class);
       TemplateOptions defaultOptions = createMock(TemplateOptions.class);
 
       expect(optionsProvider.get()).andReturn(defaultOptions);
 
-      replay(defaultOptions);
-      replay(defaultLocation);
-      replay(optionsProvider);
-      replay(templateBuilderProvider);
+      replay(defaultOptions, optionsProvider, templateBuilderProvider);
 
-      TemplateBuilderImpl template = createTemplateBuilder(null, locations, images, hardwares, defaultLocation,
+      TemplateBuilderImpl template = createTemplateBuilder(null, locations, images, hardwares, region,
                optionsProvider, templateBuilderProvider);
 
       try {
-         template.imageId("region/imageId").build();
+         template.imageId("region/imageId2").build();
          assert false;
       } catch (NoSuchElementException e) {
          // make sure big data is not in the exception message
-         assertEquals(e.getMessage(), "imageId(region/imageId) not found");
+         assertEquals(e.getMessage(), "imageId(region/imageId2) not found");
       }
 
-      verify(defaultOptions);
-      verify(defaultLocation);
-      verify(optionsProvider);
-      verify(templateBuilderProvider);
+      verify(defaultOptions, optionsProvider, templateBuilderProvider);
    }
 
    @SuppressWarnings("unchecked")
    @Test
    public void testDefaultLocationWithUnmatchedPredicateExceptionMessageAndLocationNotCalled() {
       Supplier<Set<? extends Location>> locations = Suppliers.<Set<? extends Location>> ofInstance(ImmutableSet
-               .<Location> of());
-      Supplier<Set<? extends Image>> images = Suppliers.<Set<? extends Image>> ofInstance(ImmutableSet.<Image> of());
+               .<Location> of(region));
+      Supplier<Set<? extends Image>> images = Suppliers.<Set<? extends Image>> ofInstance(ImmutableSet.<Image> of(image));
       Supplier<Set<? extends Hardware>> hardwares = Suppliers.<Set<? extends Hardware>> ofInstance(ImmutableSet
-               .<Hardware> of());
-      Location defaultLocation = createMock(Location.class);
+               .<Hardware> of(createMock(Hardware.class)));
       Provider<TemplateOptions> optionsProvider = createMock(Provider.class);
       Provider<TemplateBuilder> templateBuilderProvider = createMock(Provider.class);
       TemplateOptions defaultOptions = createMock(TemplateOptions.class);
 
       expect(optionsProvider.get()).andReturn(defaultOptions);
 
-      replay(defaultOptions);
-      replay(defaultLocation);
-      replay(optionsProvider);
-      replay(templateBuilderProvider);
+      replay(defaultOptions, optionsProvider, templateBuilderProvider);
 
-      TemplateBuilderImpl template = createTemplateBuilder(null, locations, images, hardwares, defaultLocation,
+      TemplateBuilderImpl template = createTemplateBuilder(null, locations, images, hardwares, region,
                optionsProvider, templateBuilderProvider);
 
       try {
-         template.imageDescriptionMatches("description").build();
+         template.imageDescriptionMatches("notDescription").build();
          assert false;
       } catch (NoSuchElementException e) {
          // make sure big data is not in the exception message
-         assertEquals(e.getMessage(), "no image matched predicate: And(nullEqualToIsParentOrIsGrandparentOfCurrentLocation(),imageDescription(description))");
+         assertEquals(e.getMessage(), "no image matched predicate: And(nullEqualToIsParentOrIsGrandparentOfCurrentLocation(),imageDescription(notDescription))");
       }
 
-      verify(defaultOptions);
-      verify(defaultLocation);
-      verify(optionsProvider);
-      verify(templateBuilderProvider);
+      verify(defaultOptions, optionsProvider, templateBuilderProvider);
    }
 
    protected TemplateOptions provideTemplateOptions() {
@@ -675,9 +547,9 @@
    public void testDefaultLocationWithOptions() {
       Supplier<Set<? extends Location>> locations = Suppliers.<Set<? extends Location>> ofInstance(ImmutableSet
                .<Location> of());
-      Supplier<Set<? extends Image>> images = Suppliers.<Set<? extends Image>> ofInstance(ImmutableSet.<Image> of());
+      Supplier<Set<? extends Image>> images = Suppliers.<Set<? extends Image>> ofInstance(ImmutableSet.<Image> of(image));
       Supplier<Set<? extends Hardware>> hardwares = Suppliers.<Set<? extends Hardware>> ofInstance(ImmutableSet
-               .<Hardware> of());
+               .<Hardware> of(createMock(Hardware.class)));
       Location defaultLocation = createMock(Location.class);
       Provider<TemplateOptions> optionsProvider = createMock(Provider.class);
       TemplateOptions from = provideTemplateOptions();
@@ -685,11 +557,7 @@
 
       expect(optionsProvider.get()).andReturn(from);
 
-      expect(from.getInboundPorts()).andReturn(new int[] { 22 });
-
-      replay(defaultLocation);
-      replay(optionsProvider);
-      replay(templateBuilderProvider);
+      replay(defaultLocation, optionsProvider, templateBuilderProvider);
 
       TemplateBuilderImpl template = createTemplateBuilder(null, locations, images, hardwares, defaultLocation,
                optionsProvider, templateBuilderProvider);
@@ -701,9 +569,7 @@
 
       }
 
-      verify(defaultLocation);
-      // verify(optionsProvider);
-      verify(templateBuilderProvider);
+      verify(defaultLocation, optionsProvider, templateBuilderProvider);
    }
 
    @SuppressWarnings("unchecked")
diff --git a/compute/src/test/java/org/jclouds/compute/strategy/CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapTest.java b/compute/src/test/java/org/jclouds/compute/strategy/CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapTest.java
index bd8426c..0634637 100644
--- a/compute/src/test/java/org/jclouds/compute/strategy/CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapTest.java
+++ b/compute/src/test/java/org/jclouds/compute/strategy/CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapTest.java
@@ -31,10 +31,13 @@
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicReference;
 
+import org.easymock.EasyMock;
+import org.easymock.IAnswer;
+import org.jclouds.compute.config.ComputeServiceTimeoutsModule;
 import org.jclouds.compute.config.CustomizationResponse;
 import org.jclouds.compute.domain.NodeMetadata;
-import org.jclouds.compute.domain.NodeMetadataBuilder;
 import org.jclouds.compute.domain.NodeMetadata.Status;
+import org.jclouds.compute.domain.NodeMetadataBuilder;
 import org.jclouds.compute.functions.TemplateOptionsToStatement;
 import org.jclouds.compute.options.TemplateOptions;
 import org.jclouds.compute.predicates.AtomicNodeRunning;
@@ -45,7 +48,9 @@
 import org.testng.annotations.Test;
 
 import com.google.common.base.Function;
+import com.google.common.base.Predicate;
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
 import com.google.common.collect.LinkedHashMultimap;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Multimap;
@@ -190,4 +195,58 @@
       // verify mocks
       verify(initScriptRunnerFactory, openSocketFinder);
    }
+   
+   public void testRecoversWhenTemporarilyNodeNotFound() {
+      String nodeId = "myid";
+
+      InitializeRunScriptOnNodeOrPlaceInBadMap.Factory initScriptRunnerFactory = createMock(InitializeRunScriptOnNodeOrPlaceInBadMap.Factory.class);
+      OpenSocketFinder openSocketFinder = createMock(OpenSocketFinder.class);
+      Timeouts timeouts = new Timeouts();
+      Function<TemplateOptions, Statement> templateOptionsToStatement = new TemplateOptionsToStatement();
+      Set<NodeMetadata> goodNodes = Sets.newLinkedHashSet();
+      Map<NodeMetadata, Exception> badNodes = Maps.newLinkedHashMap();
+      Multimap<NodeMetadata, CustomizationResponse> customizationResponses = LinkedHashMultimap.create();
+      TemplateOptions options = new TemplateOptions();
+      
+      final NodeMetadata pendingNode = new NodeMetadataBuilder().ids(nodeId).status(Status.PENDING).build();
+      final NodeMetadata runningNode = new NodeMetadataBuilder().ids(nodeId).status(Status.RUNNING).build();
+      GetNodeMetadataStrategy nodeClient = createMock(GetNodeMetadataStrategy.class);
+      AtomicNodeRunning nodeRunning = new AtomicNodeRunning(nodeClient);
+      Predicate<AtomicReference<NodeMetadata>> retryableNodeRunning = new ComputeServiceTimeoutsModule() {
+               public Predicate<AtomicReference<NodeMetadata>> nodeRunning(AtomicNodeRunning statusRunning, Timeouts timeouts) {
+                  return super.nodeRunning(statusRunning, timeouts);
+               }
+            }.nodeRunning(nodeRunning, timeouts);
+      AtomicReference<NodeMetadata> atomicNode = new AtomicReference<NodeMetadata>(pendingNode);
+      
+      // Simulate transient error: first call returns null; subsequent calls return the running node
+      EasyMock.expect(nodeClient.getNode(nodeId)).andAnswer(new IAnswer<NodeMetadata>() {
+               private int count = 0;
+               @Override
+               public NodeMetadata answer() throws Throwable {
+                  count++;
+                  if (count <= 1) {
+                     return null;
+                  } else {
+                     return runningNode;
+                  }
+               }
+            }).anyTimes();
+
+      // replay mocks
+      replay(initScriptRunnerFactory, openSocketFinder, nodeClient);
+      
+      // run
+      new CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap(retryableNodeRunning, openSocketFinder, timeouts,
+               templateOptionsToStatement, initScriptRunnerFactory, options, atomicNode, goodNodes, badNodes,
+               customizationResponses).apply(atomicNode);
+
+      if (badNodes.size() > 0) Iterables.get(badNodes.values(), 0).printStackTrace();
+      assertEquals(badNodes.size(), 0);
+      assertEquals(goodNodes, ImmutableSet.of(runningNode));
+      assertEquals(customizationResponses.size(), 0);
+
+      // verify mocks
+      verify(initScriptRunnerFactory, openSocketFinder, nodeClient);
+   }
 }
diff --git a/compute/src/test/resources/initscript_with_java.sh b/compute/src/test/resources/initscript_with_java.sh
index 5a0dd5b..8736539 100644
--- a/compute/src/test/resources/initscript_with_java.sh
+++ b/compute/src/test/resources/initscript_with_java.sh
@@ -83,8 +83,9 @@
    echo "aborting: $@" 1>&2
    exit 1
 }
-alias apt-get-install="apt-get install -f -y -qq --force-yes"
 alias apt-get-update="apt-get update -qq"
+alias apt-get-install="apt-get install -f -y -qq --force-yes"
+alias yum-install="yum --quiet --nogpgcheck -y install"
 
 function ensure_cmd_or_install_package_apt(){
   local cmd=$1
@@ -96,7 +97,7 @@
 function ensure_cmd_or_install_package_yum(){
   local cmd=$1
   local pkg=$2
-  hash $cmd 2>/dev/null || yum --nogpgcheck -y install $pkg
+  hash $cmd 2>/dev/null || yum-install $pkg
 }
 
 function ensure_netutils_apt() {
@@ -133,39 +134,56 @@
   ensure_can_resolve_public_dns
   return 0  
 }
+# adds JAVA_HOME and into PATH in global and user-specific profiles
 function setupJavaHomeInProfile() {
-  test -n \"$SUDO_USER\" && cat >> `getent passwd $SUDO_USER| cut -f6 -d:`/.bashrc <<-'END_OF_JCLOUDS_FILE'
+  PROFILES=(/etc/bashrc $HOME/.bashrc /etc/skel/.bashrc)
+  test -n "$SUDO_USER" &&
+    PROFILES=(${PROFILES[*]} `getent passwd $SUDO_USER| cut -f6 -d:`/.bashrc)
+  for PROFILE in ${PROFILES[*]}; do
+    cat >> $PROFILE <<-'END_OF_JCLOUDS_FILE'
 	export JAVA_HOME=/usr/local/jdk
 	export PATH=$JAVA_HOME/bin:$PATH
 END_OF_JCLOUDS_FILE
-  cat >> /etc/bashrc <<-'END_OF_JCLOUDS_FILE'
-	export JAVA_HOME=/usr/local/jdk
-	export PATH=$JAVA_HOME/bin:$PATH
-END_OF_JCLOUDS_FILE
-  cat >> $HOME/.bashrc <<-'END_OF_JCLOUDS_FILE'
-	export JAVA_HOME=/usr/local/jdk
-	export PATH=$JAVA_HOME/bin:$PATH
-END_OF_JCLOUDS_FILE
-  cat >> /etc/skel/.bashrc <<-'END_OF_JCLOUDS_FILE'
-	export JAVA_HOME=/usr/local/jdk
-	export PATH=$JAVA_HOME/bin:$PATH
-END_OF_JCLOUDS_FILE
+  done
 }
 
+# resets JAVA_HOME to what an openjdk installer created
+function findOpenJDK() {
+  local oldJavaHome=$JAVA_HOME
+  unset JAVA_HOME
+  for CANDIDATE in $oldJavaHome `ls -d /usr/lib/jvm/java-1.6.0-openjdk-* /usr/lib/jvm/java-6-openjdk-* /usr/lib/jvm/java-6-openjdk 2>&-`; do
+    if [ -n "$CANDIDATE" -a -x "$CANDIDATE/bin/java" ]; then
+      export JAVA_HOME=$CANDIDATE
+      break
+    fi
+  done
+}
+
+# assures JDK installed and JAVA_HOME to a link at /usr/local/jdk
 function installOpenJDK() {
-  if hash apt-get 2>/dev/null; then
-    export JAVA_HOME=${JAVA_HOME:-/usr/lib/jvm/java-6-openjdk}
-    test -d $JAVA_HOME || ( apt-get-update && apt-get-install openjdk-6-jdk )
-  elif hash yum 2>/dev/null; then
-    export pkg=java-1.6.0-openjdk-devel
-    yum --nogpgcheck -y install $pkg &&
-    export JAVA_HOME=`ls -d /usr/lib/jvm/java-1.6.0-openjdk-*`
-  else
-    abort "we only support apt-get and yum right now... please contribute!"
-    return 1
+  if [ "$JAVA_HOME" == "/usr/local/jdk" ]; then
+    echo skipping as JAVA_HOME is already set to /usr/local/jdk
+    return 0
   fi
-  test -n "$JAVA_HOME" || abort "JDK installation failed!"
-  ln -Fs $JAVA_HOME /usr/local/jdk 
+  if [ -n "$JAVA_HOME" -a -x "$JAVA_HOME/bin/java" ]; then
+    echo reusing JAVA_HOME $JAVA_HOME
+  else
+    if hash apt-get 2>/dev/null; then
+      apt-get-update && apt-get-install openjdk-6-jdk
+    elif hash yum 2>/dev/null; then
+      yum-install java-1.6.0-openjdk-devel
+    else
+      abort "we only support apt-get and yum right now... please contribute"
+    fi
+    findOpenJDK
+    if [ -n "$JAVA_HOME" ]; then
+      echo installed JAVA_HOME $JAVA_HOME
+    else
+      abort "JDK installation failed"
+    fi
+  fi
+  rm -rf /usr/local/jdk
+  ln -Fs $JAVA_HOME /usr/local/jdk
   /usr/local/jdk/bin/java -version || abort "cannot run java"
   setupJavaHomeInProfile
 }
@@ -194,7 +212,7 @@
 	exec 3<> /etc/ssh/sshd_config && awk -v TEXT="PasswordAuthentication no
 	PermitRootLogin no
 	" 'BEGIN {print TEXT}{print}' /etc/ssh/sshd_config >&3
-	hash service 2>/dev/null && service ssh reload || /etc/init.d/ssh* reload
+	hash service 2>&- && service ssh reload 2>&- || /etc/init.d/ssh* reload
 	awk -v user=^${SUDO_USER:=${USER}}: -v password='crypt(randompassword)' 'BEGIN { FS=OFS=":" } $0 ~ user { $2 = password } 1' /etc/shadow >/etc/shadow.${SUDO_USER:=${USER}}
 	test -f /etc/shadow.${SUDO_USER:=${USER}} && mv /etc/shadow.${SUDO_USER:=${USER}} /etc/shadow
 	setupPublicCurl || return 1
diff --git a/compute/src/test/resources/initscript_with_jboss.sh b/compute/src/test/resources/initscript_with_jboss.sh
index 4f38530..cf88c75 100644
--- a/compute/src/test/resources/initscript_with_jboss.sh
+++ b/compute/src/test/resources/initscript_with_jboss.sh
@@ -83,8 +83,9 @@
    echo "aborting: $@" 1>&2
    exit 1
 }
-alias apt-get-install="apt-get install -f -y -qq --force-yes"
 alias apt-get-update="apt-get update -qq"
+alias apt-get-install="apt-get install -f -y -qq --force-yes"
+alias yum-install="yum --quiet --nogpgcheck -y install"
 
 function ensure_cmd_or_install_package_apt(){
   local cmd=$1
@@ -96,7 +97,7 @@
 function ensure_cmd_or_install_package_yum(){
   local cmd=$1
   local pkg=$2
-  hash $cmd 2>/dev/null || yum --nogpgcheck -y install $pkg
+  hash $cmd 2>/dev/null || yum-install $pkg
 }
 
 function ensure_netutils_apt() {
@@ -133,39 +134,56 @@
   ensure_can_resolve_public_dns
   return 0  
 }
+# adds JAVA_HOME and into PATH in global and user-specific profiles
 function setupJavaHomeInProfile() {
-  test -n \"$SUDO_USER\" && cat >> `getent passwd $SUDO_USER| cut -f6 -d:`/.bashrc <<-'END_OF_JCLOUDS_FILE'
+  PROFILES=(/etc/bashrc $HOME/.bashrc /etc/skel/.bashrc)
+  test -n "$SUDO_USER" &&
+    PROFILES=(${PROFILES[*]} `getent passwd $SUDO_USER| cut -f6 -d:`/.bashrc)
+  for PROFILE in ${PROFILES[*]}; do
+    cat >> $PROFILE <<-'END_OF_JCLOUDS_FILE'
 	export JAVA_HOME=/usr/local/jdk
 	export PATH=$JAVA_HOME/bin:$PATH
 END_OF_JCLOUDS_FILE
-  cat >> /etc/bashrc <<-'END_OF_JCLOUDS_FILE'
-	export JAVA_HOME=/usr/local/jdk
-	export PATH=$JAVA_HOME/bin:$PATH
-END_OF_JCLOUDS_FILE
-  cat >> $HOME/.bashrc <<-'END_OF_JCLOUDS_FILE'
-	export JAVA_HOME=/usr/local/jdk
-	export PATH=$JAVA_HOME/bin:$PATH
-END_OF_JCLOUDS_FILE
-  cat >> /etc/skel/.bashrc <<-'END_OF_JCLOUDS_FILE'
-	export JAVA_HOME=/usr/local/jdk
-	export PATH=$JAVA_HOME/bin:$PATH
-END_OF_JCLOUDS_FILE
+  done
 }
 
+# resets JAVA_HOME to what an openjdk installer created
+function findOpenJDK() {
+  local oldJavaHome=$JAVA_HOME
+  unset JAVA_HOME
+  for CANDIDATE in $oldJavaHome `ls -d /usr/lib/jvm/java-1.6.0-openjdk-* /usr/lib/jvm/java-6-openjdk-* /usr/lib/jvm/java-6-openjdk 2>&-`; do
+    if [ -n "$CANDIDATE" -a -x "$CANDIDATE/bin/java" ]; then
+      export JAVA_HOME=$CANDIDATE
+      break
+    fi
+  done
+}
+
+# assures JDK installed and JAVA_HOME to a link at /usr/local/jdk
 function installOpenJDK() {
-  if hash apt-get 2>/dev/null; then
-    export JAVA_HOME=${JAVA_HOME:-/usr/lib/jvm/java-6-openjdk}
-    test -d $JAVA_HOME || ( apt-get-update && apt-get-install openjdk-6-jdk )
-  elif hash yum 2>/dev/null; then
-    export pkg=java-1.6.0-openjdk-devel
-    yum --nogpgcheck -y install $pkg &&
-    export JAVA_HOME=`ls -d /usr/lib/jvm/java-1.6.0-openjdk-*`
-  else
-    abort "we only support apt-get and yum right now... please contribute!"
-    return 1
+  if [ "$JAVA_HOME" == "/usr/local/jdk" ]; then
+    echo skipping as JAVA_HOME is already set to /usr/local/jdk
+    return 0
   fi
-  test -n "$JAVA_HOME" || abort "JDK installation failed!"
-  ln -Fs $JAVA_HOME /usr/local/jdk 
+  if [ -n "$JAVA_HOME" -a -x "$JAVA_HOME/bin/java" ]; then
+    echo reusing JAVA_HOME $JAVA_HOME
+  else
+    if hash apt-get 2>/dev/null; then
+      apt-get-update && apt-get-install openjdk-6-jdk
+    elif hash yum 2>/dev/null; then
+      yum-install java-1.6.0-openjdk-devel
+    else
+      abort "we only support apt-get and yum right now... please contribute"
+    fi
+    findOpenJDK
+    if [ -n "$JAVA_HOME" ]; then
+      echo installed JAVA_HOME $JAVA_HOME
+    else
+      abort "JDK installation failed"
+    fi
+  fi
+  rm -rf /usr/local/jdk
+  ln -Fs $JAVA_HOME /usr/local/jdk
   /usr/local/jdk/bin/java -version || abort "cannot run java"
   setupJavaHomeInProfile
 }
@@ -194,7 +212,7 @@
 	exec 3<> /etc/ssh/sshd_config && awk -v TEXT="PasswordAuthentication no
 	PermitRootLogin no
 	" 'BEGIN {print TEXT}{print}' /etc/ssh/sshd_config >&3
-	hash service 2>/dev/null && service ssh reload || /etc/init.d/ssh* reload
+	hash service 2>&- && service ssh reload 2>&- || /etc/init.d/ssh* reload
 	awk -v user=^${SUDO_USER:=${USER}}: -v password='crypt(randompassword)' 'BEGIN { FS=OFS=":" } $0 ~ user { $2 = password } 1' /etc/shadow >/etc/shadow.${SUDO_USER:=${USER}}
 	test -f /etc/shadow.${SUDO_USER:=${USER}} && mv /etc/shadow.${SUDO_USER:=${USER}} /etc/shadow
 	setupPublicCurl || return 1
diff --git a/compute/src/test/resources/runscript.sh b/compute/src/test/resources/runscript.sh
index b53c201..646964a 100644
--- a/compute/src/test/resources/runscript.sh
+++ b/compute/src/test/resources/runscript.sh
@@ -83,8 +83,9 @@
    echo "aborting: $@" 1>&2
    exit 1
 }
-alias apt-get-install="apt-get install -f -y -qq --force-yes"
 alias apt-get-update="apt-get update -qq"
+alias apt-get-install="apt-get install -f -y -qq --force-yes"
+alias yum-install="yum --quiet --nogpgcheck -y install"
 
 function ensure_cmd_or_install_package_apt(){
   local cmd=$1
@@ -96,7 +97,7 @@
 function ensure_cmd_or_install_package_yum(){
   local cmd=$1
   local pkg=$2
-  hash $cmd 2>/dev/null || yum --nogpgcheck -y install $pkg
+  hash $cmd 2>/dev/null || yum-install $pkg
 }
 
 function ensure_netutils_apt() {
@@ -133,39 +134,56 @@
   ensure_can_resolve_public_dns
   return 0  
 }
+# adds JAVA_HOME and into PATH in global and user-specific profiles
 function setupJavaHomeInProfile() {
-  test -n \"$SUDO_USER\" && cat >> `getent passwd $SUDO_USER| cut -f6 -d:`/.bashrc <<-'END_OF_JCLOUDS_FILE'
+  PROFILES=(/etc/bashrc $HOME/.bashrc /etc/skel/.bashrc)
+  test -n "$SUDO_USER" &&
+    PROFILES=(${PROFILES[*]} `getent passwd $SUDO_USER| cut -f6 -d:`/.bashrc)
+  for PROFILE in ${PROFILES[*]}; do
+    cat >> $PROFILE <<-'END_OF_JCLOUDS_FILE'
 	export JAVA_HOME=/usr/local/jdk
 	export PATH=$JAVA_HOME/bin:$PATH
 END_OF_JCLOUDS_FILE
-  cat >> /etc/bashrc <<-'END_OF_JCLOUDS_FILE'
-	export JAVA_HOME=/usr/local/jdk
-	export PATH=$JAVA_HOME/bin:$PATH
-END_OF_JCLOUDS_FILE
-  cat >> $HOME/.bashrc <<-'END_OF_JCLOUDS_FILE'
-	export JAVA_HOME=/usr/local/jdk
-	export PATH=$JAVA_HOME/bin:$PATH
-END_OF_JCLOUDS_FILE
-  cat >> /etc/skel/.bashrc <<-'END_OF_JCLOUDS_FILE'
-	export JAVA_HOME=/usr/local/jdk
-	export PATH=$JAVA_HOME/bin:$PATH
-END_OF_JCLOUDS_FILE
+  done
 }
 
+# resets JAVA_HOME to what an openjdk installer created
+function findOpenJDK() {
+  local oldJavaHome=$JAVA_HOME
+  unset JAVA_HOME
+  for CANDIDATE in $oldJavaHome `ls -d /usr/lib/jvm/java-1.6.0-openjdk-* /usr/lib/jvm/java-6-openjdk-* /usr/lib/jvm/java-6-openjdk 2>&-`; do
+    if [ -n "$CANDIDATE" -a -x "$CANDIDATE/bin/java" ]; then
+      export JAVA_HOME=$CANDIDATE
+      break
+    fi
+  done
+}
+
+# assures JDK installed and JAVA_HOME to a link at /usr/local/jdk
 function installOpenJDK() {
-  if hash apt-get 2>/dev/null; then
-    export JAVA_HOME=${JAVA_HOME:-/usr/lib/jvm/java-6-openjdk}
-    test -d $JAVA_HOME || ( apt-get-update && apt-get-install openjdk-6-jdk )
-  elif hash yum 2>/dev/null; then
-    export pkg=java-1.6.0-openjdk-devel
-    yum --nogpgcheck -y install $pkg &&
-    export JAVA_HOME=`ls -d /usr/lib/jvm/java-1.6.0-openjdk-*`
-  else
-    abort "we only support apt-get and yum right now... please contribute!"
-    return 1
+  if [ "$JAVA_HOME" == "/usr/local/jdk" ]; then
+    echo skipping as JAVA_HOME is already set to /usr/local/jdk
+    return 0
   fi
-  test -n "$JAVA_HOME" || abort "JDK installation failed!"
-  ln -Fs $JAVA_HOME /usr/local/jdk 
+  if [ -n "$JAVA_HOME" -a -x "$JAVA_HOME/bin/java" ]; then
+    echo reusing JAVA_HOME $JAVA_HOME
+  else
+    if hash apt-get 2>/dev/null; then
+      apt-get-update && apt-get-install openjdk-6-jdk
+    elif hash yum 2>/dev/null; then
+      yum-install java-1.6.0-openjdk-devel
+    else
+      abort "we only support apt-get and yum right now... please contribute"
+    fi
+    findOpenJDK
+    if [ -n "$JAVA_HOME" ]; then
+      echo installed JAVA_HOME $JAVA_HOME
+    else
+      abort "JDK installation failed"
+    fi
+  fi
+  rm -rf /usr/local/jdk
+  ln -Fs $JAVA_HOME /usr/local/jdk
   /usr/local/jdk/bin/java -version || abort "cannot run java"
   setupJavaHomeInProfile
 }
diff --git a/compute/src/test/resources/runscript_adminUpdate.sh b/compute/src/test/resources/runscript_adminUpdate.sh
index 5e0c289..f239e40 100644
--- a/compute/src/test/resources/runscript_adminUpdate.sh
+++ b/compute/src/test/resources/runscript_adminUpdate.sh
@@ -101,7 +101,7 @@
 	exec 3<> /etc/ssh/sshd_config && awk -v TEXT="PasswordAuthentication no
 	PermitRootLogin no
 	" 'BEGIN {print TEXT}{print}' /etc/ssh/sshd_config >&3
-	hash service 2>/dev/null && service ssh reload || /etc/init.d/ssh* reload
+	hash service 2>&- && service ssh reload 2>&- || /etc/init.d/ssh* reload
 	awk -v user=^${SUDO_USER:=${USER}}: -v password='crypt(randompassword)' 'BEGIN { FS=OFS=":" } $0 ~ user { $2 = password } 1' /etc/shadow >/etc/shadow.${SUDO_USER:=${USER}}
 	test -f /etc/shadow.${SUDO_USER:=${USER}} && mv /etc/shadow.${SUDO_USER:=${USER}} /etc/shadow
 	
diff --git a/core/src/main/java/org/jclouds/collect/PaginatedSet.java b/core/src/main/java/org/jclouds/collect/PaginatedSet.java
new file mode 100644
index 0000000..fd23d63
--- /dev/null
+++ b/core/src/main/java/org/jclouds/collect/PaginatedSet.java
@@ -0,0 +1,103 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.collect;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.Set;
+
+import org.jclouds.javax.annotation.Nullable;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Objects;
+import com.google.common.collect.ForwardingSet;
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * An {@code Set} that can be continued
+ * 
+ * @author Adrian Cole
+ */
+@Beta
+public class PaginatedSet<T> extends ForwardingSet<T> {
+
+   public static <T> PaginatedSet<T> copyOf(Iterable<T> contents) {
+      return new PaginatedSet<T>(contents, null);
+   }
+
+   public static <T> PaginatedSet<T> copyOfWithMarker(Iterable<T> contents, String marker) {
+      return new PaginatedSet<T>(contents, marker);
+   }
+
+   private final Set<T> contents;
+   private final String marker;
+
+   protected PaginatedSet(Iterable<T> contents, @Nullable String marker) {
+      this.contents = ImmutableSet.<T> copyOf(checkNotNull(contents, "contents"));
+      this.marker = marker;
+   }
+
+   /**
+    * If there is a next marker, then the set is incomplete and you should issue another command to
+    * retrieve the rest, setting the option {@code marker} to this value
+    * 
+    * @return next marker, or null if list is complete
+    */
+   @Nullable
+   public String getNextMarker() {
+      return marker;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public int hashCode() {
+      return Objects.hashCode(contents, marker);
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public boolean equals(Object obj) {
+      if (this == obj)
+         return true;
+      if (obj == null)
+         return false;
+      if (getClass() != obj.getClass())
+         return false;
+      PaginatedSet<?> other = PaginatedSet.class.cast(obj);
+      return Objects.equal(this.contents, other.contents) && Objects.equal(this.marker, other.marker);
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public String toString() {
+      return Objects.toStringHelper(this).add("contents", contents).add("marker", marker).toString();
+   }
+
+   @Override
+   protected Set<T> delegate() {
+      return contents;
+   }
+
+}
diff --git a/core/src/main/java/org/jclouds/collect/PaginatedSets.java b/core/src/main/java/org/jclouds/collect/PaginatedSets.java
new file mode 100644
index 0000000..293b1a8
--- /dev/null
+++ b/core/src/main/java/org/jclouds/collect/PaginatedSets.java
@@ -0,0 +1,86 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.collect;
+
+import java.util.Iterator;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Function;
+import com.google.common.collect.AbstractIterator;
+
+/**
+ * Utilities for using {@link PaginatedSet}s.
+ * 
+ * @author Adrian Cole, Jeremy Whitlock
+ */
+@Beta
+public class PaginatedSets {
+
+   /**
+    * 
+    * @param initial
+    *           the initial set of data
+    * @param markerToNext
+    *           produces the next set based on the marker
+    * 
+    * @return iterable of users fitting the criteria
+    */
+   public static <T> Iterable<T> lazyContinue(final PaginatedSet<T> initial,
+            final Function<String, PaginatedSet<T>> markerToNext) {
+      if (initial.getNextMarker() == null)
+         return initial;
+      return new Iterable<T>() {
+         @Override
+         public Iterator<T> iterator() {
+            return new AbstractIterator<T>() {
+
+               private PaginatedSet<T> response = initial;
+               private Iterator<T> iterator = response.iterator();
+
+               /**
+                * {@inheritDoc}
+                */
+               @Override
+               protected T computeNext() {
+                  while (true) {
+                     if (iterator == null) {
+                        response = markerToNext.apply(response.getNextMarker());
+                        iterator = response.iterator();
+                     }
+                     if (iterator.hasNext()) {
+                        return iterator.next();
+                     }
+                     if (response.getNextMarker() == null) {
+                        return endOfData();
+                     }
+                     iterator = null;
+                  }
+               }
+
+            };
+         }
+
+         @Override
+         public String toString() {
+            return "lazyContinue(" + markerToNext.toString() + ")";
+         }
+      };
+   }
+
+}
diff --git a/core/src/main/java/org/jclouds/collect/TransformingSetSupplier.java b/core/src/main/java/org/jclouds/collect/TransformingSetSupplier.java
index 2010e8b..693026b 100644
--- a/core/src/main/java/org/jclouds/collect/TransformingSetSupplier.java
+++ b/core/src/main/java/org/jclouds/collect/TransformingSetSupplier.java
@@ -20,20 +20,23 @@
 
 import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.base.Predicates.notNull;
-import static com.google.common.collect.Iterables.filter;
-import static com.google.common.collect.Iterables.transform;
 
+import java.io.Serializable;
 import java.util.Set;
 
 import com.google.common.base.Function;
+import com.google.common.base.Objects;
 import com.google.common.base.Supplier;
-import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.FluentIterable;
 
 /**
  * 
  * @author Adrian Cole
  */
-public class TransformingSetSupplier<F, T> implements Supplier<Set<? extends T>> {
+public class TransformingSetSupplier<F, T> implements Supplier<Set<? extends T>>, Serializable {
+   /** The serialVersionUID */
+   private static final long serialVersionUID = -8747953419394840218L;
+   
    private final Supplier<Iterable<F>> backingSupplier;
    private final Function<F, T> converter;
 
@@ -44,7 +47,33 @@
 
    @Override
    public Set<? extends T> get() {
-      return ImmutableSet.copyOf(filter(transform(filter(backingSupplier.get(), notNull()), converter), notNull()));
+      Iterable<F> original = backingSupplier.get();
+      return FluentIterable.from(original)
+                           .filter(notNull())
+                           .transform(converter)
+                           .filter(notNull())
+                           .toImmutableSet();
    }
 
+   @Override
+   public int hashCode() {
+      return Objects.hashCode(backingSupplier, converter);
+   }
+
+   @Override
+   public boolean equals(Object obj) {
+      if (this == obj)
+         return true;
+      if (obj == null)
+         return false;
+      if (getClass() != obj.getClass())
+         return false;
+      TransformingSetSupplier<?, ?> that = TransformingSetSupplier.class.cast(obj);
+      return Objects.equal(backingSupplier, that.backingSupplier) && Objects.equal(converter, that.converter);
+   }
+
+   @Override
+   public String toString() {
+      return Objects.toStringHelper(this).add("backingSupplier", backingSupplier).add("converter", converter).toString();
+   }
 }
diff --git a/core/src/main/java/org/jclouds/http/internal/JavaUrlHttpCommandExecutorService.java b/core/src/main/java/org/jclouds/http/internal/JavaUrlHttpCommandExecutorService.java
index a8334f3..3780e5c 100644
--- a/core/src/main/java/org/jclouds/http/internal/JavaUrlHttpCommandExecutorService.java
+++ b/core/src/main/java/org/jclouds/http/internal/JavaUrlHttpCommandExecutorService.java
@@ -215,7 +215,9 @@
          host += ":" + request.getEndpoint().getPort();
       }
       connection.setRequestProperty(HttpHeaders.HOST, host);
-      connection.setRequestProperty(HttpHeaders.USER_AGENT, USER_AGENT);
+      if (connection.getRequestProperty(HttpHeaders.USER_AGENT) == null) {
+          connection.setRequestProperty(HttpHeaders.USER_AGENT, USER_AGENT);
+      }
 
       if (request.getPayload() != null) {
          MutableContentMetadata md = request.getPayload().getContentMetadata();
diff --git a/core/src/main/java/org/jclouds/json/config/GsonModule.java b/core/src/main/java/org/jclouds/json/config/GsonModule.java
index 937f518..8d273c5 100644
--- a/core/src/main/java/org/jclouds/json/config/GsonModule.java
+++ b/core/src/main/java/org/jclouds/json/config/GsonModule.java
@@ -18,6 +18,7 @@
  */
 package org.jclouds.json.config;
 
+import java.beans.ConstructorProperties;
 import java.io.IOException;
 import java.lang.reflect.Type;
 import java.util.Date;
@@ -35,20 +36,32 @@
 import org.jclouds.date.DateService;
 import org.jclouds.domain.JsonBall;
 import org.jclouds.json.Json;
+import org.jclouds.json.internal.DeserializationConstructorAndReflectiveTypeAdapterFactory;
 import org.jclouds.json.internal.EnumTypeAdapterThatReturnsFromValue;
 import org.jclouds.json.internal.GsonWrapper;
+import org.jclouds.json.internal.IgnoreNullMapTypeAdapterFactory;
+import org.jclouds.json.internal.IgnoreNullMultimapTypeAdapterFactory;
+import org.jclouds.json.internal.IgnoreNullSetTypeAdapterFactory;
+import org.jclouds.json.internal.NamingStrategies.AnnotationConstructorNamingStrategy;
+import org.jclouds.json.internal.NamingStrategies.AnnotationOrNameFieldNamingStrategy;
+import org.jclouds.json.internal.NamingStrategies.ExtractNamed;
+import org.jclouds.json.internal.NamingStrategies.ExtractSerializedName;
 import org.jclouds.json.internal.NullHackJsonLiteralAdapter;
 import org.jclouds.json.internal.OptionalTypeAdapterFactory;
 
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableMap.Builder;
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
 import com.google.common.primitives.Bytes;
+import com.google.gson.FieldNamingStrategy;
 import com.google.gson.Gson;
 import com.google.gson.GsonBuilder;
 import com.google.gson.TypeAdapter;
 import com.google.gson.TypeAdapterFactory;
+import com.google.gson.internal.ConstructorConstructor;
+import com.google.gson.internal.Excluder;
 import com.google.gson.internal.JsonReaderInternalAccess;
 import com.google.gson.reflect.TypeToken;
 import com.google.gson.stream.JsonReader;
@@ -69,8 +82,12 @@
    @Singleton
    Gson provideGson(TypeAdapter<JsonBall> jsonAdapter, DateAdapter adapter, ByteListAdapter byteListAdapter,
             ByteArrayAdapter byteArrayAdapter, PropertiesAdapter propertiesAdapter, JsonAdapterBindings bindings)
-            throws ClassNotFoundException, Exception {
-      GsonBuilder builder = new GsonBuilder();
+            throws Exception {
+
+      FieldNamingStrategy serializationPolicy = new AnnotationOrNameFieldNamingStrategy(new ExtractSerializedName(),
+            new ExtractNamed());
+
+      GsonBuilder builder = new GsonBuilder().setFieldNamingStrategy(serializationPolicy);
 
       // simple (type adapters)
       builder.registerTypeAdapter(Properties.class, propertiesAdapter.nullSafe());
@@ -80,6 +97,17 @@
       builder.registerTypeAdapter(byte[].class, byteArrayAdapter.nullSafe());
       builder.registerTypeAdapter(JsonBall.class, jsonAdapter.nullSafe());
       builder.registerTypeAdapterFactory(new OptionalTypeAdapterFactory());
+      builder.registerTypeAdapterFactory(new IgnoreNullSetTypeAdapterFactory());
+      builder.registerTypeAdapterFactory(new IgnoreNullMapTypeAdapterFactory());
+      builder.registerTypeAdapterFactory(new IgnoreNullMultimapTypeAdapterFactory());
+
+      AnnotationConstructorNamingStrategy deserializationPolicy =
+            new AnnotationConstructorNamingStrategy(
+                  ImmutableSet.of(ConstructorProperties.class, Inject.class), ImmutableSet.of(new ExtractNamed()));
+
+      builder.registerTypeAdapterFactory(
+            new DeserializationConstructorAndReflectiveTypeAdapterFactory(new ConstructorConstructor(),
+            serializationPolicy, Excluder.DEFAULT, deserializationPolicy));
 
       // complicated (serializers/deserializers as they need context to operate)
       builder.registerTypeHierarchyAdapter(Enum.class, new EnumTypeAdapterThatReturnsFromValue());
diff --git a/core/src/main/java/org/jclouds/json/internal/DeserializationConstructorAndReflectiveTypeAdapterFactory.java b/core/src/main/java/org/jclouds/json/internal/DeserializationConstructorAndReflectiveTypeAdapterFactory.java
new file mode 100644
index 0000000..4da8913
--- /dev/null
+++ b/core/src/main/java/org/jclouds/json/internal/DeserializationConstructorAndReflectiveTypeAdapterFactory.java
@@ -0,0 +1,270 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.json.internal;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Type;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.jclouds.json.internal.NamingStrategies.ConstructorFieldNamingStrategy;
+
+import com.google.gson.FieldNamingStrategy;
+import com.google.gson.Gson;
+import com.google.gson.JsonSyntaxException;
+import com.google.gson.TypeAdapter;
+import com.google.gson.TypeAdapterFactory;
+import com.google.gson.internal.$Gson$Types;
+import com.google.gson.internal.ConstructorConstructor;
+import com.google.gson.internal.Excluder;
+import com.google.gson.internal.bind.ReflectiveTypeAdapterFactory;
+import com.google.gson.reflect.TypeToken;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonToken;
+import com.google.gson.stream.JsonWriter;
+
+/**
+ * Creates type adapters for types handled in the following ways:
+ * <p/>
+ * <ul>
+ * <li>Deserialization</li>
+ * If there's an annotation designating a parameterized constructor, invoke that for fields
+ * correlating to named parameter annotations. Otherwise, use {@link ConstructorConstructor}, and
+ * set fields via reflection.
+ * <p/>
+ * Notes: primitive constructor params are set to the Java defaults (0 or false) if not present; and
+ *  the empty object ({}) is treated as a null if the constructor for the object throws an NPE.
+ * <li>Serialization</li>
+ * Serialize based on reflective access to fields, delegating to ReflectiveTypeAdaptor.
+ * </ul>
+ * <h3>Example: Using javax inject to select a constructor and corresponding named parameters</h3>
+ * <p/>
+ * <pre>
+ *    
+ * import NamingStrategies.*;
+ *
+ * serializationStrategy = new AnnotationOrNameFieldNamingStrategy(
+ *    new ExtractSerializedName(), new ExtractNamed());
+ * 
+ * deserializationStrategy = new AnnotationConstructorNamingStrategy(
+ *    Collections.singleton(javax.inject.Inject.class),
+ *    Collections.singleton(new ExtractNamed()));
+ *    
+ * factory = new DeserializationConstructorAndReflectiveTypeAdapterFactory(new ConstructorConstructor(),
+ *      serializationStrategy, Excluder.DEFAULT, deserializationStrategy);
+ *
+ * gson = new GsonBuilder(serializationStrategy).registerTypeAdapterFactory(factory).create();
+ *
+ * </pre>
+ * <p/>
+ * The above would work fine on the following class, which has no gson-specific annotations:
+ * <p/>
+ * <pre>
+ * private static class ImmutableAndVerifiedInCtor {
+ *    final int foo;
+ *    &#064;Named(&quot;_bar&quot;)
+ *    final int bar;
+ *
+ *    &#064;Inject
+ *    ImmutableAndVerifiedInCtor(@Named(&quot;foo&quot;) int foo, @Named(&quot;_bar&quot;) int bar) {
+ *       if (foo &lt; 0)
+ *          throw new IllegalArgumentException(&quot;negative!&quot;);
+ *       this.foo = foo;
+ *       this.bar = bar;
+ *    }
+ * }
+ * </pre>
+ * <p/>
+ * <br/>
+ *
+ * @author Adrian Cole
+ * @author Adam Lowe
+ */
+public final class DeserializationConstructorAndReflectiveTypeAdapterFactory implements TypeAdapterFactory {
+   private final ConstructorFieldNamingStrategy constructorFieldNamingPolicy;
+   private final ReflectiveTypeAdapterFactory delegateFactory;
+
+   /**
+    * @param constructorConstructor         passed through to delegate ReflectiveTypeAdapterFactory for serialization
+    * @param serializationFieldNamingPolicy passed through to delegate ReflectiveTypeAdapterFactory for serialization
+    * @param excluder                       passed through to delegate ReflectiveTypeAdapterFactory for serialization
+    * @param deserializationFieldNamingPolicy
+    *                                       determines which constructor to use and how to determine field names for
+    *                                       deserialization
+    * @see ReflectiveTypeAdapterFactory
+    */
+   public DeserializationConstructorAndReflectiveTypeAdapterFactory(
+         ConstructorConstructor constructorConstructor,
+         FieldNamingStrategy serializationFieldNamingPolicy,
+         Excluder excluder,
+         ConstructorFieldNamingStrategy deserializationFieldNamingPolicy) {
+      this.constructorFieldNamingPolicy = checkNotNull(deserializationFieldNamingPolicy, "deserializationFieldNamingPolicy");
+      this.delegateFactory = new ReflectiveTypeAdapterFactory(constructorConstructor, checkNotNull(serializationFieldNamingPolicy, "fieldNamingPolicy"), checkNotNull(excluder, "excluder"));
+   }
+
+   @SuppressWarnings("unchecked")
+   public <T> TypeAdapter<T> create(Gson gson, final TypeToken<T> type) {
+      Class<? super T> raw = type.getRawType();
+      Constructor<? super T> deserializationCtor = constructorFieldNamingPolicy.getDeserializationConstructor(raw);
+
+      if (deserializationCtor == null) {
+         return null; // allow GSON to choose the correct Adapter (can't simply return delegateFactory.create())
+      } else {
+         deserializationCtor.setAccessible(true);
+         return new DeserializeWithParameterizedConstructorSerializeWithDelegate<T>(delegateFactory.create(gson, type), deserializationCtor,
+               getParameterReaders(gson, type, deserializationCtor));
+      }
+   }
+
+   private final class DeserializeWithParameterizedConstructorSerializeWithDelegate<T> extends TypeAdapter<T> {
+      private final Constructor<? super T> parameterizedCtor;
+      private final Map<String, ParameterReader> parameterReaders;
+      private final TypeAdapter<T> delegate;
+
+      private DeserializeWithParameterizedConstructorSerializeWithDelegate(TypeAdapter<T> delegate,
+                                                                           Constructor<? super T> parameterizedCtor, Map<String, ParameterReader> parameterReaders) {
+         this.delegate = delegate;
+         this.parameterizedCtor = parameterizedCtor;
+         this.parameterReaders = parameterReaders;
+      }
+
+      @Override
+      public T read(JsonReader in) throws IOException {
+         if (in.peek() == JsonToken.NULL) {
+            in.nextNull();
+            return null;
+         }
+
+         Class<?>[] paramTypes = parameterizedCtor.getParameterTypes();
+         Object[] ctorParams = new Object[paramTypes.length];
+         boolean empty = true;
+
+         // Set all primitive constructor params to defaults
+         for (int i = 0; i < paramTypes.length; i++) {
+            if (paramTypes[i] == boolean.class) {
+               ctorParams[i] = Boolean.FALSE;
+            } else if (paramTypes[i].isPrimitive()) {
+               ctorParams[i] = 0;
+            }
+         }
+
+         try {
+            in.beginObject();
+            while (in.hasNext()) {
+               empty = false;
+               String name = in.nextName();
+               ParameterReader parameter = parameterReaders.get(name);
+               if (parameter == null) {
+                  in.skipValue();
+               } else {
+                  Object value = parameter.read(in);
+                  if (value != null) ctorParams[parameter.index] = value;
+               }
+            }
+         } catch (IllegalStateException e) {
+            throw new JsonSyntaxException(e);
+         }
+
+         for (int i = 0; i < paramTypes.length; i++) {
+            if (paramTypes[i].isPrimitive()) {
+               checkArgument(ctorParams[i] != null, "Primitive param[" + i + "] in constructor " + parameterizedCtor
+                     + " cannot be absent!");
+            }
+         }
+         in.endObject();
+
+         try {
+            return newInstance(ctorParams);
+         } catch (NullPointerException ex) {
+            // If {} was found and constructor threw NPE, we treat the field as null
+            if (empty && paramTypes.length > 0) {
+               return null;
+            }
+            throw ex;
+         }
+      }
+
+      /**
+       * pass to delegate
+       */
+      @Override
+      public void write(JsonWriter out, T value) throws IOException {
+         delegate.write(out, value);
+      }
+
+      @SuppressWarnings("unchecked")
+      private T newInstance(Object[] ctorParams) throws AssertionError {
+         try {
+            return (T) parameterizedCtor.newInstance(ctorParams);
+         } catch (InstantiationException e) {
+            throw new AssertionError(e);
+         } catch (IllegalAccessException e) {
+            throw new AssertionError(e);
+         } catch (InvocationTargetException e) {
+            if (e.getCause() instanceof RuntimeException)
+               throw RuntimeException.class.cast(e.getCause());
+            throw new AssertionError(e);
+         }
+      }
+   }
+
+   // logic borrowed from ReflectiveTypeAdapterFactory
+   static class ParameterReader<T> {
+      final String name;
+      final int index;
+      final TypeAdapter<T> typeAdapter;
+
+      ParameterReader(String name, int index, TypeAdapter<T> typeAdapter) {
+         this.name = name;
+         this.index = index;
+         this.typeAdapter = typeAdapter;
+      }
+
+      public Object read(JsonReader reader) throws IOException {
+         return typeAdapter.read(reader);
+      }
+   }
+
+   @SuppressWarnings("unchecked")
+   private Map<String, ParameterReader> getParameterReaders(Gson context, TypeToken<?> declaring, Constructor<?> constructor) {
+      Map<String, ParameterReader> result = new LinkedHashMap<String, ParameterReader>();
+
+      for (int index = 0; index < constructor.getGenericParameterTypes().length; index++) {
+         Type parameterType = getTypeOfConstructorParameter(declaring, constructor, index);
+         TypeAdapter<?> adapter = context.getAdapter(TypeToken.get(parameterType));
+         String parameterName = constructorFieldNamingPolicy.translateName(constructor, index);
+         checkArgument(parameterName != null, constructor + " parameter " + 0 + " failed to be named by " + constructorFieldNamingPolicy);
+         ParameterReader parameterReader = new ParameterReader(parameterName, index, adapter);
+         ParameterReader previous = result.put(parameterReader.name, parameterReader);
+         checkArgument(previous == null, constructor + " declares multiple JSON parameters named " + parameterReader.name);
+      }
+
+      return result;
+   }
+
+   private Type getTypeOfConstructorParameter(TypeToken<?> declaring, Constructor<?> constructor, int index) {
+      Type genericParameter = constructor.getGenericParameterTypes()[index];
+      return $Gson$Types.resolve(declaring.getType(), declaring.getRawType(), genericParameter);
+   }
+}
diff --git a/core/src/main/java/org/jclouds/json/internal/IgnoreNullMapTypeAdapterFactory.java b/core/src/main/java/org/jclouds/json/internal/IgnoreNullMapTypeAdapterFactory.java
new file mode 100644
index 0000000..7fcfb2b
--- /dev/null
+++ b/core/src/main/java/org/jclouds/json/internal/IgnoreNullMapTypeAdapterFactory.java
@@ -0,0 +1,82 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.json.internal;
+
+import java.io.IOException;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.Map;
+
+import com.google.common.collect.Maps;
+import com.google.gson.Gson;
+import com.google.gson.TypeAdapter;
+import com.google.gson.TypeAdapterFactory;
+import com.google.gson.internal.JsonReaderInternalAccess;
+import com.google.gson.reflect.TypeToken;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonWriter;
+
+/**
+ * Eliminates null values when deserializing Maps
+ * <p/>
+ * Treats {"a":null} as the empty map; {"a":1, "b":null} as {"a":1}; etc.
+ * 
+ * @author Adam Lowe
+ */
+public class IgnoreNullMapTypeAdapterFactory implements TypeAdapterFactory {
+   @SuppressWarnings("unchecked")
+   public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
+      Type type = typeToken.getType();
+      if (typeToken.getRawType() != Map.class || !(type instanceof ParameterizedType)) {
+         return null;
+      }
+
+      Type keyType = ((ParameterizedType) type).getActualTypeArguments()[0];
+      Type valueType = ((ParameterizedType) type).getActualTypeArguments()[1];
+      TypeAdapter<?> keyAdapter = gson.getAdapter(TypeToken.get(keyType));
+      TypeAdapter<?> valueAdapter = gson.getAdapter(TypeToken.get(valueType));
+      return (TypeAdapter<T>) newMapAdapter(keyAdapter, valueAdapter);
+   }
+
+   private <K,V> TypeAdapter<Map<K, V>> newMapAdapter(final TypeAdapter<K> keyAdapter, final TypeAdapter<V> valueAdapter) {
+      return new TypeAdapter<Map<K, V>>() {
+         public void write(JsonWriter out, Map<K, V> value) throws IOException {
+            out.beginObject();
+            for (Map.Entry<K, V> element : value.entrySet()) {
+               out.name(String.valueOf(element.getKey()));
+               valueAdapter.write(out, element.getValue());
+            }
+            out.endObject();
+         }
+
+         public Map<K, V> read(JsonReader in) throws IOException {
+            Map<K, V> result = Maps.newLinkedHashMap();
+            in.beginObject();
+            while (in.hasNext()) {
+               JsonReaderInternalAccess.INSTANCE.promoteNameToValue(in);
+               K name = keyAdapter.read(in);
+               V value = valueAdapter.read(in);
+               if (value != null) result.put(name, value);
+            }
+            in.endObject();
+            return result;
+         }
+      }.nullSafe();
+   }
+}
\ No newline at end of file
diff --git a/core/src/main/java/org/jclouds/json/internal/IgnoreNullMultimapTypeAdapterFactory.java b/core/src/main/java/org/jclouds/json/internal/IgnoreNullMultimapTypeAdapterFactory.java
new file mode 100644
index 0000000..77938bd
--- /dev/null
+++ b/core/src/main/java/org/jclouds/json/internal/IgnoreNullMultimapTypeAdapterFactory.java
@@ -0,0 +1,90 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.json.internal;
+
+import java.io.IOException;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+
+import com.google.common.collect.ImmutableListMultimap;
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.Multimap;
+import com.google.gson.Gson;
+import com.google.gson.TypeAdapter;
+import com.google.gson.TypeAdapterFactory;
+import com.google.gson.internal.JsonReaderInternalAccess;
+import com.google.gson.reflect.TypeToken;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonWriter;
+
+/**
+ * Parses Multimaps to/from json - strips out any null values when deserializing
+ * 
+ * @author Adam Lowe
+ */
+public class IgnoreNullMultimapTypeAdapterFactory implements TypeAdapterFactory {
+
+   @SuppressWarnings("unchecked")
+   public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
+      Type type = typeToken.getType();
+      if ((typeToken.getRawType() != Multimap.class) || !(type instanceof ParameterizedType)) {
+         return null;
+      }
+
+      Type keyType = ((ParameterizedType) type).getActualTypeArguments()[0];
+      Type valueType = ((ParameterizedType) type).getActualTypeArguments()[1];
+      TypeAdapter<?> keyAdapter = gson.getAdapter(TypeToken.get(keyType));
+      TypeAdapter<?> valueAdapter = gson.getAdapter(TypeToken.get(valueType));
+      return (TypeAdapter<T>) newMapAdapter(keyAdapter, valueAdapter);
+   }
+
+   private <K,V> TypeAdapter<Multimap<K, V>> newMapAdapter(final TypeAdapter<K> keyAdapter, final TypeAdapter<V> valueAdapter) {
+      return new TypeAdapter<Multimap<K, V>>() {
+         public void write(JsonWriter out, Multimap<K, V> map) throws IOException {
+            out.beginObject();
+            for (K key : map.keySet()) {
+               out.name(String.valueOf(key));
+               out.beginArray();
+               for (V value : map.get(key)) {
+                  valueAdapter.write(out, value);
+               }
+               out.endArray();
+            }
+            out.endObject();
+         }
+
+         public Multimap<K, V> read(JsonReader in) throws IOException {
+            ImmutableMultimap.Builder<K, V> result = ImmutableListMultimap.builder();
+            in.beginObject();
+            while (in.hasNext()) {
+               JsonReaderInternalAccess.INSTANCE.promoteNameToValue(in);
+               K name = keyAdapter.read(in);
+               in.beginArray();
+               while (in.hasNext()) {
+                  V value = valueAdapter.read(in);
+                  if (value != null) result.put(name, value);
+               }
+               in.endArray();
+            }
+            in.endObject();
+            return result.build();
+         }
+      }.nullSafe();
+   }
+}
\ No newline at end of file
diff --git a/core/src/main/java/org/jclouds/json/internal/IgnoreNullSetTypeAdapterFactory.java b/core/src/main/java/org/jclouds/json/internal/IgnoreNullSetTypeAdapterFactory.java
new file mode 100644
index 0000000..393a710
--- /dev/null
+++ b/core/src/main/java/org/jclouds/json/internal/IgnoreNullSetTypeAdapterFactory.java
@@ -0,0 +1,77 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.json.internal;
+
+import java.io.IOException;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.Set;
+
+import com.google.common.collect.Sets;
+import com.google.gson.Gson;
+import com.google.gson.TypeAdapter;
+import com.google.gson.TypeAdapterFactory;
+import com.google.gson.reflect.TypeToken;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonWriter;
+
+/**
+ * Eliminates null values when deserializing Sets
+ * <p/>
+ * Treats [null] as the empty set; [A, null] as [A]; etc.
+ * 
+ * @author Adam Lowe
+ */
+public class IgnoreNullSetTypeAdapterFactory implements TypeAdapterFactory {
+   
+   @SuppressWarnings("unchecked")
+   public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
+      Type type = typeToken.getType();
+      if (typeToken.getRawType() != Set.class || !(type instanceof ParameterizedType)) {
+         return null;
+      }
+
+      Type elementType = ((ParameterizedType) type).getActualTypeArguments()[0];
+      TypeAdapter<?> elementAdapter = gson.getAdapter(TypeToken.get(elementType));
+      return (TypeAdapter<T>) newSetAdapter(elementAdapter);
+   }
+
+   private <E> TypeAdapter<Set<E>> newSetAdapter(final TypeAdapter<E> elementAdapter) {
+      return new TypeAdapter<Set<E>>() {
+         public void write(JsonWriter out, Set<E> value) throws IOException {
+            out.beginArray();
+            for (E element : value) {
+               elementAdapter.write(out, element);
+            }
+            out.endArray();
+         }
+
+         public Set<E> read(JsonReader in) throws IOException {
+            Set<E> result = Sets.newLinkedHashSet();
+            in.beginArray();
+            while (in.hasNext()) {
+               E element = elementAdapter.read(in);
+               if (element != null) result.add(element);
+            }
+            in.endArray();
+            return result;
+         }
+      }.nullSafe();
+   }
+}
diff --git a/core/src/main/java/org/jclouds/json/internal/NamingStrategies.java b/core/src/main/java/org/jclouds/json/internal/NamingStrategies.java
new file mode 100644
index 0000000..e3a3c3d
--- /dev/null
+++ b/core/src/main/java/org/jclouds/json/internal/NamingStrategies.java
@@ -0,0 +1,226 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.json.internal;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.beans.ConstructorProperties;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.util.Map;
+import java.util.Set;
+
+import javax.inject.Named;
+
+import com.google.common.base.Function;
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Maps;
+import com.google.gson.FieldNamingStrategy;
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * NamingStrategies used for JSON deserialization using GSON
+ *
+ * @author Adrian Cole
+ * @author Adam Lowe
+ */
+public class NamingStrategies {
+   /**
+    * Specifies how to extract the name from an annotation for use in determining the serialized
+    * name.
+    *
+    * @see com.google.gson.annotations.SerializedName
+    * @see ExtractSerializedName
+    */
+   public abstract static class NameExtractor<A extends Annotation> {
+      protected final Class<A> annotationType;
+
+      protected NameExtractor(Class<A> annotationType) {
+         this.annotationType = checkNotNull(annotationType, "annotationType");
+      }
+
+      public abstract String extractName(A in);
+
+      public Class<A> annotationType() {
+         return annotationType;
+      }
+
+      @Override
+      public String toString() {
+         return "nameExtractor(" + annotationType.getSimpleName() + ")";
+      }
+
+      @Override
+      public int hashCode() {
+         return annotationType.hashCode();
+      }
+
+      @Override
+      public boolean equals(Object obj) {
+         if (this == obj)
+            return true;
+         if (obj == null || getClass() != obj.getClass())
+            return false;
+         return annotationType.equals(NameExtractor.class.cast(obj).annotationType);
+      }
+   }
+
+   public static class ExtractSerializedName extends NameExtractor<SerializedName> {
+      public ExtractSerializedName() {
+         super(SerializedName.class);
+      }
+
+      @Override
+      public String extractName(SerializedName in) {
+         return checkNotNull(in, "input annotation").value();
+      }
+   }
+
+   public static class ExtractNamed extends NameExtractor<Named> {
+      public ExtractNamed() {
+         super(Named.class);
+      }
+
+      @Override
+      public String extractName(Named in) {
+         return checkNotNull(in, "input annotation").value();
+      }
+   }
+
+   public static abstract class AnnotationBasedNamingStrategy {
+      protected final Map<Class<? extends Annotation>, ? extends NameExtractor> annotationToNameExtractor;
+      private String forToString;
+
+      @SuppressWarnings("unchecked")
+      public AnnotationBasedNamingStrategy(Iterable<? extends NameExtractor> extractors) {
+         checkNotNull(extractors, "means to extract names by annotations");
+
+         this.annotationToNameExtractor = Maps.uniqueIndex(extractors, new Function<NameExtractor, Class<? extends Annotation>>() {
+            @Override
+            public Class<? extends Annotation> apply(NameExtractor input) {
+               return input.annotationType();
+            }
+         });
+         this.forToString = Joiner.on(",").join(Iterables.transform(extractors, new Function<NameExtractor, String>() {
+            @Override
+            public String apply(NameExtractor input) {
+               return input.annotationType().getName();
+            }
+         }));
+      }
+
+      @Override
+      public String toString() {
+         return "AnnotationBasedNamingStrategy requiring one of " + forToString;
+      }
+   }
+
+   /**
+    * Definition of field naming policy for annotation-based field
+    */
+   public static class AnnotationFieldNamingStrategy extends AnnotationBasedNamingStrategy implements FieldNamingStrategy {
+
+      public AnnotationFieldNamingStrategy(Iterable<? extends NameExtractor> extractors) {
+         super(extractors);
+         checkArgument(extractors.iterator().hasNext(), "you must supply at least one name extractor, for example: "
+               + ExtractSerializedName.class.getSimpleName());
+      }
+
+      @SuppressWarnings("unchecked")
+      @Override
+      public String translateName(Field f) {
+         for (Annotation annotation : f.getAnnotations()) {
+            if (annotationToNameExtractor.containsKey(annotation.annotationType())) {
+               return annotationToNameExtractor.get(annotation.annotationType()).extractName(annotation);
+            }
+         }
+         return null;
+      }
+   }
+
+   public static class AnnotationOrNameFieldNamingStrategy extends AnnotationFieldNamingStrategy implements FieldNamingStrategy {
+      public AnnotationOrNameFieldNamingStrategy(NameExtractor... extractors) {
+         this(ImmutableSet.copyOf(extractors));
+      }
+
+      public AnnotationOrNameFieldNamingStrategy(Iterable<? extends NameExtractor> extractors) {
+         super(extractors);
+      }
+
+      @Override
+      public String translateName(Field f) {
+         String result = super.translateName(f);
+         return result == null ? f.getName() : result;
+      }
+   }
+
+   public static interface ConstructorFieldNamingStrategy {
+      public String translateName(Constructor<?> c, int index);
+
+      public <T> Constructor<? super T> getDeserializationConstructor(Class<?> raw);
+
+   }
+
+   /**
+    * Determines field naming from constructor annotations
+    */
+   public static class AnnotationConstructorNamingStrategy extends AnnotationBasedNamingStrategy implements ConstructorFieldNamingStrategy {
+      private final Set<Class<? extends Annotation>> markers;
+
+      public AnnotationConstructorNamingStrategy(Iterable<? extends Class<? extends Annotation>> markers, Iterable<? extends NameExtractor> extractors) {
+         super(extractors);
+         this.markers = ImmutableSet.copyOf(checkNotNull(markers, "you must supply at least one annotation to mark deserialization constructors"));
+      }
+
+      @SuppressWarnings("unchecked")
+      public <T> Constructor<? super T> getDeserializationConstructor(Class<?> raw) {
+         for (Constructor<?> ctor : raw.getDeclaredConstructors())
+            for (Class<? extends Annotation> deserializationCtorAnnotation : markers)
+               if (ctor.isAnnotationPresent(deserializationCtorAnnotation))
+                  return (Constructor<T>) ctor;
+
+         return null;
+      }
+
+      @SuppressWarnings("unchecked")
+      @Override
+      public String translateName(Constructor<?> c, int index) {
+         String name = null;
+
+         if (markers.contains(ConstructorProperties.class) && c.getAnnotation(ConstructorProperties.class) != null) {
+            String[] names = c.getAnnotation(ConstructorProperties.class).value();
+            if (names != null && names.length > index) {
+               name = names[index];
+            }
+         }
+
+         for (Annotation annotation : c.getParameterAnnotations()[index]) {
+            if (annotationToNameExtractor.containsKey(annotation.annotationType())) {
+               name = annotationToNameExtractor.get(annotation.annotationType()).extractName(annotation);
+               break;
+            }
+         }
+         return name;
+      }
+   }
+}
diff --git a/core/src/main/java/org/jclouds/location/config/LocationModule.java b/core/src/main/java/org/jclouds/location/config/LocationModule.java
index c3737ff..089b62f 100644
--- a/core/src/main/java/org/jclouds/location/config/LocationModule.java
+++ b/core/src/main/java/org/jclouds/location/config/LocationModule.java
@@ -24,6 +24,7 @@
 import java.net.URI;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicReference;
 
 import javax.inject.Named;
@@ -51,12 +52,12 @@
 import org.jclouds.rest.AuthorizationException;
 import org.jclouds.rest.functions.ImplicitOptionalConverter;
 import org.jclouds.rest.suppliers.MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier;
+import org.jclouds.util.Suppliers2;
 
 import com.google.common.base.Function;
 import com.google.common.base.Optional;
 import com.google.common.base.Predicate;
 import com.google.common.base.Supplier;
-import com.google.common.base.Suppliers;
 import com.google.common.collect.Sets;
 import com.google.inject.AbstractModule;
 import com.google.inject.Provides;
@@ -81,7 +82,8 @@
    protected Supplier<Map<String, Supplier<Set<String>>>> isoCodesSupplier(
             AtomicReference<AuthorizationException> authException, @Named(PROPERTY_SESSION_INTERVAL) long seconds,
             LocationIdToIso3166CodesSupplier uncached) {
-      return MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(authException, seconds, uncached);
+      return MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(authException, uncached, seconds,
+               TimeUnit.SECONDS);
    }
 
    @Provides
@@ -89,14 +91,16 @@
    @Provider
    protected Supplier<URI> provideProvider(AtomicReference<AuthorizationException> authException,
             @Named(PROPERTY_SESSION_INTERVAL) long seconds, ProviderURISupplier uncached) {
-      return MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(authException, seconds, uncached);
+      return MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(authException, uncached, seconds,
+               TimeUnit.SECONDS);
    }
 
    @Provides
    @Singleton
    protected Supplier<Location> implicitLocationSupplier(AtomicReference<AuthorizationException> authException,
             @Named(PROPERTY_SESSION_INTERVAL) long seconds, ImplicitLocationSupplier uncached) {
-      return MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(authException, seconds, uncached);
+      return MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(authException, uncached, seconds,
+               TimeUnit.SECONDS);
    }
 
    @Provides
@@ -106,7 +110,8 @@
    protected Supplier<Set<? extends Location>> memoizedLocationsSupplier(
             AtomicReference<AuthorizationException> authException, @Named(PROPERTY_SESSION_INTERVAL) long seconds,
             LocationsSupplier uncached) {
-      return MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(authException, seconds, uncached);
+      return MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(authException, uncached, seconds,
+               TimeUnit.SECONDS);
    }
 
    @Provides
@@ -114,8 +119,8 @@
    @Region
    protected Supplier<Set<String>> regionIdsSupplier(AtomicReference<AuthorizationException> authException,
             @Named(PROPERTY_SESSION_INTERVAL) long seconds, RegionIdFilter filter, RegionIdsSupplier uncached) {
-      return MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(authException, seconds,
-            Suppliers.compose(new FilterStrings(filter), uncached));
+      return MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(authException,
+               Suppliers2.compose(new FilterStrings(filter), uncached), seconds, TimeUnit.SECONDS);
    }
    
    @Provides
@@ -124,8 +129,8 @@
    protected Supplier<Set<String>> zoneIdsSupplier(
             AtomicReference<AuthorizationException> authException, @Named(PROPERTY_SESSION_INTERVAL) long seconds,
             ZoneIdFilter filter, ZoneIdsSupplier uncached) {
-      return MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(authException, seconds,
-            Suppliers.compose(new FilterStrings(filter), uncached));
+      return MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(authException,
+               Suppliers2.compose(new FilterStrings(filter), uncached), seconds, TimeUnit.SECONDS);
    }
 
    static class FilterStrings implements Function<Set<String>, Set<String>>{
@@ -149,7 +154,8 @@
    protected Supplier<Map<String, Supplier<URI>>> regionIdToURISupplier(
             AtomicReference<AuthorizationException> authException, @Named(PROPERTY_SESSION_INTERVAL) long seconds,
             RegionIdToURISupplier uncached) {
-      return MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(authException, seconds, uncached);
+      return MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(authException, uncached, seconds,
+               TimeUnit.SECONDS);
    }
 
    @Provides
@@ -157,7 +163,8 @@
    @Region
    protected Supplier<String> implicitRegionIdSupplier(AtomicReference<AuthorizationException> authException,
             @Named(PROPERTY_SESSION_INTERVAL) long seconds, ImplicitRegionIdSupplier uncached) {
-      return MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(authException, seconds, uncached);
+      return MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(authException, uncached, seconds,
+               TimeUnit.SECONDS);
    }
 
 
@@ -167,7 +174,8 @@
    protected Supplier<Map<String, Supplier<Set<String>>>> regionIdToZoneIdsSupplier(
             AtomicReference<AuthorizationException> authException, @Named(PROPERTY_SESSION_INTERVAL) long seconds,
             RegionIdToZoneIdsSupplier uncached) {
-      return MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(authException, seconds, uncached);
+      return MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(authException, uncached, seconds,
+               TimeUnit.SECONDS);
    }
 
    @Provides
@@ -176,6 +184,7 @@
    protected Supplier<Map<String, Supplier<URI>>> zoneIdToURISupplier(
             AtomicReference<AuthorizationException> authException, @Named(PROPERTY_SESSION_INTERVAL) long seconds,
             ZoneIdToURISupplier uncached) {
-      return MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(authException, seconds, uncached);
+      return MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(authException, uncached, seconds,
+               TimeUnit.SECONDS);
    }
 }
\ No newline at end of file
diff --git a/core/src/main/java/org/jclouds/location/suppliers/all/RegionToProvider.java b/core/src/main/java/org/jclouds/location/suppliers/all/RegionToProvider.java
new file mode 100644
index 0000000..1a90178
--- /dev/null
+++ b/core/src/main/java/org/jclouds/location/suppliers/all/RegionToProvider.java
@@ -0,0 +1,77 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.location.suppliers.all;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+
+import java.util.Map;
+import java.util.Set;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.jclouds.domain.Location;
+import org.jclouds.domain.LocationBuilder;
+import org.jclouds.domain.LocationScope;
+import org.jclouds.location.Iso3166;
+import org.jclouds.location.Region;
+import org.jclouds.location.suppliers.LocationsSupplier;
+
+import com.google.common.base.Supplier;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSet.Builder;
+import com.google.common.collect.Iterables;
+
+/**
+ * 
+ * @author Adrian Cole
+ */
+@Singleton
+public class RegionToProvider implements LocationsSupplier {
+   private final JustProvider justProvider;
+   private final Supplier<Set<String>> regionsSupplier;
+   private final Supplier<Map<String, Supplier<Set<String>>>> isoCodesByIdSupplier;
+
+   @Inject
+   public RegionToProvider(JustProvider justProvider, @Region Supplier<Set<String>> regionsSupplier,
+            @Iso3166 Supplier<Map<String, Supplier<Set<String>>>> isoCodesByIdSupplier) {
+      this.justProvider = checkNotNull(justProvider, "justProvider");
+      this.regionsSupplier = checkNotNull(regionsSupplier, "regionsSupplier");
+      this.isoCodesByIdSupplier = checkNotNull(isoCodesByIdSupplier, "isoCodesByIdSupplier");
+   }
+
+   @Override
+   public Set<? extends Location> get() {
+      Builder<Location> locations = ImmutableSet.builder();
+      Location provider = Iterables.getOnlyElement(justProvider.get());
+      Set<String> regions = regionsSupplier.get();
+      checkState(regions.size() > 0, "no regions found for provider %s, using supplier %s", provider, regionsSupplier);
+      Map<String, Supplier<Set<String>>> isoCodesById = isoCodesByIdSupplier.get();
+      for (String region : regions) {
+         LocationBuilder builder = new LocationBuilder().scope(LocationScope.REGION).id(region).description(region)
+                  .parent(provider);
+         if (isoCodesById.containsKey(region))
+            builder.iso3166Codes(isoCodesById.get(region).get());
+         locations.add(builder.build());
+      }
+      return locations.build();
+   }
+
+}
\ No newline at end of file
diff --git a/core/src/main/java/org/jclouds/location/suppliers/all/ZoneToProvider.java b/core/src/main/java/org/jclouds/location/suppliers/all/ZoneToProvider.java
new file mode 100644
index 0000000..627d572
--- /dev/null
+++ b/core/src/main/java/org/jclouds/location/suppliers/all/ZoneToProvider.java
@@ -0,0 +1,84 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.location.suppliers.all;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+
+import java.util.Map;
+import java.util.Set;
+
+import javax.annotation.Resource;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.jclouds.domain.Location;
+import org.jclouds.domain.LocationBuilder;
+import org.jclouds.domain.LocationScope;
+import org.jclouds.location.Iso3166;
+import org.jclouds.location.Zone;
+import org.jclouds.location.suppliers.LocationsSupplier;
+import org.jclouds.logging.Logger;
+
+import com.google.common.base.Supplier;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSet.Builder;
+import com.google.common.collect.Iterables;
+
+/**
+ * 
+ * @author Adrian Cole
+ */
+@Singleton
+public class ZoneToProvider implements LocationsSupplier {
+
+   @Resource
+   protected Logger logger = Logger.NULL;
+
+   private final JustProvider justProvider;
+   private final Supplier<Set<String>> zoneIdsSupplier;
+   private final Supplier<Map<String, Supplier<Set<String>>>> isoCodesByIdSupplier;
+
+   @Inject
+   ZoneToProvider(JustProvider justProvider, @Zone Supplier<Set<String>> zoneIdsSupplier,
+            @Iso3166 Supplier<Map<String, Supplier<Set<String>>>> isoCodesByIdSupplier) {
+      this.justProvider = checkNotNull(justProvider, "justProvider");
+      this.zoneIdsSupplier = checkNotNull(zoneIdsSupplier, "zoneIdsSupplier");
+      this.isoCodesByIdSupplier = checkNotNull(isoCodesByIdSupplier, "isoCodesByIdSupplier");
+   }
+
+   @Override
+   public Set<? extends Location> get() {
+      Location provider = Iterables.getOnlyElement(justProvider.get());
+      Set<String> zoneIds = zoneIdsSupplier.get();
+      checkState(zoneIds.size() > 0, "no zones found for provider %s, using supplier %s", provider, zoneIdsSupplier);
+      Map<String, Supplier<Set<String>>> isoCodesById = isoCodesByIdSupplier.get();
+
+      Builder<Location> locations = ImmutableSet.builder();
+      for (String zoneId : zoneIds) {
+         LocationBuilder builder = new LocationBuilder().scope(LocationScope.ZONE).id(zoneId).description(zoneId)
+                  .parent(provider);
+         if (isoCodesById.containsKey(zoneId))
+            builder.iso3166Codes(isoCodesById.get(zoneId).get());
+         locations.add(builder.build());
+      }
+      return locations.build();
+   }
+
+}
\ No newline at end of file
diff --git a/core/src/main/java/org/jclouds/location/suppliers/implicit/FirstRegion.java b/core/src/main/java/org/jclouds/location/suppliers/implicit/FirstRegion.java
new file mode 100644
index 0000000..e0e8feb
--- /dev/null
+++ b/core/src/main/java/org/jclouds/location/suppliers/implicit/FirstRegion.java
@@ -0,0 +1,65 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.location.suppliers.implicit;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.Iterables.find;
+import static com.google.common.collect.Iterables.transform;
+import static org.jclouds.location.predicates.LocationPredicates.isRegion;
+
+import java.util.NoSuchElementException;
+import java.util.Set;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.jclouds.collect.Memoized;
+import org.jclouds.domain.Location;
+import org.jclouds.location.functions.ToIdAndScope;
+import org.jclouds.location.suppliers.ImplicitLocationSupplier;
+
+import com.google.common.base.Supplier;
+
+/**
+ * 
+ * @author Adrian Cole
+ * 
+ */
+@Singleton
+public class FirstRegion implements ImplicitLocationSupplier {
+
+   private final Supplier<Set<? extends Location>> locationsSupplier;
+
+   @Inject
+   FirstRegion(@Memoized Supplier<Set<? extends Location>> locationsSupplier) {
+      this.locationsSupplier = checkNotNull(locationsSupplier, "locationsSupplierSupplier");
+   }
+
+   @Override
+   public Location get() {
+      Set<? extends Location> locations = locationsSupplier.get();
+      try {
+         return find(locations, isRegion());
+      } catch (NoSuchElementException e) {
+         throw new NoSuchElementException("none to of the locations are scope REGION: "
+               + transform(locations, ToIdAndScope.INSTANCE));
+      }
+   }
+
+}
\ No newline at end of file
diff --git a/core/src/main/java/org/jclouds/location/suppliers/implicit/FirstZone.java b/core/src/main/java/org/jclouds/location/suppliers/implicit/FirstZone.java
new file mode 100644
index 0000000..94b48ee
--- /dev/null
+++ b/core/src/main/java/org/jclouds/location/suppliers/implicit/FirstZone.java
@@ -0,0 +1,65 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.location.suppliers.implicit;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.Iterables.find;
+import static com.google.common.collect.Iterables.transform;
+import static org.jclouds.location.predicates.LocationPredicates.isZone;
+
+import java.util.NoSuchElementException;
+import java.util.Set;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.jclouds.collect.Memoized;
+import org.jclouds.domain.Location;
+import org.jclouds.location.functions.ToIdAndScope;
+import org.jclouds.location.suppliers.ImplicitLocationSupplier;
+
+import com.google.common.base.Supplier;
+
+/**
+ * 
+ * @author Adrian Cole
+ * 
+ */
+@Singleton
+public class FirstZone implements ImplicitLocationSupplier {
+
+   private final Supplier<Set<? extends Location>> locationsSupplier;
+
+   @Inject
+   FirstZone(@Memoized Supplier<Set<? extends Location>> locationsSupplier) {
+      this.locationsSupplier = checkNotNull(locationsSupplier, "locationsSupplierSupplier");
+   }
+
+   @Override
+   public Location get() {
+      Set<? extends Location> locations = locationsSupplier.get();
+      try {
+         return find(locations, isZone());
+      } catch (NoSuchElementException e) {
+         throw new NoSuchElementException("none to of the locations are scope ZONE: "
+               + transform(locations, ToIdAndScope.INSTANCE));
+      }
+   }
+
+}
\ No newline at end of file
diff --git a/core/src/main/java/org/jclouds/rest/suppliers/MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.java b/core/src/main/java/org/jclouds/rest/suppliers/MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.java
index 5751b1d..41876de 100644
--- a/core/src/main/java/org/jclouds/rest/suppliers/MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.java
+++ b/core/src/main/java/org/jclouds/rest/suppliers/MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.java
@@ -18,15 +18,24 @@
  */
 package org.jclouds.rest.suppliers;
 
-import static org.jclouds.util.Suppliers2.memoizeWithExpirationOnAbsoluteInterval;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Throwables.propagate;
+import static org.jclouds.util.Throwables2.getFirstThrowableOfType;
 
+import java.io.Serializable;
+import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicReference;
 
-import org.jclouds.concurrent.RetryOnTimeOutExceptionSupplier;
 import org.jclouds.rest.AuthorizationException;
 
+import com.google.common.base.Objects;
 import com.google.common.base.Supplier;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import com.google.common.collect.ForwardingObject;
+import com.google.common.util.concurrent.UncheckedExecutionException;
 
 /**
  * This will retry the supplier if it encounters a timeout exception, but not if it encounters an
@@ -41,31 +50,110 @@
  * it is called again for each provider method that depends on it. To short-circuit this, we
  * remember the last exception trusting that guice is single-threaded.
  * 
+ * Note this implementation is folded into the same class, vs being decorated as stacktraces are
+ * exceptionally long and difficult to grok otherwise. We use {@link LoadingCache} to deal with
+ * concurrency issues related to the supplier.
+ * 
  * @author Adrian Cole
  */
-public class MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier<T> implements Supplier<T> {
+public class MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier<T> extends ForwardingObject implements
+         Supplier<T>, Serializable {
+
+   /** The serialVersionUID */
+   private static final long serialVersionUID = 7626769175726919353L;
+
+   static class NullValueException extends RuntimeException {
+
+      /** The serialVersionUID */
+      private static final long serialVersionUID = 5064521423206078374L;
+
+   }
+
+   static class SetAndThrowAuthorizationExceptionSupplierBackedLoader<V> extends CacheLoader<String, V> implements
+            Serializable {
+      /** The serialVersionUID */
+      private static final long serialVersionUID = -6129510622181946809L;
+
+      private final Supplier<V> delegate;
+      private final AtomicReference<AuthorizationException> authException;
+
+      public SetAndThrowAuthorizationExceptionSupplierBackedLoader(Supplier<V> delegate,
+               AtomicReference<AuthorizationException> authException) {
+         this.delegate = checkNotNull(delegate, "delegate");
+         this.authException = checkNotNull(authException, "authException");
+      }
+
+      @Override
+      public V load(String key) {
+         if (authException.get() != null)
+            throw authException.get();
+         try {
+            V value = delegate.get();
+            if (value == null)
+               throw new NullValueException();
+            return value;
+         } catch (Exception e) {
+            AuthorizationException aex = getFirstThrowableOfType(e, AuthorizationException.class);
+            if (aex != null) {
+               authException.set(aex);
+               throw aex;
+            }
+            throw propagate(e);
+         }
+      }
+
+      @Override
+      public String toString() {
+         return Objects.toStringHelper(this).add("delegate", delegate).toString();
+      }
+
+   }
+
    private final Supplier<T> delegate;
-   private final long seconds;
+   private final long duration;
+   private final TimeUnit unit;
+   private final LoadingCache<String, T> cache;
 
    public static <T> MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier<T> create(
-            AtomicReference<AuthorizationException> authException, long seconds, Supplier<T> delegate) {
-      return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier<T>(authException, seconds, delegate);
+            AtomicReference<AuthorizationException> authException, Supplier<T> delegate, long duration, TimeUnit unit) {
+      return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier<T>(authException, delegate, duration,
+               unit);
    }
-   
-   public MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier(
-         AtomicReference<AuthorizationException> authException, long seconds, Supplier<T> delegate) {
-      this.delegate = memoizeWithExpirationOnAbsoluteInterval(new RetryOnTimeOutExceptionSupplier<T>(
-            new SetAndThrowAuthorizationExceptionSupplier<T>(delegate, authException)), seconds, TimeUnit.SECONDS);
-      this.seconds = seconds;
+
+   MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier(AtomicReference<AuthorizationException> authException,
+            Supplier<T> delegate, long duration, TimeUnit unit) {
+      this.delegate = delegate;
+      this.duration = duration;
+      this.unit = unit;
+      this.cache = CacheBuilder.newBuilder().expireAfterWrite(duration, unit)
+               .build(new SetAndThrowAuthorizationExceptionSupplierBackedLoader<T>(delegate, authException));
+   }
+
+   @Override
+   protected Supplier<T> delegate() {
+      return delegate;
    }
 
    @Override
    public T get() {
-      return delegate.get();
+      try {
+         T returnVal = cache.get("FOO");
+         return returnVal;
+      } catch (UncheckedExecutionException e) {
+         NullValueException nullV = getFirstThrowableOfType(e, NullValueException.class);
+         if (nullV != null) {
+            return null;
+         }
+         throw propagate(e.getCause());
+      } catch (ExecutionException e) {
+         throw propagate(e.getCause());
+      }
    }
 
    @Override
    public String toString() {
-      return "memoizeWithExpiration(" + delegate + ", seconds=" + seconds + ")";
+      return Objects.toStringHelper(this).add("delegate", delegate).add("duration", duration).add("unit", unit)
+               .toString();
    }
+
 }
\ No newline at end of file
diff --git a/core/src/main/java/org/jclouds/util/Suppliers2.java b/core/src/main/java/org/jclouds/util/Suppliers2.java
index 44cbb5f..bc9b80b 100644
--- a/core/src/main/java/org/jclouds/util/Suppliers2.java
+++ b/core/src/main/java/org/jclouds/util/Suppliers2.java
@@ -24,9 +24,7 @@
 import java.io.OutputStream;
 import java.io.Serializable;
 import java.util.Map;
-import java.util.concurrent.TimeUnit;
 
-import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Function;
 import com.google.common.base.Objects;
 import com.google.common.base.Preconditions;
@@ -42,7 +40,7 @@
 public class Suppliers2 {
 
    public static <K, V> Supplier<V> getLastValueInMap(Supplier<Map<K, Supplier<V>>> input) {
-      return Suppliers.compose(new Function<Map<K, Supplier<V>>, V>() {
+      return Suppliers2.compose(new Function<Map<K, Supplier<V>>, V>() {
 
          @Override
          public V apply(Map<K, Supplier<V>> input) {
@@ -57,15 +55,15 @@
    }
 
    public static <X> Function<X, Supplier<X>> ofInstanceFunction() {
-      return new Function<X, Supplier<X>>(){
+      return new Function<X, Supplier<X>>() {
 
          @Override
          public Supplier<X> apply(X arg0) {
             return Suppliers.ofInstance(arg0);
          }
-        
+
          @Override
-         public String toString(){
+         public String toString() {
             return "Suppliers.ofInstance()";
          }
       };
@@ -84,89 +82,35 @@
       };
    }
 
-   /**
-    * See Supplier.memoizeWithExpiration.
-    * 
-    * Difference between this impl and v11.0 is that we fix
-    * http://code.google.com/p/guava-libraries/issues/detail?id=857.
-    */
-   public static <T> Supplier<T> memoizeWithExpirationOnAbsoluteInterval(Supplier<T> delegate, long duration,
-            TimeUnit unit) {
-      return new ExpiringMemoizingSupplier<T>(delegate, duration, unit);
+   // only here until guava compose gives a toString!
+   // http://code.google.com/p/guava-libraries/issues/detail?id=1052
+   public static <F, T> Supplier<T> compose(Function<? super F, T> function, Supplier<F> supplier) {
+      Preconditions.checkNotNull(function);
+      Preconditions.checkNotNull(supplier);
+      return new SupplierComposition<F, T>(function, supplier);
    }
 
-   @VisibleForTesting
-   static class ExpiringMemoizingSupplier<T> implements Supplier<T>, Serializable {
-      final Supplier<T> delegate;
-      final long durationNanos;
-      transient volatile T value;
-      // The special value 0 means "not yet initialized".
-      transient volatile long expirationNanos;
+   private static class SupplierComposition<F, T> implements Supplier<T>, Serializable {
+      /** The serialVersionUID */
+      private static final long serialVersionUID = 1023509665531743802L;
 
-      ExpiringMemoizingSupplier(Supplier<T> delegate, long duration, TimeUnit unit) {
-         this.delegate = Preconditions.checkNotNull(delegate);
-         this.durationNanos = unit.toNanos(duration);
-         Preconditions.checkArgument(duration > 0);
+      final Function<? super F, T> function;
+      final Supplier<F> supplier;
+
+      SupplierComposition(Function<? super F, T> function, Supplier<F> supplier) {
+         this.function = function;
+         this.supplier = supplier;
       }
 
       @Override
       public T get() {
-         // Another variant of Double Checked Locking.
-         //
-         // We use two volatile reads. We could reduce this to one by
-         // putting our fields into a holder class, but (at least on x86)
-         // the extra memory consumption and indirection are more
-         // expensive than the extra volatile reads.
-         long nanos = expirationNanos;
-         long now = System.nanoTime();
-         if (nanos == 0 || now - nanos >= 0) {
-            synchronized (this) {
-               if (nanos == expirationNanos) { // recheck for lost race
-
-                  // Set value to null prior to retrieving new val, so old and new are not held in
-                  // memory simultaneously
-                  value = null;
-
-                  T t = delegate.get();
-                  value = t;
-
-                  // Update now so that, if call was expensive, we keep value for the full duration
-                  now = System.nanoTime();
-
-                  nanos = now + durationNanos;
-                  // In the very unlikely event that nanos is 0, set it to 1;
-                  // no one will notice 1 ns of tardiness.
-                  expirationNanos = (nanos == 0) ? 1 : nanos;
-                  return t;
-               }
-            }
-         }
-         return value;
-      }
-
-      private static final long serialVersionUID = 0;
-
-      @Override
-      public int hashCode() {
-         return Objects.hashCode(delegate, durationNanos);
-      }
-
-      @Override
-      public boolean equals(Object obj) {
-         if (this == obj)
-            return true;
-         if (obj == null)
-            return false;
-         if (getClass() != obj.getClass())
-            return false;
-         ExpiringMemoizingSupplier<?> that = ExpiringMemoizingSupplier.class.cast(obj);
-         return Objects.equal(delegate, that.delegate) && Objects.equal(durationNanos, that.durationNanos);
+         return function.apply(supplier.get());
       }
 
       @Override
       public String toString() {
-         return Objects.toStringHelper(this).add("delegate", delegate).add("durationNanos", durationNanos).toString();
+         return Objects.toStringHelper(this).add("function", function).add("supplier", supplier).toString();
       }
-
    }
+
 }
diff --git a/core/src/test/java/org/jclouds/collect/PaginatedSetsTest.java b/core/src/test/java/org/jclouds/collect/PaginatedSetsTest.java
new file mode 100644
index 0000000..e260c1f
--- /dev/null
+++ b/core/src/test/java/org/jclouds/collect/PaginatedSetsTest.java
@@ -0,0 +1,76 @@
+package org.jclouds.collect;
+
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+
+import org.easymock.EasyMock;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * Tests behavior of {@code PaginatedSets}.
+ * 
+ * @author Adrian Cole
+ */
+@Test(testName = "PaginatedSetsTest")
+public class PaginatedSetsTest {
+
+   @SuppressWarnings("unchecked")
+   @Test
+   public void testSinglePageResultReturnsSame() {
+
+      PaginatedSet<String> initial = PaginatedSet.copyOf(ImmutableSet.of("foo", "bar"));
+      Function<String, PaginatedSet<String>> markerToNext = createMock(Function.class);
+
+      EasyMock.replay(markerToNext);
+
+      Assert.assertSame(PaginatedSets.lazyContinue(initial, markerToNext), initial);
+
+      EasyMock.verify(markerToNext);
+   }
+
+   @SuppressWarnings("unchecked")
+   @Test
+   public void testMultiPage2Pages() {
+
+      PaginatedSet<String> initial = PaginatedSet.copyOfWithMarker(ImmutableSet.of("foo", "bar"), "MARKER1");
+      Function<String, PaginatedSet<String>> markerToNext = createMock(Function.class);
+
+      expect(markerToNext.apply("MARKER1")).andReturn(
+               PaginatedSet.copyOfWithMarker(ImmutableSet.of("boo", "baz"), null));
+
+      EasyMock.replay(markerToNext);
+
+      Assert.assertEquals(ImmutableSet.copyOf(PaginatedSets.lazyContinue(initial, markerToNext)), ImmutableSet.of(
+               "foo", "bar", "boo", "baz"));
+      
+      EasyMock.verify(markerToNext);
+
+   }
+   
+   @SuppressWarnings("unchecked")
+   @Test
+   public void testMultiPage3Pages() {
+
+      PaginatedSet<String> initial = PaginatedSet.copyOfWithMarker(ImmutableSet.of("foo", "bar"), "MARKER1");
+      Function<String, PaginatedSet<String>> markerToNext = createMock(Function.class);
+
+      expect(markerToNext.apply("MARKER1")).andReturn(
+               PaginatedSet.copyOfWithMarker(ImmutableSet.of("boo", "baz"), "MARKER2"));
+
+      expect(markerToNext.apply("MARKER2")).andReturn(
+               PaginatedSet.copyOfWithMarker(ImmutableSet.of("ham", "cheeze"), null));
+
+      EasyMock.replay(markerToNext);
+
+      Assert.assertEquals(ImmutableSet.copyOf(PaginatedSets.lazyContinue(initial, markerToNext)), ImmutableSet.of(
+               "foo", "bar", "boo", "baz", "ham", "cheeze"));
+      
+      EasyMock.verify(markerToNext);
+
+   }
+
+}
diff --git a/core/src/test/java/org/jclouds/json/internal/DeserializationConstructorAndReflectiveTypeAdapterFactoryTest.java b/core/src/test/java/org/jclouds/json/internal/DeserializationConstructorAndReflectiveTypeAdapterFactoryTest.java
new file mode 100644
index 0000000..478a3f1
--- /dev/null
+++ b/core/src/test/java/org/jclouds/json/internal/DeserializationConstructorAndReflectiveTypeAdapterFactoryTest.java
@@ -0,0 +1,286 @@
+package org.jclouds.json.internal;
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotSame;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.fail;
+
+import java.beans.ConstructorProperties;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import org.jclouds.json.internal.NamingStrategies.AnnotationOrNameFieldNamingStrategy;
+import org.jclouds.json.internal.NamingStrategies.ExtractNamed;
+import org.jclouds.json.internal.NamingStrategies.ExtractSerializedName;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableSet;
+import com.google.gson.FieldNamingStrategy;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.TypeAdapter;
+import com.google.gson.internal.ConstructorConstructor;
+import com.google.gson.internal.Excluder;
+import com.google.gson.reflect.TypeToken;
+
+/**
+ * @author Adrian Cole
+ * @author Adam Lowe
+ */
+@Test(testName = "DeserializationConstructorTypeAdapterFactoryTest")
+public final class DeserializationConstructorAndReflectiveTypeAdapterFactoryTest {
+
+   Gson gson = new Gson();
+
+   DeserializationConstructorAndReflectiveTypeAdapterFactory parameterizedCtorFactory = parameterizedCtorFactory();
+
+   static DeserializationConstructorAndReflectiveTypeAdapterFactory parameterizedCtorFactory() {
+      FieldNamingStrategy serializationPolicy = new AnnotationOrNameFieldNamingStrategy(
+            ImmutableSet.of(new ExtractSerializedName(), new ExtractNamed())
+      );
+      NamingStrategies.AnnotationConstructorNamingStrategy deserializationPolicy =
+            new NamingStrategies.AnnotationConstructorNamingStrategy(
+                  ImmutableSet.of(ConstructorProperties.class, Inject.class),
+                  ImmutableSet.of(new ExtractNamed()));
+
+      return new DeserializationConstructorAndReflectiveTypeAdapterFactory(new ConstructorConstructor(),
+            serializationPolicy, Excluder.DEFAULT, deserializationPolicy);
+   }
+
+   public void testNullWhenPrimitive() {
+      assertNull(parameterizedCtorFactory.create(gson, TypeToken.get(int.class)));
+   }
+
+   private static class DefaultConstructor {
+      int foo;
+      int bar;
+
+      private DefaultConstructor() {
+      }
+
+      @Override
+      public boolean equals(Object obj) {
+         DefaultConstructor other = DefaultConstructor.class.cast(obj);
+         if (bar != other.bar)
+            return false;
+         if (foo != other.foo)
+            return false;
+         return true;
+      }
+
+   }
+
+   public void testRejectsIfNoConstuctorMarked() throws IOException {
+      TypeAdapter<DefaultConstructor> adapter = parameterizedCtorFactory.create(gson, TypeToken.get(DefaultConstructor.class));
+      assertNull(adapter);
+   }
+
+   private static class WithDeserializationConstructorButWithoutSerializedName {
+      final int foo;
+
+      @Inject
+      WithDeserializationConstructorButWithoutSerializedName(int foo) {
+         this.foo = foo;
+      }
+   }
+
+   public void testSerializedNameRequiredOnAllParameters() {
+      try {
+         parameterizedCtorFactory.create(gson, TypeToken
+               .get(WithDeserializationConstructorButWithoutSerializedName.class));
+         fail();
+      } catch (IllegalArgumentException actual) {
+         assertEquals(actual.getMessage(),
+               "org.jclouds.json.internal.DeserializationConstructorAndReflectiveTypeAdapterFactoryTest$WithDeserializationConstructorButWithoutSerializedName(int)" +
+                     " parameter 0 failed to be named by AnnotationBasedNamingStrategy requiring one of javax.inject.Named");
+      }
+   }
+
+   private static class DuplicateSerializedNames {
+      final int foo;
+      final int bar;
+
+      @Inject
+      DuplicateSerializedNames(@Named("foo") int foo, @Named("foo") int bar) {
+         this.foo = foo;
+         this.bar = bar;
+      }
+   }
+
+   public void testNoDuplicateSerializedNamesRequiredOnAllParameters() {
+      try {
+         parameterizedCtorFactory.create(gson, TypeToken.get(DuplicateSerializedNames.class));
+         fail();
+      } catch (IllegalArgumentException actual) {
+         assertEquals(actual.getMessage(),
+               "org.jclouds.json.internal.DeserializationConstructorAndReflectiveTypeAdapterFactoryTest$DuplicateSerializedNames(int,int)" +
+                     " declares multiple JSON parameters named foo");
+      }
+   }
+
+   private static class ValidatedConstructor {
+      final int foo;
+      final int bar;
+
+      @Inject
+      ValidatedConstructor(@Named("foo") int foo, @Named("bar") int bar) {
+         if (foo < 0)
+            throw new IllegalArgumentException("negative!");
+         this.foo = foo;
+         this.bar = bar;
+      }
+
+      @Override
+      public boolean equals(Object obj) {
+         ValidatedConstructor other = ValidatedConstructor.class.cast(obj);
+         if (bar != other.bar)
+            return false;
+         if (foo != other.foo)
+            return false;
+         return true;
+      }
+
+      @Override
+      public String toString() { return "ValidatedConstructor[foo=" + foo + ",bar=" + bar + "]"; }
+   }
+
+   public void testValidatedConstructor() throws IOException {
+      TypeAdapter<ValidatedConstructor> adapter = parameterizedCtorFactory.create(gson, TypeToken
+            .get(ValidatedConstructor.class));
+      assertEquals(new ValidatedConstructor(0, 1), adapter.fromJson("{\"foo\":0,\"bar\":1}"));
+      try {
+         adapter.fromJson("{\"foo\":-1,\"bar\":1}");
+         fail();
+      } catch (IllegalArgumentException expected) {
+         assertEquals("negative!", expected.getMessage());
+      }
+   }
+
+   private static class GenericParamsCopiedIn {
+      final List<String> foo;
+      final Map<String, String> bar;
+
+      @Inject
+      GenericParamsCopiedIn(@Named("foo") List<String> foo, @Named("bar") Map<String, String> bar) {
+         this.foo = new ArrayList<String>(foo);
+         this.bar = new HashMap<String, String>(bar);
+      }
+
+   }
+
+   public void testGenericParamsCopiedIn() throws IOException {
+      TypeAdapter<GenericParamsCopiedIn> adapter = parameterizedCtorFactory.create(gson, TypeToken
+            .get(GenericParamsCopiedIn.class));
+      List<String> inputFoo = new ArrayList<String>();
+      inputFoo.add("one");
+      HashMap<String, String> inputBar = new HashMap<String, String>();
+      inputBar.put("2", "two");
+
+      GenericParamsCopiedIn toTest = adapter.fromJson("{ \"foo\":[\"one\"], \"bar\":{ \"2\":\"two\"}}");
+      assertEquals(inputFoo, toTest.foo);
+      assertNotSame(inputFoo, toTest.foo);
+      assertEquals(inputBar, toTest.bar);
+
+   }
+
+   private static class RenamedFields {
+      final int foo;
+      @Named("_bar")
+      final int bar;
+
+      @ConstructorProperties({"foo", "_bar"})
+      RenamedFields(int foo, int bar) {
+         if (foo < 0)
+            throw new IllegalArgumentException("negative!");
+         this.foo = foo;
+         this.bar = bar;
+      }
+
+      @Override
+      public boolean equals(Object obj) {
+         RenamedFields other = RenamedFields.class.cast(obj);
+         if (bar != other.bar)
+            return false;
+         if (foo != other.foo)
+            return false;
+         return true;
+      }
+   }
+
+   public void testRenamedFields() throws IOException {
+      TypeAdapter<RenamedFields> adapter = parameterizedCtorFactory.create(gson, TypeToken.get(RenamedFields.class));
+      assertEquals(new RenamedFields(0, 1), adapter.fromJson("{\"foo\":0,\"_bar\":1}"));
+      assertEquals(adapter.toJson(new RenamedFields(0, 1)), "{\"foo\":0,\"_bar\":1}");
+   }
+
+   private static class ComposedObjects {
+      final ValidatedConstructor x;
+      final ValidatedConstructor y;
+
+      @ConstructorProperties({"x", "y"})
+      ComposedObjects(ValidatedConstructor x, ValidatedConstructor y) {
+         this.x = checkNotNull(x);
+         this.y = checkNotNull(y);
+      }
+
+      @Override
+      public boolean equals(Object obj) {
+         ComposedObjects other = ComposedObjects.class.cast(obj);
+         return other != null && Objects.equal(x, other.x) && Objects.equal(y, other.y);
+      }
+      
+      @Override
+      public String toString() { return "ComposedObjects[x=" + x.toString() + ";y=" + y.toString() + "]"; }
+   }
+   
+   public void checkSimpleComposedObject() throws IOException  {
+      ValidatedConstructor x = new ValidatedConstructor(0,1);
+      ValidatedConstructor y = new ValidatedConstructor(1,2);
+      TypeAdapter<ComposedObjects> adapter = parameterizedCtorFactory.create(gson, TypeToken.get(ComposedObjects.class));
+      assertEquals(new ComposedObjects(x, y), adapter.fromJson("{\"x\":{\"foo\":0,\"bar\":1},\"y\":{\"foo\":1,\"bar\":2}}"));
+   }
+
+   public void testEmptyObjectIsNull() throws IOException {
+      TypeAdapter<ComposedObjects> adapter = parameterizedCtorFactory.create(gson, TypeToken.get(ComposedObjects.class));
+      assertNull(adapter.fromJson("{}"));
+   }
+
+   @Test(expectedExceptions = NullPointerException.class)
+   public void testPartialObjectStillThrows() throws IOException {
+      TypeAdapter<ComposedObjects> adapter = parameterizedCtorFactory.create(gson, TypeToken.get(ComposedObjects.class));
+      assertNull(adapter.fromJson("{\"x\":{\"foo\":0,\"bar\":1}}"));
+   }
+
+   public void testCanOverrideDefault() throws IOException {
+      Gson gson = new GsonBuilder().registerTypeAdapterFactory(parameterizedCtorFactory).create();
+
+      assertEquals(new RenamedFields(0, 1), gson.fromJson("{\"foo\":0,\"_bar\":1}", RenamedFields.class));
+      assertEquals(gson.toJson(new RenamedFields(0, 1)), "{\"foo\":0,\"_bar\":1}");
+   }
+}
diff --git a/core/src/test/java/org/jclouds/json/internal/NamingStrategiesTest.java b/core/src/test/java/org/jclouds/json/internal/NamingStrategiesTest.java
new file mode 100644
index 0000000..1397775
--- /dev/null
+++ b/core/src/test/java/org/jclouds/json/internal/NamingStrategiesTest.java
@@ -0,0 +1,203 @@
+package org.jclouds.json.internal;
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.fail;
+
+import java.beans.ConstructorProperties;
+import java.lang.reflect.Constructor;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import org.jclouds.json.internal.NamingStrategies.AnnotationConstructorNamingStrategy;
+import org.jclouds.json.internal.NamingStrategies.AnnotationFieldNamingStrategy;
+import org.jclouds.json.internal.NamingStrategies.AnnotationOrNameFieldNamingStrategy;
+import org.jclouds.json.internal.NamingStrategies.ConstructorFieldNamingStrategy;
+import org.jclouds.json.internal.NamingStrategies.ExtractNamed;
+import org.jclouds.json.internal.NamingStrategies.ExtractSerializedName;
+import org.jclouds.json.internal.NamingStrategies.NameExtractor;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.gson.FieldNamingStrategy;
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * @author Adam Lowe
+ */
+@Test(testName = "NamingStrategiesTest")
+public final class NamingStrategiesTest {
+
+   private static class SimpleTest {
+      @SerializedName("aardvark")
+      private String a;
+      private String b;
+      @Named("cat")
+      private String c;
+      @Named("dog")
+      private String d;
+      
+      @ConstructorProperties({"aardvark", "bat", "coyote", "dog"})
+      private SimpleTest(String aa, String bb, String cc, @Named("dingo") String dd) {
+      }
+      
+      @Inject
+      private SimpleTest(@Named("aa") String aa, @Named("bb") String bb, @Named("cc") String cc, @Named("dd") String dd, boolean nothing) {         
+      }
+   }
+
+   private static class MixedConstructorTest {
+      @Inject
+      @ConstructorProperties("thiscanbeoverriddenbyNamed")
+      private MixedConstructorTest(@Named("aardvark") String aa, @Named("bat") String bb, @Named("cat") String cc, @Named("dog") String dd) {
+      }
+   }
+
+
+   public void testExtractSerializedName() throws Exception {
+      NameExtractor extractor = new ExtractSerializedName();
+      assertEquals(extractor.extractName(SimpleTest.class.getDeclaredField("a").getAnnotation(SerializedName.class)),
+            "aardvark");
+      try {
+         extractor.extractName(SimpleTest.class.getDeclaredField("b").getAnnotation(SerializedName.class));
+         fail();
+      } catch (NullPointerException e) {
+      }
+      try {
+         extractor.extractName(SimpleTest.class.getDeclaredField("c").getAnnotation(SerializedName.class));
+         fail();
+      } catch (NullPointerException e) {
+      }
+      try {
+         extractor.extractName(SimpleTest.class.getDeclaredField("d").getAnnotation(SerializedName.class));
+         fail();
+      } catch (NullPointerException e) {
+      }
+   }
+
+   public void testExtractNamed() throws Exception {
+      NameExtractor extractor = new ExtractNamed();
+      try {
+         extractor.extractName(SimpleTest.class.getDeclaredField("a").getAnnotation(Named.class));
+      } catch (NullPointerException e) {
+      }
+      try {
+         extractor.extractName(SimpleTest.class.getDeclaredField("b").getAnnotation(Named.class));
+         fail();
+      } catch (NullPointerException e) {
+      }
+      assertEquals(extractor.extractName(SimpleTest.class.getDeclaredField("c").getAnnotation(Named.class)),
+            "cat");
+      assertEquals(extractor.extractName(SimpleTest.class.getDeclaredField("d").getAnnotation(Named.class)),
+            "dog");
+   }
+   
+   public void testAnnotationFieldNamingStrategy() throws Exception {
+      FieldNamingStrategy strategy = new AnnotationFieldNamingStrategy(ImmutableSet.of(new ExtractNamed()));
+
+      assertNull(strategy.translateName(SimpleTest.class.getDeclaredField("a")));
+      assertNull(strategy.translateName(SimpleTest.class.getDeclaredField("b")));
+      assertEquals(strategy.translateName(SimpleTest.class.getDeclaredField("c")), "cat");
+      assertEquals(strategy.translateName(SimpleTest.class.getDeclaredField("d")), "dog");
+   }
+
+   public void testAnnotationOrNameFieldNamingStrategy() throws Exception {
+      FieldNamingStrategy strategy = new AnnotationOrNameFieldNamingStrategy(ImmutableSet.of(new ExtractNamed()));
+
+      assertEquals(strategy.translateName(SimpleTest.class.getDeclaredField("a")), "a");
+      assertEquals(strategy.translateName(SimpleTest.class.getDeclaredField("b")), "b");
+      assertEquals(strategy.translateName(SimpleTest.class.getDeclaredField("c")), "cat");
+      assertEquals(strategy.translateName(SimpleTest.class.getDeclaredField("d")), "dog");
+   }
+
+   public void testAnnotationConstructorFieldNamingStrategyCPAndNamed() throws Exception {
+      ConstructorFieldNamingStrategy strategy = new AnnotationConstructorNamingStrategy(
+            ImmutableSet.of(ConstructorProperties.class), ImmutableSet.of(new ExtractNamed()));
+
+      Constructor<? super SimpleTest> constructor = strategy.getDeserializationConstructor(SimpleTest.class);
+      assertNotNull(constructor);
+      assertEquals(constructor.getParameterTypes().length, 4);
+
+      assertEquals(strategy.translateName(constructor, 0), "aardvark");
+      assertEquals(strategy.translateName(constructor, 1), "bat");
+      assertEquals(strategy.translateName(constructor, 2), "coyote");
+      // Note: @Named overrides the ConstructorProperties setting
+      assertEquals(strategy.translateName(constructor, 3), "dingo");
+
+      Constructor<? super MixedConstructorTest> mixedCtor = strategy.getDeserializationConstructor(MixedConstructorTest.class);
+      assertNotNull(mixedCtor);
+      assertEquals(mixedCtor.getParameterTypes().length, 4);
+
+      assertEquals(strategy.translateName(mixedCtor, 0), "aardvark");
+      assertEquals(strategy.translateName(mixedCtor, 1), "bat");
+      assertEquals(strategy.translateName(mixedCtor, 2), "cat");
+      assertEquals(strategy.translateName(mixedCtor, 3), "dog");
+   }
+
+   public void testAnnotationConstructorFieldNamingStrategyCP() throws Exception {
+      ConstructorFieldNamingStrategy strategy = new AnnotationConstructorNamingStrategy(
+            ImmutableSet.of(ConstructorProperties.class), ImmutableSet.<NameExtractor>of());
+
+      Constructor<? super SimpleTest> constructor = strategy.getDeserializationConstructor(SimpleTest.class);
+      assertNotNull(constructor);
+      assertEquals(constructor.getParameterTypes().length, 4);
+
+      assertEquals(strategy.translateName(constructor, 0), "aardvark");
+      assertEquals(strategy.translateName(constructor, 1), "bat");
+      assertEquals(strategy.translateName(constructor, 2), "coyote");
+      assertEquals(strategy.translateName(constructor, 3), "dog");
+
+      Constructor<? super MixedConstructorTest> mixedCtor = strategy.getDeserializationConstructor(MixedConstructorTest.class);
+      assertNotNull(mixedCtor);
+      assertEquals(mixedCtor.getParameterTypes().length, 4);
+
+      assertEquals(strategy.translateName(mixedCtor, 0), "thiscanbeoverriddenbyNamed");
+      assertNull(strategy.translateName(mixedCtor, 1));
+      assertNull(strategy.translateName(mixedCtor, 2));
+      assertNull(strategy.translateName(mixedCtor, 3));
+   }
+   
+   public void testAnnotationConstructorFieldNamingStrategyInject() throws Exception {
+      ConstructorFieldNamingStrategy strategy = new AnnotationConstructorNamingStrategy(
+            ImmutableSet.of(Inject.class), ImmutableSet.of(new ExtractNamed()));
+
+      Constructor<? super SimpleTest> constructor = strategy.getDeserializationConstructor(SimpleTest.class);
+      assertNotNull(constructor);
+      assertEquals(constructor.getParameterTypes().length, 5);
+
+      assertEquals(strategy.translateName(constructor, 0), "aa");
+      assertEquals(strategy.translateName(constructor, 1), "bb");
+      assertEquals(strategy.translateName(constructor, 2), "cc");
+      assertEquals(strategy.translateName(constructor, 3), "dd");
+
+      Constructor<? super MixedConstructorTest> mixedCtor = strategy.getDeserializationConstructor(MixedConstructorTest.class);
+      assertNotNull(mixedCtor);
+      assertEquals(mixedCtor.getParameterTypes().length, 4);
+
+      assertEquals(strategy.translateName(mixedCtor, 0), "aardvark");
+      assertEquals(strategy.translateName(mixedCtor, 1), "bat");
+      assertEquals(strategy.translateName(mixedCtor, 2), "cat");
+      assertEquals(strategy.translateName(mixedCtor, 3), "dog");
+   }
+
+}
diff --git a/core/src/test/java/org/jclouds/location/suppliers/all/RegionToProviderTest.java b/core/src/test/java/org/jclouds/location/suppliers/all/RegionToProviderTest.java
new file mode 100644
index 0000000..af08109
--- /dev/null
+++ b/core/src/test/java/org/jclouds/location/suppliers/all/RegionToProviderTest.java
@@ -0,0 +1,72 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.location.suppliers.all;
+
+import static org.testng.Assert.assertEquals;
+
+import java.net.URI;
+import java.util.Map;
+import java.util.Set;
+
+import org.jclouds.domain.Location;
+import org.jclouds.domain.LocationBuilder;
+import org.jclouds.domain.LocationScope;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * 
+ * @author Adrian Cole
+ */
+@Test(groups = "unit", testName = "RegionToProviderTest")
+public class RegionToProviderTest {
+   Location provider = new LocationBuilder().scope(LocationScope.PROVIDER).id("servo").description("http://servo")
+            .iso3166Codes(ImmutableSet.of("US")).build();
+   JustProvider justProvider = new JustProvider("servo", Suppliers.ofInstance(URI.create("http://servo")), ImmutableSet.of("US"));
+
+   @Test
+   public void test() {
+      Supplier<Set<String>> regionIdsSupplier = Suppliers.<Set<String>>ofInstance(ImmutableSet.of("region1", "region2"));
+      Supplier<Map<String, Supplier<Set<String>>>> locationToIsoCodes = Suppliers.<Map<String, Supplier<Set<String>>>>ofInstance(
+               ImmutableMap.of(
+                        "servo", Suppliers.<Set<String>>ofInstance(ImmutableSet.of("US")),
+                        "region1", Suppliers.<Set<String>>ofInstance(ImmutableSet.of("US-CA")),
+                        "region2", Suppliers.<Set<String>>ofInstance(ImmutableSet.of("US-VA"))
+                        ));
+      RegionToProvider fn = new RegionToProvider(justProvider, regionIdsSupplier, locationToIsoCodes);
+      
+      assertEquals(fn.get(), ImmutableSet.of(
+               new LocationBuilder().scope(LocationScope.REGION).id("region1").description("region1").iso3166Codes(ImmutableSet.of("US-CA")).parent(provider).build(),
+               new LocationBuilder().scope(LocationScope.REGION).id("region2").description("region2").iso3166Codes(ImmutableSet.of("US-VA")).parent(provider).build()
+               ));
+   }
+   
+   @Test(expectedExceptions = IllegalStateException.class)
+   public void testWhenNoRegions() {
+      Supplier<Set<String>> regionIdsSupplier = Suppliers.<Set<String>>ofInstance(ImmutableSet.<String>of());
+      Supplier<Map<String, Supplier<Set<String>>>> locationToIsoCodes = Suppliers.<Map<String, Supplier<Set<String>>>>ofInstance(
+               ImmutableMap.<String, Supplier<Set<String>>>of());
+      RegionToProvider fn = new RegionToProvider(justProvider, regionIdsSupplier, locationToIsoCodes);
+      fn.get();
+   }
+}
diff --git a/core/src/test/java/org/jclouds/location/suppliers/all/ZoneToProviderTest.java b/core/src/test/java/org/jclouds/location/suppliers/all/ZoneToProviderTest.java
new file mode 100644
index 0000000..fa80aa3
--- /dev/null
+++ b/core/src/test/java/org/jclouds/location/suppliers/all/ZoneToProviderTest.java
@@ -0,0 +1,72 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.location.suppliers.all;
+
+import static org.testng.Assert.assertEquals;
+
+import java.net.URI;
+import java.util.Map;
+import java.util.Set;
+
+import org.jclouds.domain.Location;
+import org.jclouds.domain.LocationBuilder;
+import org.jclouds.domain.LocationScope;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * 
+ * @author Adrian Cole
+ */
+@Test(groups = "unit", testName = "ZoneToProviderTest")
+public class ZoneToProviderTest {
+   Location provider = new LocationBuilder().scope(LocationScope.PROVIDER).id("servo").description("http://servo")
+            .iso3166Codes(ImmutableSet.of("US")).build();
+   JustProvider justProvider = new JustProvider("servo", Suppliers.ofInstance(URI.create("http://servo")), ImmutableSet.of("US"));
+
+   @Test
+   public void test() {
+      Supplier<Set<String>> zoneIdsSupplier = Suppliers.<Set<String>>ofInstance(ImmutableSet.of("zone1", "zone2"));
+      Supplier<Map<String, Supplier<Set<String>>>> locationToIsoCodes = Suppliers.<Map<String, Supplier<Set<String>>>>ofInstance(
+               ImmutableMap.of(
+                        "servo", Suppliers.<Set<String>>ofInstance(ImmutableSet.of("US")),
+                        "zone1", Suppliers.<Set<String>>ofInstance(ImmutableSet.of("US-CA")),
+                        "zone2", Suppliers.<Set<String>>ofInstance(ImmutableSet.of("US-VA"))
+                        ));
+      ZoneToProvider fn = new ZoneToProvider(justProvider, zoneIdsSupplier, locationToIsoCodes);
+      
+      assertEquals(fn.get(), ImmutableSet.of(
+               new LocationBuilder().scope(LocationScope.ZONE).id("zone1").description("zone1").iso3166Codes(ImmutableSet.of("US-CA")).parent(provider).build(),
+               new LocationBuilder().scope(LocationScope.ZONE).id("zone2").description("zone2").iso3166Codes(ImmutableSet.of("US-VA")).parent(provider).build()
+               ));
+   }
+   
+   @Test(expectedExceptions = IllegalStateException.class)
+   public void testWhenNoZones() {
+      Supplier<Set<String>> zoneIdsSupplier = Suppliers.<Set<String>>ofInstance(ImmutableSet.<String>of());
+      Supplier<Map<String, Supplier<Set<String>>>> locationToIsoCodes = Suppliers.<Map<String, Supplier<Set<String>>>>ofInstance(
+               ImmutableMap.<String, Supplier<Set<String>>>of());
+      ZoneToProvider fn = new ZoneToProvider(justProvider, zoneIdsSupplier, locationToIsoCodes);
+      fn.get();
+   }
+}
diff --git a/core/src/test/java/org/jclouds/location/suppliers/implicit/FirstRegionTest.java b/core/src/test/java/org/jclouds/location/suppliers/implicit/FirstRegionTest.java
new file mode 100644
index 0000000..6a37593
--- /dev/null
+++ b/core/src/test/java/org/jclouds/location/suppliers/implicit/FirstRegionTest.java
@@ -0,0 +1,70 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.location.suppliers.implicit;
+
+import static org.testng.Assert.assertEquals;
+
+import java.util.NoSuchElementException;
+import java.util.Set;
+
+import org.jclouds.domain.Location;
+import org.jclouds.domain.LocationBuilder;
+import org.jclouds.domain.LocationScope;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * Tests behavior of {@code FirstRegion}
+ * 
+ * @author Adrian Cole
+ */
+@Test(groups = "unit", testName = "FirstRegionTest")
+public class FirstRegionTest {
+   Location provider = new LocationBuilder().scope(LocationScope.PROVIDER).id("servo").description("http://servo")
+         .build();
+   Location region = new LocationBuilder().scope(LocationScope.REGION).id("servo-r").description("http://r.servo")
+         .parent(provider).build();
+   Location zone = new LocationBuilder().scope(LocationScope.ZONE).id("servo-z").description("http://z.r.servo")
+         .parent(region).build();
+
+   @Test
+   public void testDidntFindRegionThrowsNSEEWithReasonableMessage() {
+      Supplier<Set<? extends Location>> supplier = Suppliers.<Set<? extends Location>> ofInstance(ImmutableSet
+            .<Location> of(provider, zone));
+      FirstRegion fn = new FirstRegion(supplier);
+
+      try {
+         fn.get();
+         assert false;
+      } catch (NoSuchElementException e) {
+         assertEquals(e.getMessage(), "none to of the locations are scope REGION: [servo:PROVIDER, servo-z:ZONE]");
+      }
+   }
+
+   @Test
+   public void testFirstRegion() {
+      Supplier<Set<? extends Location>> supplier = Suppliers.<Set<? extends Location>> ofInstance(ImmutableSet
+            .<Location> of(provider, region, zone));
+      FirstRegion fn = new FirstRegion(supplier);
+      assertEquals(fn.get(), region);
+   }
+}
diff --git a/core/src/test/java/org/jclouds/location/suppliers/implicit/FirstZoneTest.java b/core/src/test/java/org/jclouds/location/suppliers/implicit/FirstZoneTest.java
new file mode 100644
index 0000000..d9f523d
--- /dev/null
+++ b/core/src/test/java/org/jclouds/location/suppliers/implicit/FirstZoneTest.java
@@ -0,0 +1,70 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.location.suppliers.implicit;
+
+import static org.testng.Assert.assertEquals;
+
+import java.util.NoSuchElementException;
+import java.util.Set;
+
+import org.jclouds.domain.Location;
+import org.jclouds.domain.LocationBuilder;
+import org.jclouds.domain.LocationScope;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * Tests behavior of {@code FirstZone}
+ * 
+ * @author Adrian Cole
+ */
+@Test(groups = "unit", testName = "FirstZoneTest")
+public class FirstZoneTest {
+   Location provider = new LocationBuilder().scope(LocationScope.PROVIDER).id("servo").description("http://servo")
+         .build();
+   Location region = new LocationBuilder().scope(LocationScope.REGION).id("servo-r").description("http://r.servo")
+         .parent(provider).build();
+   Location zone = new LocationBuilder().scope(LocationScope.ZONE).id("servo-z").description("http://z.r.servo")
+         .parent(region).build();
+
+   @Test
+   public void testDidntFindZoneThrowsNSEEWithReasonableMessage() {
+      Supplier<Set<? extends Location>> supplier = Suppliers.<Set<? extends Location>> ofInstance(ImmutableSet
+            .<Location> of(provider, region));
+      FirstZone fn = new FirstZone(supplier);
+
+      try {
+         fn.get();
+         assert false;
+      } catch (NoSuchElementException e) {
+         assertEquals(e.getMessage(), "none to of the locations are scope ZONE: [servo:PROVIDER, servo-r:REGION]");
+      }
+   }
+
+   @Test
+   public void testFirstZone() {
+      Supplier<Set<? extends Location>> supplier = Suppliers.<Set<? extends Location>> ofInstance(ImmutableSet
+            .<Location> of(provider, region, zone));
+      FirstZone fn = new FirstZone(supplier);
+      assertEquals(fn.get(), zone);
+   }
+}
diff --git a/core/src/test/java/org/jclouds/rest/suppliers/MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplierTest.java b/core/src/test/java/org/jclouds/rest/suppliers/MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplierTest.java
new file mode 100644
index 0000000..32114b4
--- /dev/null
+++ b/core/src/test/java/org/jclouds/rest/suppliers/MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplierTest.java
@@ -0,0 +1,300 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.rest.suppliers;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertSame;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.jclouds.rest.AuthorizationException;
+import org.jclouds.rest.suppliers.MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.SetAndThrowAuthorizationExceptionSupplierBackedLoader;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Function;
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
+
+/**
+ * 
+ * @author Adrian Cole
+ */
+@SuppressWarnings({ "unchecked", "rawtypes" })
+@Test(groups = "unit", testName = "MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplierTest")
+public class MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplierTest {
+   @Test
+   public void testLoaderNormal() {
+      AtomicReference<AuthorizationException> authException = new AtomicReference<AuthorizationException>();
+      assertEquals(new SetAndThrowAuthorizationExceptionSupplierBackedLoader<String>(Suppliers.ofInstance("foo"),
+               authException).load("KEY"), "foo");
+      assertEquals(authException.get(), null);
+   }
+
+   @Test(expectedExceptions = AuthorizationException.class)
+   public void testLoaderThrowsAuthorizationExceptionAndAlsoSetsExceptionType() {
+      AtomicReference<AuthorizationException> authException = new AtomicReference<AuthorizationException>();
+      try {
+         new SetAndThrowAuthorizationExceptionSupplierBackedLoader<String>(new Supplier<String>() {
+
+            @Override
+            public String get() {
+               throw new AuthorizationException();
+            }
+         }, authException).load("KEY");
+      } finally {
+         assertEquals(authException.get().getClass(), AuthorizationException.class);
+      }
+   }
+
+   @Test(expectedExceptions = AuthorizationException.class)
+   public void testLoaderThrowsAuthorizationExceptionAndAlsoSetsExceptionTypeWhenNested() {
+      AtomicReference<AuthorizationException> authException = new AtomicReference<AuthorizationException>();
+      try {
+         new SetAndThrowAuthorizationExceptionSupplierBackedLoader<String>(new Supplier<String>() {
+
+            @Override
+            public String get() {
+               throw new RuntimeException(new ExecutionException(new AuthorizationException()));
+            }
+         }, authException).load("KEY");
+      } finally {
+         assertEquals(authException.get().getClass(), AuthorizationException.class);
+      }
+   }
+
+   @Test(expectedExceptions = RuntimeException.class)
+   public void testLoaderThrowsOriginalExceptionAndAlsoSetsExceptionTypeWhenNestedAndNotAuthorizationException() {
+      AtomicReference<AuthorizationException> authException = new AtomicReference<AuthorizationException>();
+      try {
+         new SetAndThrowAuthorizationExceptionSupplierBackedLoader<String>(new Supplier<String>() {
+
+            @Override
+            public String get() {
+               throw new RuntimeException(new IllegalArgumentException("foo"));
+            }
+         }, authException).load("KEY");
+      } finally {
+         assertEquals(authException.get().getClass(), RuntimeException.class);
+      }
+   }
+
+   @Test
+   public void testMemoizeKeepsValueForFullDurationWhenDelegateCallIsSlow() {
+      final long SLEEP_TIME = 250;
+      final long EXPIRATION_TIME = 200;
+
+      Supplier<Integer> slowSupplier = new CountingSupplier() {
+         private static final long serialVersionUID = 1L;
+
+         @Override
+         public Integer get() {
+            try {
+               Thread.sleep(SLEEP_TIME);
+            } catch (InterruptedException e) {
+               Thread.currentThread().interrupt();
+            }
+            return super.get();
+         }
+      };
+
+      Supplier<Integer> memoizedSupplier = new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier(
+               new AtomicReference<AuthorizationException>(), slowSupplier, EXPIRATION_TIME, TimeUnit.MILLISECONDS);
+
+      assertEquals(memoizedSupplier.get(), (Integer) 10);
+      assertEquals(memoizedSupplier.get(), (Integer) 10);
+   }
+
+   // =================================
+   //
+   // TODO Everything below this point is taken from SuppliersTest, to test our version of the
+   // Suppliers2.memoizeWithExpiration
+   // It should be deleted when we can switch back to using the google
+   // Supplier.memoizeWithExpiration.
+
+   private static class CountingSupplier implements Supplier<Integer>, Serializable {
+      private static final long serialVersionUID = 0L;
+      transient int calls = 0;
+
+      @Override
+      public Integer get() {
+         calls++;
+         return calls * 10;
+      }
+   }
+
+   @Test
+   public void testMemoizeWithExpiration() throws InterruptedException {
+      CountingSupplier countingSupplier = new CountingSupplier();
+
+      Supplier<Integer> memoizedSupplier = new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier(
+               new AtomicReference<AuthorizationException>(), countingSupplier, 75, TimeUnit.MILLISECONDS);
+
+      checkExpiration(countingSupplier, memoizedSupplier);
+   }
+
+   @Test
+   public void testMemoizeWithExpirationSerialized() throws InterruptedException {
+      CountingSupplier countingSupplier = new CountingSupplier();
+
+      Supplier<Integer> memoizedSupplier = new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier(
+               new AtomicReference<AuthorizationException>(), countingSupplier, 75, TimeUnit.MILLISECONDS);
+      // Calls to the original memoized supplier shouldn't affect its copy.
+      memoizedSupplier.get();
+
+      Supplier<Integer> copy = reserialize(memoizedSupplier);
+      memoizedSupplier.get();
+
+      CountingSupplier countingCopy = (CountingSupplier) ((MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier) copy)
+               .delegate();
+      checkExpiration(countingCopy, copy);
+   }
+
+   private void checkExpiration(CountingSupplier countingSupplier, Supplier<Integer> memoizedSupplier)
+            throws InterruptedException {
+      // the underlying supplier hasn't executed yet
+      assertEquals(0, countingSupplier.calls);
+
+      assertEquals(10, (int) memoizedSupplier.get());
+      // now it has
+      assertEquals(1, countingSupplier.calls);
+
+      assertEquals(10, (int) memoizedSupplier.get());
+      // it still should only have executed once due to memoization
+      assertEquals(1, countingSupplier.calls);
+
+      Thread.sleep(150);
+
+      assertEquals(20, (int) memoizedSupplier.get());
+      // old value expired
+      assertEquals(2, countingSupplier.calls);
+
+      assertEquals(20, (int) memoizedSupplier.get());
+      // it still should only have executed twice due to memoization
+      assertEquals(2, countingSupplier.calls);
+   }
+
+   @Test
+   public void testExpiringMemoizedSupplierThreadSafe() throws Throwable {
+      Function<Supplier<Boolean>, Supplier<Boolean>> memoizer = new Function<Supplier<Boolean>, Supplier<Boolean>>() {
+         @Override
+         public Supplier<Boolean> apply(Supplier<Boolean> supplier) {
+            return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier(
+                     new AtomicReference<AuthorizationException>(), supplier, Long.MAX_VALUE, TimeUnit.NANOSECONDS);
+         }
+      };
+      supplierThreadSafe(memoizer);
+   }
+
+   private void supplierThreadSafe(Function<Supplier<Boolean>, Supplier<Boolean>> memoizer) throws Throwable {
+      final AtomicInteger count = new AtomicInteger(0);
+      final AtomicReference<Throwable> thrown = new AtomicReference<Throwable>(null);
+      final int numThreads = 3;
+      final Thread[] threads = new Thread[numThreads];
+      final long timeout = TimeUnit.SECONDS.toNanos(60);
+
+      final Supplier<Boolean> supplier = new Supplier<Boolean>() {
+         boolean isWaiting(Thread thread) {
+            switch (thread.getState()) {
+               case BLOCKED:
+               case WAITING:
+               case TIMED_WAITING:
+                  return true;
+               default:
+                  return false;
+            }
+         }
+
+         int waitingThreads() {
+            int waitingThreads = 0;
+            for (Thread thread : threads) {
+               if (isWaiting(thread)) {
+                  waitingThreads++;
+               }
+            }
+            return waitingThreads;
+         }
+
+         @Override
+         public Boolean get() {
+            // Check that this method is called exactly once, by the first
+            // thread to synchronize.
+            long t0 = System.nanoTime();
+            while (waitingThreads() != numThreads - 1) {
+               if (System.nanoTime() - t0 > timeout) {
+                  thrown.set(new TimeoutException("timed out waiting for other threads to block"
+                           + " synchronizing on supplier"));
+                  break;
+               }
+               Thread.yield();
+            }
+            count.getAndIncrement();
+            return Boolean.TRUE;
+         }
+      };
+
+      final Supplier<Boolean> memoizedSupplier = memoizer.apply(supplier);
+
+      for (int i = 0; i < numThreads; i++) {
+         threads[i] = new Thread() {
+            @Override
+            public void run() {
+               assertSame(Boolean.TRUE, memoizedSupplier.get());
+            }
+         };
+      }
+      for (Thread t : threads) {
+         t.start();
+      }
+      for (Thread t : threads) {
+         t.join();
+      }
+
+      if (thrown.get() != null) {
+         throw thrown.get();
+      }
+      assertEquals(1, count.get());
+   }
+
+   // Taken from com.google.common.testing.SerializableTester
+   private static <T> T reserialize(T object) {
+      checkNotNull(object);
+      ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+      try {
+         ObjectOutputStream out = new ObjectOutputStream(bytes);
+         out.writeObject(object);
+         ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bytes.toByteArray()));
+         return (T) in.readObject();
+      } catch (IOException e) {
+         throw new RuntimeException(e);
+      } catch (ClassNotFoundException e) {
+         throw new RuntimeException(e);
+      }
+   }
+}
diff --git a/core/src/test/java/org/jclouds/rest/suppliers/SetAndThrowAuthorizationExceptionSupplierTest.java b/core/src/test/java/org/jclouds/rest/suppliers/SetAndThrowAuthorizationExceptionSupplierTest.java
index 3163750..a94f094 100644
--- a/core/src/test/java/org/jclouds/rest/suppliers/SetAndThrowAuthorizationExceptionSupplierTest.java
+++ b/core/src/test/java/org/jclouds/rest/suppliers/SetAndThrowAuthorizationExceptionSupplierTest.java
@@ -30,11 +30,10 @@
 import com.google.common.base.Suppliers;
 
 /**
- * Tests behavior of {@code SetAndThrowAuthorizationExceptionSupplier}
  * 
  * @author Adrian Cole
  */
-@Test(groups = "unit")
+@Test(groups = "unit", testName = "MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplierTest")
 public class SetAndThrowAuthorizationExceptionSupplierTest {
    @Test
    public void testNormal() {
diff --git a/core/src/test/java/org/jclouds/util/Suppliers2Test.java b/core/src/test/java/org/jclouds/util/Suppliers2Test.java
index 7e85f90..6ff27cc 100644
--- a/core/src/test/java/org/jclouds/util/Suppliers2Test.java
+++ b/core/src/test/java/org/jclouds/util/Suppliers2Test.java
@@ -56,199 +56,4 @@
       assertEquals(Suppliers2.ofInstanceFunction().apply("foo").get(), "foo");
    }
    
-   @Test
-   public void testMemoizeKeepsValueForFullDurationWhenDelegateCallIsSlow() {
-      final long SLEEP_TIME = 250;
-      final long EXPIRATION_TIME = 200;
-      
-      Supplier<Integer> slowSupplier = new CountingSupplier() {
-         private static final long serialVersionUID = 1L;
-         
-         @Override public Integer get() {
-           try {
-              Thread.sleep(SLEEP_TIME);
-           } catch (InterruptedException e) {
-              Thread.currentThread().interrupt();
-           }
-           return super.get();
-         }
-      };
-      
-      Supplier<Integer> memoizedSupplier = Suppliers2.memoizeWithExpirationOnAbsoluteInterval(
-               slowSupplier, EXPIRATION_TIME, TimeUnit.MILLISECONDS);
-      
-      assertEquals(memoizedSupplier.get(), (Integer)10);
-      assertEquals(memoizedSupplier.get(), (Integer)10);
-   }
-   
-   // =================================
-   // 
-   // TODO Everything below this point is taken from SuppliersTest, to test our version of the Suppliers2.memoizeWithExpiration
-   // It should be deleted when we can switch back to using the google Supplier.memoizeWithExpiration. 
-   
-    private static class CountingSupplier implements Supplier<Integer>, Serializable {
-      private static final long serialVersionUID = 0L;
-      transient int calls = 0;
-      @Override
-      public Integer get() {
-        calls++;
-        return calls * 10;
-      }
-    }
-
-    @Test
-    public void testMemoizeWithExpiration() throws InterruptedException {
-      CountingSupplier countingSupplier = new CountingSupplier();
-
-      Supplier<Integer> memoizedSupplier = Suppliers2.memoizeWithExpirationOnAbsoluteInterval(
-          countingSupplier, 75, TimeUnit.MILLISECONDS);
-
-      checkExpiration(countingSupplier, memoizedSupplier);
-    }
-
-    @Test
-    public void testMemoizeWithExpirationSerialized()
-        throws InterruptedException {
-      CountingSupplier countingSupplier = new CountingSupplier();
-
-      Supplier<Integer> memoizedSupplier = Suppliers2.memoizeWithExpirationOnAbsoluteInterval(
-          countingSupplier, 75, TimeUnit.MILLISECONDS);
-      // Calls to the original memoized supplier shouldn't affect its copy.
-      memoizedSupplier.get();
-
-      Supplier<Integer> copy = reserialize(memoizedSupplier);
-      memoizedSupplier.get();
-
-      CountingSupplier countingCopy = (CountingSupplier)
-          ((Suppliers2.ExpiringMemoizingSupplier<Integer>) copy).delegate;
-      checkExpiration(countingCopy, copy);
-    }
-
-    private void checkExpiration(
-        CountingSupplier countingSupplier, Supplier<Integer> memoizedSupplier)
-        throws InterruptedException {
-      // the underlying supplier hasn't executed yet
-      assertEquals(0, countingSupplier.calls);
-
-      assertEquals(10, (int) memoizedSupplier.get());
-      // now it has
-      assertEquals(1, countingSupplier.calls);
-
-      assertEquals(10, (int) memoizedSupplier.get());
-      // it still should only have executed once due to memoization
-      assertEquals(1, countingSupplier.calls);
-
-      Thread.sleep(150);
-
-      assertEquals(20, (int) memoizedSupplier.get());
-      // old value expired
-      assertEquals(2, countingSupplier.calls);
-
-      assertEquals(20, (int) memoizedSupplier.get());
-      // it still should only have executed twice due to memoization
-      assertEquals(2, countingSupplier.calls);
-    }
-
-    @Test
-    public void testExpiringMemoizedSupplierThreadSafe() throws Throwable {
-      Function<Supplier<Boolean>, Supplier<Boolean>> memoizer =
-          new Function<Supplier<Boolean>, Supplier<Boolean>>() {
-        @Override public Supplier<Boolean> apply(Supplier<Boolean> supplier) {
-          return Suppliers2.memoizeWithExpirationOnAbsoluteInterval(
-              supplier, Long.MAX_VALUE, TimeUnit.NANOSECONDS);
-        }
-      };
-      testSupplierThreadSafe(memoizer);
-    }
-
-    public void testSupplierThreadSafe(
-        Function<Supplier<Boolean>, Supplier<Boolean>> memoizer)
-        throws Throwable {
-      final AtomicInteger count = new AtomicInteger(0);
-      final AtomicReference<Throwable> thrown =
-          new AtomicReference<Throwable>(null);
-      final int numThreads = 3;
-      final Thread[] threads = new Thread[numThreads];
-      final long timeout = TimeUnit.SECONDS.toNanos(60);
-
-      final Supplier<Boolean> supplier = new Supplier<Boolean>() {
-        boolean isWaiting(Thread thread) {
-          switch (thread.getState()) {
-            case BLOCKED:
-            case WAITING:
-            case TIMED_WAITING:
-            return true;
-            default:
-            return false;
-          }
-        }
-
-        int waitingThreads() {
-          int waitingThreads = 0;
-          for (Thread thread : threads) {
-            if (isWaiting(thread)) {
-              waitingThreads++;
-            }
-          }
-          return waitingThreads;
-        }
-
-        @Override
-        public Boolean get() {
-          // Check that this method is called exactly once, by the first
-          // thread to synchronize.
-          long t0 = System.nanoTime();
-          while (waitingThreads() != numThreads - 1) {
-            if (System.nanoTime() - t0 > timeout) {
-              thrown.set(new TimeoutException(
-                  "timed out waiting for other threads to block" +
-                  " synchronizing on supplier"));
-              break;
-            }
-            Thread.yield();
-          }
-          count.getAndIncrement();
-          return Boolean.TRUE;
-        }
-      };
-
-      final Supplier<Boolean> memoizedSupplier = memoizer.apply(supplier);
-
-      for (int i = 0; i < numThreads; i++) {
-        threads[i] = new Thread() {
-          @Override public void run() {
-            assertSame(Boolean.TRUE, memoizedSupplier.get());
-          }
-        };
-      }
-      for (Thread t : threads) {
-        t.start();
-      }
-      for (Thread t : threads) {
-        t.join();
-      }
-
-      if (thrown.get() != null) {
-        throw thrown.get();
-      }
-      assertEquals(1, count.get());
-    }
-    
-    // Taken from com.google.common.testing.SerializableTester
-    @SuppressWarnings("unchecked")
-    private static <T> T reserialize(T object) {
-      checkNotNull(object);
-      ByteArrayOutputStream bytes = new ByteArrayOutputStream();
-      try {
-        ObjectOutputStream out = new ObjectOutputStream(bytes);
-        out.writeObject(object);
-        ObjectInputStream in = new ObjectInputStream(
-            new ByteArrayInputStream(bytes.toByteArray()));
-        return (T) in.readObject();
-      } catch (IOException e) {
-        throw new RuntimeException(e);
-      } catch (ClassNotFoundException e) {
-        throw new RuntimeException(e);
-      }
-    }
 }
diff --git a/drivers/sshj/pom.xml b/drivers/sshj/pom.xml
index e762305..46faacc 100644
--- a/drivers/sshj/pom.xml
+++ b/drivers/sshj/pom.xml
@@ -52,7 +52,7 @@
         <test.ssh.keyfile />
 
         <jclouds.osgi.export>org.jclouds.sshj*;version="${project.version}"</jclouds.osgi.export>
-        <jclouds.osgi.import>org.jclouds*;version="${project.version}",*</jclouds.osgi.import>
+        <jclouds.osgi.import>org.jclouds*;version="${project.version}",org.apache.commons.io.input;version="[1.4,3)",*</jclouds.osgi.import>
     </properties>
 
     <dependencies>
@@ -97,7 +97,7 @@
         <dependency>
             <groupId>commons-io</groupId>
             <artifactId>commons-io</artifactId>
-            <version>1.4</version>
+            <version>2.0</version>
         </dependency>
     </dependencies>
 
diff --git a/labs/aws-iam/pom.xml b/labs/aws-iam/pom.xml
new file mode 100644
index 0000000..fc9f282
--- /dev/null
+++ b/labs/aws-iam/pom.xml
@@ -0,0 +1,108 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Licensed to jclouds, Inc. (jclouds) under one or more
+    contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  jclouds licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.jclouds</groupId>
+        <artifactId>jclouds-project</artifactId>
+        <version>1.5.0-SNAPSHOT</version>
+        <relativePath>../../project/pom.xml</relativePath>
+    </parent>
+    <groupId>org.jclouds.labs</groupId>
+    <artifactId>aws-iam</artifactId>
+    <name>jclouds Amazon Identity and Access Management (IAM) provider</name>
+    <description>Identity and Access Management (IAM) to Amazon Web Services</description>
+    <packaging>bundle</packaging>
+
+    <properties>
+        <test.aws-iam.endpoint>https://iam.amazonaws.com</test.aws-iam.endpoint>
+        <test.aws-iam.api-version>2010-05-08</test.aws-iam.api-version>
+        <test.aws-iam.build-version></test.aws-iam.build-version>
+        <test.aws-iam.identity>${test.aws.identity}</test.aws-iam.identity>
+        <test.aws-iam.credential>${test.aws.credential}</test.aws-iam.credential>
+
+        <jclouds.osgi.export>org.jclouds.aws.iam*;version="${project.version}"</jclouds.osgi.export>
+        <jclouds.osgi.import>org.jclouds*;version="${project.version}",*</jclouds.osgi.import>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.jclouds.labs</groupId>
+            <artifactId>iam</artifactId>
+            <version>${project.version}</version>
+            <type>jar</type>
+        </dependency>
+        <dependency>
+            <groupId>org.jclouds.labs</groupId>
+            <artifactId>iam</artifactId>
+            <version>${project.version}</version>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.jclouds</groupId>
+            <artifactId>jclouds-core</artifactId>
+            <version>${project.version}</version>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.jclouds.driver</groupId>
+            <artifactId>jclouds-log4j</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <profiles>
+        <profile>
+            <id>live</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-surefire-plugin</artifactId>
+                        <executions>
+                            <execution>
+                                <id>integration</id>
+                                <phase>integration-test</phase>
+                                <goals>
+                                    <goal>test</goal>
+                                </goals>
+                                <configuration>
+                                    <systemPropertyVariables>
+                                        <test.aws-iam.endpoint>${test.aws-iam.endpoint}</test.aws-iam.endpoint>
+                                        <test.aws-iam.api-version>${test.aws-iam.api-version}</test.aws-iam.api-version>
+                                        <test.aws-iam.build-version>${test.aws-iam.build-version}</test.aws-iam.build-version>
+                                        <test.aws-iam.identity>${test.aws-iam.identity}</test.aws-iam.identity>
+                                        <test.aws-iam.credential>${test.aws-iam.credential}</test.aws-iam.credential>
+                                    </systemPropertyVariables>
+                                </configuration>
+                            </execution>
+                        </executions>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/labs/aws-iam/src/main/java/org/jclouds/aws/iam/AWSIAMProviderMetadata.java b/labs/aws-iam/src/main/java/org/jclouds/aws/iam/AWSIAMProviderMetadata.java
new file mode 100644
index 0000000..8ed1444
--- /dev/null
+++ b/labs/aws-iam/src/main/java/org/jclouds/aws/iam/AWSIAMProviderMetadata.java
@@ -0,0 +1,87 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.aws.iam;
+
+import java.net.URI;
+import java.util.Properties;
+
+import org.jclouds.iam.IAMApiMetadata;
+import org.jclouds.providers.ProviderMetadata;
+import org.jclouds.providers.internal.BaseProviderMetadata;
+
+/**
+ * Implementation of @ link org.jclouds.types.ProviderMetadata} for Amazon's IAM
+ * provider.
+*
+* @author Adrian Cole
+*/
+public class AWSIAMProviderMetadata extends BaseProviderMetadata {
+   
+   /** The serialVersionUID */
+   private static final long serialVersionUID = 2394954723306943404L;
+
+   public static Builder builder() {
+      return new Builder();
+   }
+
+   @Override
+   public Builder toBuilder() {
+      return builder().fromProviderMetadata(this);
+   }
+   
+   public AWSIAMProviderMetadata() {
+      super(builder());
+   }
+
+   public AWSIAMProviderMetadata(Builder builder) {
+      super(builder);
+   }
+
+   public static Properties defaultProperties() {
+      Properties properties = new Properties();
+      return properties;
+   }
+   
+   public static class Builder extends BaseProviderMetadata.Builder {
+
+      protected Builder(){
+         id("aws-iam")
+         .name("Amazon IAM")
+         .endpoint("https://iam.amazonaws.com")
+         .homepage(URI.create("http://aws.amazon.com/iam"))
+         .console(URI.create("https://console.aws.amazon.com/iam/home"))
+         .linkedServices("aws-ec2","aws-elb", "aws-cloudwatch", "aws-s3", "aws-simpledb")
+         .iso3166Codes("US-VA", "US-CA", "BR-SP", "US-OR", "IE", "SG", "JP-13")
+         .apiMetadata(new IAMApiMetadata())
+         .defaultProperties(AWSIAMProviderMetadata.defaultProperties());
+      }
+
+      @Override
+      public AWSIAMProviderMetadata build() {
+         return new AWSIAMProviderMetadata(this);
+      }
+      
+      @Override
+      public Builder fromProviderMetadata(ProviderMetadata in) {
+         super.fromProviderMetadata(in);
+         return this;
+      }
+
+   }
+}
\ No newline at end of file
diff --git a/labs/aws-iam/src/main/resources/META-INF/services/org.jclouds.providers.ProviderMetadata b/labs/aws-iam/src/main/resources/META-INF/services/org.jclouds.providers.ProviderMetadata
new file mode 100644
index 0000000..9f5ac03
--- /dev/null
+++ b/labs/aws-iam/src/main/resources/META-INF/services/org.jclouds.providers.ProviderMetadata
@@ -0,0 +1 @@
+org.jclouds.aws.iam.AWSIAMProviderMetadata
diff --git a/labs/aws-iam/src/test/java/org/jclouds/aws/iam/AWSIAMClientLiveTest.java b/labs/aws-iam/src/test/java/org/jclouds/aws/iam/AWSIAMClientLiveTest.java
new file mode 100644
index 0000000..c31f58f
--- /dev/null
+++ b/labs/aws-iam/src/test/java/org/jclouds/aws/iam/AWSIAMClientLiveTest.java
@@ -0,0 +1,35 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.aws.iam;
+
+import org.jclouds.iam.IAMClientLiveTest;
+import org.testng.annotations.Test;
+
+/**
+ * Tests behavior of {@code IAMClient}
+ * 
+ * @author Adrian Cole
+ */
+@Test(groups = "live", singleThreaded = true, testName = "AWSIAMClientLiveTest")
+public class AWSIAMClientLiveTest extends IAMClientLiveTest {
+   public AWSIAMClientLiveTest() {
+      provider = "aws-iam";
+   }
+
+}
diff --git a/labs/aws-iam/src/test/java/org/jclouds/aws/iam/AWSIAMProviderTest.java b/labs/aws-iam/src/test/java/org/jclouds/aws/iam/AWSIAMProviderTest.java
new file mode 100644
index 0000000..577a23e
--- /dev/null
+++ b/labs/aws-iam/src/test/java/org/jclouds/aws/iam/AWSIAMProviderTest.java
@@ -0,0 +1,37 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.aws.iam;
+
+import org.jclouds.aws.iam.AWSIAMProviderMetadata;
+import org.jclouds.iam.IAMApiMetadata;
+import org.jclouds.providers.internal.BaseProviderMetadataTest;
+import org.testng.annotations.Test;
+
+/**
+ * The AWSIAMProviderTest tests the org.jclouds.providers.AWSIAMProvider class.
+ * 
+ * @author Adrian Cole
+ */
+@Test(groups = "unit", testName = "AWSIAMProviderTest")
+public class AWSIAMProviderTest extends BaseProviderMetadataTest {
+
+   public AWSIAMProviderTest() {
+      super(new AWSIAMProviderMetadata(), new IAMApiMetadata());
+   }
+}
diff --git a/labs/aws-iam/src/test/java/org/jclouds/aws/iam/features/AWSUserClientLiveTest.java b/labs/aws-iam/src/test/java/org/jclouds/aws/iam/features/AWSUserClientLiveTest.java
new file mode 100644
index 0000000..d298f7b
--- /dev/null
+++ b/labs/aws-iam/src/test/java/org/jclouds/aws/iam/features/AWSUserClientLiveTest.java
@@ -0,0 +1,32 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.aws.iam.features;
+
+import org.jclouds.iam.features.UserClientLiveTest;
+import org.testng.annotations.Test;
+
+/**
+ * @author Adrian Cole
+ */
+@Test(groups = "live", testName = "AWSUserClientLiveTest")
+public class AWSUserClientLiveTest extends UserClientLiveTest {
+   public AWSUserClientLiveTest() {
+      provider = "aws-iam";
+   }
+}
diff --git a/labs/elb/pom.xml b/labs/elb/pom.xml
index f169418..6a83ac9 100644
--- a/labs/elb/pom.xml
+++ b/labs/elb/pom.xml
@@ -36,8 +36,8 @@
     <properties>
         <test.elb.zone>us-east-1a</test.elb.zone>
         <test.elb.endpoint>https://elasticloadbalancing.us-east-1.amazonaws.com</test.elb.endpoint>
-        <test.elb.api-version>2010-07-01</test.elb.api-version>
-        <test.elb.build-version />
+        <test.elb.api-version>2012-06-01</test.elb.api-version>
+        <test.elb.build-version></test.elb.build-version>
         <test.elb.identity>${test.aws.identity}</test.elb.identity>
         <test.elb.credential>${test.aws.credential}</test.elb.credential>
         <test.elb.compute.provider>aws-ec2</test.elb.compute.provider>
@@ -99,10 +99,16 @@
         </dependency>
         <dependency>
             <groupId>org.jclouds.driver</groupId>
-            <artifactId>jclouds-log4j</artifactId>
+            <artifactId>jclouds-slf4j</artifactId>
             <version>${project.version}</version>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-classic</artifactId>
+            <version>1.0.0</version>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
     <profiles>
diff --git a/labs/elb/src/main/java/org/jclouds/elb/ELB.java b/labs/elb/src/main/java/org/jclouds/elb/ELB.java
new file mode 100644
index 0000000..f6ef6e0
--- /dev/null
+++ b/labs/elb/src/main/java/org/jclouds/elb/ELB.java
@@ -0,0 +1,47 @@
+package org.jclouds.elb;
+
+import org.jclouds.collect.PaginatedSet;
+import org.jclouds.collect.PaginatedSets;
+import org.jclouds.elb.domain.LoadBalancer;
+import org.jclouds.elb.features.LoadBalancerClient;
+import org.jclouds.elb.options.ListLoadBalancersOptions;
+
+import com.google.common.base.Function;
+
+/**
+ * Utilities for using ELB.
+ * 
+ * @author Adrian Cole
+ */
+public class ELB {
+
+   /**
+    * List loadBalancers based on the criteria in the {@link ListLoadBalancersOptions} passed in.
+    * 
+    * @param loadBalancerClient
+    *           the {@link LoadBalancerClient} to use for the request
+    * @param options
+    *           the {@link ListLoadBalancersOptions} describing the ListLoadBalancers request
+    * 
+    * @return iterable of loadBalancers fitting the criteria
+    */
+   public static Iterable<LoadBalancer> listLoadBalancers(final LoadBalancerClient loadBalancerClient, final ListLoadBalancersOptions options) {
+      return PaginatedSets.lazyContinue(loadBalancerClient.list(options), new Function<String, PaginatedSet<LoadBalancer>>() {
+
+         @Override
+         public PaginatedSet<LoadBalancer> apply(String input) {
+            return loadBalancerClient.list(options.clone().marker(input));
+         }
+
+         @Override
+         public String toString() {
+            return "listLoadBalancers(" + options + ")";
+         }
+      });
+   }
+   
+   public static Iterable<LoadBalancer> listLoadBalancers(LoadBalancerClient loadBalancerClient) {
+      return listLoadBalancers(loadBalancerClient, new ListLoadBalancersOptions());
+   }
+   
+}
diff --git a/labs/elb/src/main/java/org/jclouds/elb/ELBAsyncClient.java b/labs/elb/src/main/java/org/jclouds/elb/ELBAsyncClient.java
index e8bd451..c2c7803 100644
--- a/labs/elb/src/main/java/org/jclouds/elb/ELBAsyncClient.java
+++ b/labs/elb/src/main/java/org/jclouds/elb/ELBAsyncClient.java
@@ -31,12 +31,16 @@
 import org.jclouds.elb.binders.BindAvailabilityZonesToIndexedFormParams;
 import org.jclouds.elb.binders.BindInstanceIdsToIndexedFormParams;
 import org.jclouds.elb.binders.BindLoadBalancerNamesToIndexedFormParams;
-import org.jclouds.elb.domain.LoadBalancer;
+import org.jclouds.elb.domain.CrappyLoadBalancer;
+import org.jclouds.elb.features.LoadBalancerAsyncClient;
+import org.jclouds.elb.features.PolicyAsyncClient;
 import org.jclouds.elb.xml.CreateLoadBalancerResponseHandler;
 import org.jclouds.elb.xml.DescribeLoadBalancersResponseHandler;
 import org.jclouds.elb.xml.RegisterInstancesWithLoadBalancerResponseHandler;
+import org.jclouds.location.Region;
 import org.jclouds.location.functions.RegionToEndpointOrProviderIfNull;
 import org.jclouds.rest.annotations.BinderParam;
+import org.jclouds.rest.annotations.Delegate;
 import org.jclouds.rest.annotations.EndpointParam;
 import org.jclouds.rest.annotations.ExceptionParser;
 import org.jclouds.rest.annotations.FormParams;
@@ -47,6 +51,7 @@
 
 import com.google.common.annotations.Beta;
 import com.google.common.util.concurrent.ListenableFuture;
+import com.google.inject.Provides;
 
 /**
  * Provides access to EC2 Elastic Load Balancer via REST API.
@@ -60,7 +65,28 @@
 @RequestFilters(FormSigner.class)
 @VirtualHost
 public interface ELBAsyncClient {
-   public static final String VERSION = "2011-11-15";
+   /**
+    * 
+    * @return the Region codes configured
+    */
+   @Provides
+   @Region
+   Set<String> getConfiguredRegions();
+   
+   /**
+    * Provides asynchronous access to LoadBalancer features.
+    */
+   @Delegate
+   LoadBalancerAsyncClient getLoadBalancerClientForRegion(@EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region);
+
+   /**
+    * Provides asynchronous access to Policy features.
+    */
+   @Delegate
+   PolicyAsyncClient getPolicyClientForRegion(@EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region);
+
+   /// old stuff
+   public static final String VERSION = "2012-06-01";
 
    // TODO: there are a lot of missing methods
 
@@ -122,7 +148,7 @@
    @XMLResponseParser(DescribeLoadBalancersResponseHandler.class)
    @FormParams(keys = ACTION, values = "DescribeLoadBalancers")
    @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
-   ListenableFuture<Set<? extends LoadBalancer>> describeLoadBalancersInRegion(
+   ListenableFuture<Set<? extends CrappyLoadBalancer>> describeLoadBalancersInRegion(
             @EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region,
             @BinderParam(BindLoadBalancerNamesToIndexedFormParams.class) String... loadbalancerNames);
 
diff --git a/labs/elb/src/main/java/org/jclouds/elb/ELBClient.java b/labs/elb/src/main/java/org/jclouds/elb/ELBClient.java
index 1135033..d84f304 100644
--- a/labs/elb/src/main/java/org/jclouds/elb/ELBClient.java
+++ b/labs/elb/src/main/java/org/jclouds/elb/ELBClient.java
@@ -24,9 +24,16 @@
 import javax.annotation.Nullable;
 
 import org.jclouds.concurrent.Timeout;
-import org.jclouds.elb.domain.LoadBalancer;
+import org.jclouds.elb.domain.CrappyLoadBalancer;
+import org.jclouds.elb.features.LoadBalancerClient;
+import org.jclouds.elb.features.PolicyClient;
+import org.jclouds.location.Region;
+import org.jclouds.location.functions.RegionToEndpointOrProviderIfNull;
+import org.jclouds.rest.annotations.Delegate;
+import org.jclouds.rest.annotations.EndpointParam;
 
 import com.google.common.annotations.Beta;
+import com.google.inject.Provides;
 
 /**
  * Provides access to EC2 Elastic Load Balancer via their REST API.
@@ -38,8 +45,28 @@
 // see ELBAsyncClient
 @Timeout(duration = 30, timeUnit = TimeUnit.SECONDS)
 public interface ELBClient {
+   /**
+    * 
+    * @return the Region codes configured
+    */
+   @Provides
+   @Region
+   Set<String> getConfiguredRegions();
+  
+   /**
+    * Provides synchronous access to LoadBalancer features.
+    */
+   @Delegate
+   LoadBalancerClient getLoadBalancerClientForRegion(@EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region);
 
    /**
+    * Provides synchronous access to Policy features.
+    */
+   @Delegate
+   PolicyClient getPolicyClientForRegion(@EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region);
+   
+   /// old stuff
+   /**
     * Creates a load balancer
     * 
     * @param name
@@ -105,6 +132,6 @@
     *           names associated with the LoadBalancers at creation time.
     * @return
     */
-   Set<? extends LoadBalancer> describeLoadBalancersInRegion(@Nullable String region, String... loadbalancerNames);
+   Set<? extends CrappyLoadBalancer> describeLoadBalancersInRegion(@Nullable String region, String... loadbalancerNames);
 
 }
diff --git a/labs/elb/src/main/java/org/jclouds/elb/binders/BindPolicyTypeNamesToIndexedFormParams.java b/labs/elb/src/main/java/org/jclouds/elb/binders/BindPolicyTypeNamesToIndexedFormParams.java
new file mode 100644
index 0000000..f6e828f
--- /dev/null
+++ b/labs/elb/src/main/java/org/jclouds/elb/binders/BindPolicyTypeNamesToIndexedFormParams.java
@@ -0,0 +1,40 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.elb.binders;
+
+import static org.jclouds.aws.util.AWSUtils.indexIterableToFormValuesWithPrefix;
+
+import javax.inject.Singleton;
+
+import org.jclouds.http.HttpRequest;
+import org.jclouds.rest.Binder;
+
+/**
+ * Binds the Iterable<String> to form parameters named with PolicyTypeNames.member.N
+ * 
+ * @author Adrian Cole
+ */
+@Singleton
+public class BindPolicyTypeNamesToIndexedFormParams implements Binder {
+   @Override
+   public <R extends HttpRequest> R bindToRequest(R request, Object input) {
+      return indexIterableToFormValuesWithPrefix(request, "PolicyTypeNames.member", input);
+   }
+
+}
diff --git a/labs/elb/src/main/java/org/jclouds/elb/config/ELBRestClientModule.java b/labs/elb/src/main/java/org/jclouds/elb/config/ELBRestClientModule.java
index cf67ae6..b16d295 100644
--- a/labs/elb/src/main/java/org/jclouds/elb/config/ELBRestClientModule.java
+++ b/labs/elb/src/main/java/org/jclouds/elb/config/ELBRestClientModule.java
@@ -18,11 +18,20 @@
  */
 package org.jclouds.elb.config;
 
+import java.util.Map;
+
 import org.jclouds.aws.config.FormSigningRestClientModule;
 import org.jclouds.elb.ELBAsyncClient;
 import org.jclouds.elb.ELBClient;
+import org.jclouds.elb.features.LoadBalancerAsyncClient;
+import org.jclouds.elb.features.LoadBalancerClient;
+import org.jclouds.elb.features.PolicyAsyncClient;
+import org.jclouds.elb.features.PolicyClient;
 import org.jclouds.rest.ConfiguresRestClient;
 
+import com.google.common.collect.ImmutableMap;
+import com.google.common.reflect.TypeToken;
+
 /**
  * Configures the ELB connection.
  * 
@@ -30,5 +39,12 @@
  */
 @ConfiguresRestClient
 public class ELBRestClientModule extends FormSigningRestClientModule<ELBClient, ELBAsyncClient> {
+   public static final Map<Class<?>, Class<?>> DELEGATE_MAP = ImmutableMap.<Class<?>, Class<?>> builder()//
+            .put(LoadBalancerClient.class, LoadBalancerAsyncClient.class)
+            .put(PolicyClient.class, PolicyAsyncClient.class)
+            .build();
 
+   public ELBRestClientModule() {
+      super(TypeToken.of(ELBClient.class), TypeToken.of(ELBAsyncClient.class), DELEGATE_MAP);
+   }
 }
diff --git a/labs/elb/src/main/java/org/jclouds/elb/domain/AttributeMetadata.java b/labs/elb/src/main/java/org/jclouds/elb/domain/AttributeMetadata.java
new file mode 100644
index 0000000..648dc87
--- /dev/null
+++ b/labs/elb/src/main/java/org/jclouds/elb/domain/AttributeMetadata.java
@@ -0,0 +1,235 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.elb.domain;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
+import com.google.common.base.Optional;
+
+/**
+ * This data type is used to describe values that are acceptable for the policy attribute.
+ * 
+ * @see <a
+ *      href="http://docs.amazonwebservices.com/ElasticLoadBalancing/latest/APIReference/API_PolicyAttributeTypeDescription.html"
+ *      >doc</a>
+ * 
+ * @author Adrian Cole
+ */
+public class AttributeMetadata<V> {
+   /**
+    * The cardinality of the attribute.
+    */
+   public static enum Cardinality {
+      /**
+       * ONE(1) : Single value required
+       */
+      ONE,
+      /**
+       * ZERO_OR_ONE(0..1) : Up to one value can be supplied
+       */
+      ZERO_OR_ONE,
+      /**
+       * ZERO_OR_MORE(0..*) : Optional. Multiple values are allowed
+       */
+      ZERO_OR_MORE,
+      /**
+       * ONE_OR_MORE(1..*0) : Required. Multiple values are allowed *
+       */
+      ONE_OR_MORE;
+
+   }
+
+   public static <V> Builder<V> builder() {
+      return new Builder<V>();
+   }
+
+   public Builder<V> toBuilder() {
+      return new Builder<V>().fromAttributeMetadata(this);
+   }
+
+   public static class Builder<V> {
+
+      protected String name;
+      protected Class<V> type;
+      protected String rawType;
+      protected Cardinality cardinality;
+      protected Optional<V> defaultValue = Optional.absent();
+      protected Optional<String> description = Optional.absent();
+
+      /**
+       * @see AttributeMetadata#getName()
+       */
+      public Builder<V> name(String name) {
+         this.name = name;
+         return this;
+      }
+
+      /**
+       * @see AttributeMetadata#getType()
+       */
+      public Builder<V> type(Class<V> type) {
+         this.type = type;
+         return this;
+      }
+
+      /**
+       * @see AttributeMetadata#getRawType()
+       */
+      public Builder<V> rawType(String rawType) {
+         this.rawType = rawType;
+         return this;
+      }
+
+      /**
+       * @see AttributeMetadata#getCardinality()
+       */
+      public Builder<V> cardinality(Cardinality cardinality) {
+         this.cardinality = cardinality;
+         return this;
+      }
+
+      /**
+       * @see AttributeMetadata#getDefaultValue()
+       */
+      public Builder<V> defaultValue(V defaultValue) {
+         this.defaultValue = Optional.fromNullable(defaultValue);
+         return this;
+      }
+
+      /**
+       * @see AttributeMetadata#getDescription()
+       */
+      public Builder<V> description(String description) {
+         this.description = Optional.fromNullable(description);
+         return this;
+      }
+
+      public AttributeMetadata<V> build() {
+         return new AttributeMetadata<V>(name, type, rawType, cardinality, defaultValue, description);
+      }
+
+      public Builder<V> fromAttributeMetadata(AttributeMetadata<V> in) {
+         return this.name(in.getName()).type(in.getType()).rawType(in.getRawType()).cardinality(in.getCardinality())
+                  .defaultValue(in.getDefaultValue().orNull()).description(in.getDescription().orNull());
+      }
+   }
+
+   protected final String name;
+   protected final Class<V> type;
+   protected final String rawType;
+   protected final Cardinality cardinality;
+   protected final Optional<V> defaultValue;
+   protected final Optional<String> description;
+
+   protected AttributeMetadata(String name, Class<V> type, String rawType, Cardinality cardinality,
+            Optional<V> defaultValue, Optional<String> description) {
+      this.name = checkNotNull(name, "name");
+      this.type = checkNotNull(type, "type");
+      this.rawType = checkNotNull(rawType, "rawType");
+      this.cardinality = checkNotNull(cardinality, "cardinality");
+      this.defaultValue = checkNotNull(defaultValue, "defaultValue");
+      this.description = checkNotNull(description, "description");
+   }
+
+   /**
+    * The name of the attribute associated with the policy type.
+    */
+   public String getName() {
+      return name;
+   }
+
+   /**
+    * The type of attribute. For example, Boolean, Long, String, etc.
+    */
+   public Class<V> getType() {
+      return type;
+   }
+
+   /**
+    * Literal type of the value, noting that if it doesn't correspond to a primitive or String,
+    * {@link #getType() will return String.class}
+    */
+   public String getRawType() {
+      return rawType;
+   }
+
+   /**
+    * The cardinality of the attribute.
+    */
+   public Cardinality getCardinality() {
+      return cardinality;
+   }
+
+   /**
+    * The default value of the attribute, if applicable.
+    */
+   public Optional<V> getDefaultValue() {
+      return defaultValue;
+   }
+
+   /**
+    * A human-readable description of the attribute.
+    */
+   public Optional<String> getDescription() {
+      return description;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public int hashCode() {
+      return Objects.hashCode(name, type, rawType, cardinality, defaultValue, description);
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public boolean equals(Object obj) {
+      if (this == obj)
+         return true;
+      if (obj == null)
+         return false;
+      if (getClass() != obj.getClass())
+         return false;
+      AttributeMetadata<?> other = AttributeMetadata.class.cast(obj);
+      return Objects.equal(this.name, other.name) && Objects.equal(this.type, other.type)
+               && Objects.equal(this.rawType, other.rawType) && Objects.equal(this.cardinality, other.cardinality)
+               && Objects.equal(this.defaultValue, other.defaultValue)
+               && Objects.equal(this.description, other.description);
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public String toString() {
+      return string().toString();
+   }
+
+   protected ToStringHelper string() {
+      return Objects.toStringHelper(this).omitNullValues().add("name", name).add("type", type).add("rawType", rawType)
+               .add("cardinality", cardinality).add("defaultValue", defaultValue.orNull())
+               .add("description", description.orNull());
+   }
+
+}
diff --git a/labs/elb/src/main/java/org/jclouds/elb/domain/CrappyLoadBalancer.java b/labs/elb/src/main/java/org/jclouds/elb/domain/CrappyLoadBalancer.java
new file mode 100644
index 0000000..93c5b25
--- /dev/null
+++ b/labs/elb/src/main/java/org/jclouds/elb/domain/CrappyLoadBalancer.java
@@ -0,0 +1,447 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.elb.domain;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * 
+ * 
+ * @author Lili Nader
+ */
+@Beta
+// Missing fields, this class is too big, please cut out inner classes into top-level
+@Deprecated
+public class CrappyLoadBalancer implements Comparable<CrappyLoadBalancer> {
+
+   // Missing: createdTime, healthcheck
+   private String region;
+   private String name;
+   private Set<String> instanceIds;
+   private Set<String> availabilityZones;
+   private String dnsName;
+   // TODO: this could be cleaned up to be a policy collection of subclasses of Policy. note that
+   // docs suggest there could be many
+   private AppCookieStickinessPolicy appCookieStickinessPolicy;
+   private LBCookieStickinessPolicy lBCookieStickinessPolicy;
+   private Set<LoadBalancerListener> loadBalancerListeners;
+
+   public CrappyLoadBalancer() {
+      super();
+      this.instanceIds = new HashSet<String>();
+      this.availabilityZones = new HashSet<String>();
+      this.loadBalancerListeners = new HashSet<LoadBalancerListener>();
+   }
+
+   public CrappyLoadBalancer(String region, String name, Set<String> instanceIds, Set<String> availabilityZones,
+            String dnsName) {
+      this.region = region;
+      this.name = name;
+      this.instanceIds = instanceIds;
+      this.availabilityZones = availabilityZones;
+      this.dnsName = dnsName;
+      this.loadBalancerListeners = new HashSet<LoadBalancerListener>();
+   }
+
+   public void setRegion(String region) {
+      this.region = region;
+   }
+
+   public void setName(String name) {
+      this.name = name;
+   }
+
+   public void setInstanceIds(Set<String> instanceIds) {
+      this.instanceIds = instanceIds;
+   }
+
+   public void setAvailabilityZones(Set<String> availabilityZones) {
+      this.availabilityZones = availabilityZones;
+   }
+
+   public void setDnsName(String dnsName) {
+      this.dnsName = dnsName;
+   }
+
+   public void setAppCookieStickinessPolicy(AppCookieStickinessPolicy appCookieStickinessPolicy) {
+      this.appCookieStickinessPolicy = appCookieStickinessPolicy;
+   }
+
+   public void setlBCookieStickinessPolicy(LBCookieStickinessPolicy lBCookieStickinessPolicy) {
+      this.lBCookieStickinessPolicy = lBCookieStickinessPolicy;
+   }
+
+   public void setLoadBalancerListeners(Set<LoadBalancerListener> loadBalancerListeners) {
+      this.loadBalancerListeners = loadBalancerListeners;
+   }
+
+   public String getName() {
+      return name;
+   }
+
+   public Set<String> getInstanceIds() {
+      return instanceIds;
+   }
+
+   public Set<String> getAvailabilityZones() {
+      return availabilityZones;
+   }
+
+   public String getDnsName() {
+      return dnsName;
+   }
+
+   public AppCookieStickinessPolicy getAppCookieStickinessPolicy() {
+      return appCookieStickinessPolicy;
+   }
+
+   public LBCookieStickinessPolicy getlBCookieStickinessPolicy() {
+      return lBCookieStickinessPolicy;
+   }
+
+   public Set<LoadBalancerListener> getLoadBalancerListeners() {
+      return loadBalancerListeners;
+   }
+
+   public String getRegion() {
+      return region;
+   }
+
+   @Override
+   public int compareTo(CrappyLoadBalancer that) {
+      return name.compareTo(that.name);
+   }
+
+   @Override
+   public String toString() {
+      return "[region=" + region + ", name=" + name + ", instanceIds=" + instanceIds + ", availabilityZones="
+               + availabilityZones + ", dnsName=" + dnsName + ", appCookieStickinessPolicy="
+               + appCookieStickinessPolicy + ", lBCookieStickinessPolicy=" + lBCookieStickinessPolicy
+               + ", loadBalancerListeners=" + loadBalancerListeners + "]";
+   }
+
+   @Override
+   public int hashCode() {
+      final int prime = 31;
+      int result = 1;
+      result = prime * result + ((appCookieStickinessPolicy == null) ? 0 : appCookieStickinessPolicy.hashCode());
+      result = prime * result + ((availabilityZones == null) ? 0 : availabilityZones.hashCode());
+      result = prime * result + ((dnsName == null) ? 0 : dnsName.hashCode());
+      result = prime * result + ((instanceIds == null) ? 0 : instanceIds.hashCode());
+      result = prime * result + ((lBCookieStickinessPolicy == null) ? 0 : lBCookieStickinessPolicy.hashCode());
+      result = prime * result + ((loadBalancerListeners == null) ? 0 : loadBalancerListeners.hashCode());
+      result = prime * result + ((name == null) ? 0 : name.hashCode());
+      result = prime * result + ((region == null) ? 0 : region.hashCode());
+      return result;
+   }
+
+   @Override
+   public boolean equals(Object obj) {
+      if (this == obj)
+         return true;
+      if (obj == null)
+         return false;
+      if (getClass() != obj.getClass())
+         return false;
+      CrappyLoadBalancer other = (CrappyLoadBalancer) obj;
+      if (appCookieStickinessPolicy == null) {
+         if (other.appCookieStickinessPolicy != null)
+            return false;
+      } else if (!appCookieStickinessPolicy.equals(other.appCookieStickinessPolicy))
+         return false;
+      if (availabilityZones == null) {
+         if (other.availabilityZones != null)
+            return false;
+      } else if (!availabilityZones.equals(other.availabilityZones))
+         return false;
+      if (dnsName == null) {
+         if (other.dnsName != null)
+            return false;
+      } else if (!dnsName.equals(other.dnsName))
+         return false;
+      if (instanceIds == null) {
+         if (other.instanceIds != null)
+            return false;
+      } else if (!instanceIds.equals(other.instanceIds))
+         return false;
+      if (lBCookieStickinessPolicy == null) {
+         if (other.lBCookieStickinessPolicy != null)
+            return false;
+      } else if (!lBCookieStickinessPolicy.equals(other.lBCookieStickinessPolicy))
+         return false;
+      if (loadBalancerListeners == null) {
+         if (other.loadBalancerListeners != null)
+            return false;
+      } else if (!loadBalancerListeners.equals(other.loadBalancerListeners))
+         return false;
+      if (name == null) {
+         if (other.name != null)
+            return false;
+      } else if (!name.equals(other.name))
+         return false;
+      if (region == null) {
+         if (other.region != null)
+            return false;
+      } else if (!region.equals(other.region))
+         return false;
+      return true;
+   }
+
+   public static class AppCookieStickinessPolicy {
+      private String policyName;
+      private String cookieName;
+
+      public AppCookieStickinessPolicy() {
+         super();
+      }
+
+      public AppCookieStickinessPolicy(String policyName, String cookieName) {
+         super();
+         this.policyName = policyName;
+         this.cookieName = cookieName;
+      }
+
+      public String getPolicyName() {
+         return policyName;
+      }
+
+      public String getCookieName() {
+         return cookieName;
+      }
+
+      public void setPolicyName(String policyName) {
+         this.policyName = policyName;
+      }
+
+      public void setCookieName(String cookieName) {
+         this.cookieName = cookieName;
+      }
+
+      @Override
+      public int hashCode() {
+         final int prime = 31;
+         int result = 1;
+         result = prime * result + ((cookieName == null) ? 0 : cookieName.hashCode());
+         result = prime * result + ((policyName == null) ? 0 : policyName.hashCode());
+         return result;
+      }
+
+      @Override
+      public String toString() {
+         return "[policyName=" + policyName + ", cookieName=" + cookieName + "]";
+      }
+
+      @Override
+      public boolean equals(Object obj) {
+         if (this == obj)
+            return true;
+         if (obj == null)
+            return false;
+         if (getClass() != obj.getClass())
+            return false;
+         AppCookieStickinessPolicy other = (AppCookieStickinessPolicy) obj;
+         if (cookieName == null) {
+            if (other.cookieName != null)
+               return false;
+         } else if (!cookieName.equals(other.cookieName))
+            return false;
+         if (policyName == null) {
+            if (other.policyName != null)
+               return false;
+         } else if (!policyName.equals(other.policyName))
+            return false;
+         return true;
+      }
+
+   }
+
+   public static class LBCookieStickinessPolicy {
+      private String policyName;
+      private Integer cookieExpirationPeriod;
+
+      public LBCookieStickinessPolicy() {
+         super();
+      }
+
+      public LBCookieStickinessPolicy(String policyName, Integer cookieExpirationPeriod) {
+         super();
+         this.policyName = policyName;
+         this.cookieExpirationPeriod = cookieExpirationPeriod;
+      }
+
+      public String getPolicyName() {
+         return policyName;
+      }
+
+      public Integer getCookieExpirationPeriod() {
+         return cookieExpirationPeriod;
+      }
+
+      public void setPolicyName(String policyName) {
+         this.policyName = policyName;
+      }
+
+      public void setCookieExpirationPeriod(Integer cookieExpirationPeriod) {
+         this.cookieExpirationPeriod = cookieExpirationPeriod;
+      }
+
+      @Override
+      public int hashCode() {
+         final int prime = 31;
+         int result = 1;
+         result = prime * result + ((cookieExpirationPeriod == null) ? 0 : cookieExpirationPeriod.hashCode());
+         result = prime * result + ((policyName == null) ? 0 : policyName.hashCode());
+         return result;
+      }
+
+      @Override
+      public boolean equals(Object obj) {
+         if (this == obj)
+            return true;
+         if (obj == null)
+            return false;
+         if (getClass() != obj.getClass())
+            return false;
+         LBCookieStickinessPolicy other = (LBCookieStickinessPolicy) obj;
+         if (cookieExpirationPeriod == null) {
+            if (other.cookieExpirationPeriod != null)
+               return false;
+         } else if (!cookieExpirationPeriod.equals(other.cookieExpirationPeriod))
+            return false;
+         if (policyName == null) {
+            if (other.policyName != null)
+               return false;
+         } else if (!policyName.equals(other.policyName))
+            return false;
+         return true;
+      }
+
+      @Override
+      public String toString() {
+         return "[policyName=" + policyName + ", cookieExpirationPeriod=" + cookieExpirationPeriod + "]";
+      }
+
+   }
+
+   public static class LoadBalancerListener {
+      // TODO: missing SSLCertificateId
+      private Set<String> policyNames;
+      private Integer instancePort;
+      private Integer loadBalancerPort;
+      private String protocol;
+
+      public LoadBalancerListener(Set<String> policyNames, Integer instancePort, Integer loadBalancerPort,
+               String protocol) {
+         super();
+         this.policyNames = policyNames;
+         this.instancePort = instancePort;
+         this.loadBalancerPort = loadBalancerPort;
+         this.protocol = protocol;
+      }
+
+      public LoadBalancerListener() {
+         super();
+      }
+
+      public Set<String> getPolicyNames() {
+         return policyNames;
+      }
+
+      public Integer getInstancePort() {
+         return instancePort;
+      }
+
+      public Integer getLoadBalancerPort() {
+         return loadBalancerPort;
+      }
+
+      public String getProtocol() {
+         return protocol;
+      }
+
+      public void setPolicyNames(Set<String> policyNames) {
+         this.policyNames = policyNames;
+      }
+
+      public void setInstancePort(Integer instancePort) {
+         this.instancePort = instancePort;
+      }
+
+      public void setLoadBalancerPort(Integer loadBalancerPort) {
+         this.loadBalancerPort = loadBalancerPort;
+      }
+
+      public void setProtocol(String protocol) {
+         this.protocol = protocol;
+      }
+
+      @Override
+      public int hashCode() {
+         final int prime = 31;
+         int result = 1;
+         result = prime * result + ((instancePort == null) ? 0 : instancePort.hashCode());
+         result = prime * result + ((loadBalancerPort == null) ? 0 : loadBalancerPort.hashCode());
+         result = prime * result + ((policyNames == null) ? 0 : policyNames.hashCode());
+         result = prime * result + ((protocol == null) ? 0 : protocol.hashCode());
+         return result;
+      }
+
+      @Override
+      public boolean equals(Object obj) {
+         if (this == obj)
+            return true;
+         if (obj == null)
+            return false;
+         if (getClass() != obj.getClass())
+            return false;
+         LoadBalancerListener other = (LoadBalancerListener) obj;
+         if (instancePort == null) {
+            if (other.instancePort != null)
+               return false;
+         } else if (!instancePort.equals(other.instancePort))
+            return false;
+         if (loadBalancerPort == null) {
+            if (other.loadBalancerPort != null)
+               return false;
+         } else if (!loadBalancerPort.equals(other.loadBalancerPort))
+            return false;
+         if (policyNames == null) {
+            if (other.policyNames != null)
+               return false;
+         } else if (!policyNames.equals(other.policyNames))
+            return false;
+         if (protocol == null) {
+            if (other.protocol != null)
+               return false;
+         } else if (!protocol.equals(other.protocol))
+            return false;
+         return true;
+      }
+
+      @Override
+      public String toString() {
+         return "[policyNames=" + policyNames + ", instancePort=" + instancePort + ", loadBalancerPort="
+                  + loadBalancerPort + ", protocol=" + protocol + "]";
+      }
+
+   }
+}
diff --git a/labs/elb/src/main/java/org/jclouds/elb/domain/HealthCheck.java b/labs/elb/src/main/java/org/jclouds/elb/domain/HealthCheck.java
new file mode 100644
index 0000000..71b5684
--- /dev/null
+++ b/labs/elb/src/main/java/org/jclouds/elb/domain/HealthCheck.java
@@ -0,0 +1,229 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.elb.domain;
+
+import static com.google.common.base.Preconditions.*;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
+
+/**
+ * Elastic Load Balancing routinely checks the health of each load-balanced Amazon EC2 instance
+ * based on the configurations that you specify. If Elastic Load Balancing finds an unhealthy
+ * instance, it stops sending traffic to the instance and reroutes traffic to healthy instances.
+ * 
+ * 
+ * @see <a
+ *      href="http://docs.amazonwebservices.com/ElasticLoadBalancing/latest/DeveloperGuide/ConfigureHealthCheck.html"
+ *      >doc</a>
+ * 
+ * @author Adrian Cole
+ */
+public class HealthCheck {
+
+   public static Builder<?> builder() {
+      return new ConcreteBuilder();
+   }
+
+   public Builder<?> toBuilder() {
+      return new ConcreteBuilder().fromListener(this);
+   }
+
+   public static abstract class Builder<T extends Builder<T>> {
+      protected abstract T self();
+
+      protected int healthyThreshold = -1;
+      protected int interval = -1;
+      protected String target;
+      protected int timeout = -1;
+      protected int unhealthyThreshold = -1;
+
+      /**
+       * @see HealthCheck#getHealthyThreshold()
+       */
+      public T healthyThreshold(int healthyThreshold) {
+         this.healthyThreshold = healthyThreshold;
+         return self();
+      }
+
+      /**
+       * @see HealthCheck#getInterval()
+       */
+      public T interval(int interval) {
+         this.interval = interval;
+         return self();
+      }
+
+      /**
+       * @see HealthCheck#getTarget()
+       */
+      public T target(String target) {
+         this.target = target;
+         return self();
+      }
+
+      /**
+       * @see HealthCheck#getTimeout()
+       */
+      public T timeout(int timeout) {
+         this.timeout = timeout;
+         return self();
+      }
+
+      /**
+       * @see HealthCheck#getUnhealthyThreshold()
+       */
+      public T unhealthyThreshold(int unhealthyThreshold) {
+         this.unhealthyThreshold = unhealthyThreshold;
+         return self();
+      }
+
+      public HealthCheck build() {
+         return new HealthCheck(healthyThreshold, interval, target, timeout, unhealthyThreshold);
+      }
+
+      public T fromListener(HealthCheck in) {
+         return this.healthyThreshold(in.getHealthyThreshold()).interval(in.getInterval()).target(in.getTarget())
+                  .timeout(in.getTimeout()).unhealthyThreshold(in.getUnhealthyThreshold());
+      }
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
+   }
+
+   protected final int healthyThreshold;
+   protected final int interval;
+   protected final String target;
+   protected final int timeout;
+   protected final int unhealthyThreshold;
+
+   protected HealthCheck(int healthyThreshold, int interval, String target, int timeout, int unhealthyThreshold) {
+      this.healthyThreshold = checkNonNegative(healthyThreshold, "healthyThreshold");
+      this.interval = checkNonNegative(interval, "interval");
+      this.target = checkNotNull(target, "target");
+      this.timeout = checkNonNegative(timeout, "timeout");
+      this.unhealthyThreshold = checkNonNegative(unhealthyThreshold, "unhealthyThreshold");
+   }
+
+   static int checkNonNegative(int in, String name) {
+      checkArgument(in > 0, "%s must be non-negative", name);
+      return in;
+   }
+   
+   /**
+    * Specifies the number of consecutive health probe successes required before moving the instance
+    * to the Healthy state.
+    */
+   public int getHealthyThreshold() {
+      return healthyThreshold;
+   }
+
+   /**
+    * Specifies the approximate interval, in seconds, between health checks of an individual
+    * instance.
+    */
+   public int getInterval() {
+      return interval;
+   }
+
+   /**
+    * Specifies the instance being checked. The timeout is either TCP, HTTP, HTTPS, or SSL. The
+    * range of valid ports is one (1) through 65535.
+    * 
+    * <h3>Note</h3>
+    * 
+    * TCP is the default, specified as a TCP: port pair, for example "TCP:5000". In this case a
+    * healthcheck simply attempts to open a TCP connection to the instance on the specified port.
+    * Failure to connect within the configured timeout is considered unhealthy. <br/>
+    * SSL is also specified as SSL: port pair, for example, SSL:5000. <br/>
+    * 
+    * For HTTP or HTTPS timeout, the situation is different. You have to include a ping path in the
+    * string. HTTP is specified as a HTTP:port;/;PathToPing; grouping, for example
+    * "HTTP:80/weather/us/wa/seattle". In this case, a HTTP GET request is issued to the instance on
+    * the given port and path. Any answer other than "200 OK" within the timeout period is
+    * considered unhealthy.
+    * 
+    * The total length of the HTTP ping target needs to be 1024 16-bit Unicode characters or less.
+    */
+   public String getTarget() {
+      return target;
+   }
+
+   /**
+    * Specifies the amount of time, in seconds, during which no response means a failed health
+    * probe.
+    * 
+    * <h3>Note</h3> This value must be less than the Interval value.
+    */
+   public int getTimeout() {
+      return timeout;
+   }
+
+   /**
+    * Specifies the number of consecutive health probe failures required before moving the instance
+    * to the Unhealthy state.
+    */
+   public int getUnhealthyThreshold() {
+      return unhealthyThreshold;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public int hashCode() {
+      return Objects.hashCode(healthyThreshold, interval, target, timeout, unhealthyThreshold);
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public boolean equals(Object obj) {
+      if (this == obj)
+         return true;
+      if (obj == null)
+         return false;
+      if (getClass() != obj.getClass())
+         return false;
+      HealthCheck other = (HealthCheck) obj;
+      return Objects.equal(this.healthyThreshold, other.healthyThreshold)
+               && Objects.equal(this.interval, other.interval) && Objects.equal(this.target, other.target)
+               && Objects.equal(this.timeout, other.timeout)
+               && Objects.equal(this.unhealthyThreshold, other.unhealthyThreshold);
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public String toString() {
+      return string().toString();
+   }
+
+   protected ToStringHelper string() {
+      return Objects.toStringHelper(this).omitNullValues().add("healthyThreshold", healthyThreshold).add("interval",
+               interval).add("target", target).add("timeout", timeout).add("unhealthyThreshold", unhealthyThreshold);
+   }
+
+}
diff --git a/labs/elb/src/main/java/org/jclouds/elb/domain/Listener.java b/labs/elb/src/main/java/org/jclouds/elb/domain/Listener.java
new file mode 100644
index 0000000..035546d
--- /dev/null
+++ b/labs/elb/src/main/java/org/jclouds/elb/domain/Listener.java
@@ -0,0 +1,222 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.elb.domain;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Optional;
+import com.google.common.base.Objects.ToStringHelper;
+
+/**
+ * Listener is a process that listens for client connection requests. It is configured with a
+ * protocol and a port number for front-end (Load Balancer) and back-end (Back-end instance)
+ * connections.
+ * 
+ * 
+ * By default, your load balancer is set to use the HTTP protocol with port 80 for the front-end
+ * connection and the back-end connection. The default settings can be changed using the AWS
+ * Management Console, the Query API, the command line interface (CLI), or the SDKs.
+ * 
+ * @see <a
+ *      href="http://docs.amazonwebservices.com/ElasticLoadBalancing/latest/DeveloperGuide/elb-listener-config.html"
+ *      >doc</a>
+ * 
+ * @author Adrian Cole
+ */
+public class Listener {
+
+   public static Builder<?> builder() {
+      return new ConcreteBuilder();
+   }
+
+   public Builder<?> toBuilder() {
+      return new ConcreteBuilder().fromListener(this);
+   }
+
+   public static abstract class Builder<T extends Builder<T>> {
+      protected abstract T self();
+
+      protected int instancePort = -1;
+      protected Protocol instanceProtocol;
+      protected int port = -1;
+      protected Protocol protocol;
+      protected Optional<String> SSLCertificateId = Optional.absent();
+
+      /**
+       * @see Listener#getInstancePort()
+       */
+      public T instancePort(int instancePort) {
+         this.instancePort = instancePort;
+         return self();
+      }
+
+      /**
+       * @see Listener#getInstanceProtocol()
+       */
+      public T instanceProtocol(Protocol instanceProtocol) {
+         this.instanceProtocol = instanceProtocol;
+         return self();
+      }
+
+      /**
+       * @see Listener#getPort()
+       */
+      public T port(int port) {
+         this.port = port;
+         return self();
+      }
+
+      /**
+       * @see Listener#getProtocol()
+       */
+      public T protocol(Protocol protocol) {
+         this.protocol = protocol;
+         if (instanceProtocol == null)
+            instanceProtocol = protocol;
+         return self();
+      }
+
+      /**
+       * @see Listener#getSSLCertificateId()
+       */
+      public T SSLCertificateId(String SSLCertificateId) {
+         this.SSLCertificateId = Optional.fromNullable(SSLCertificateId);
+         return self();
+      }
+
+      public Listener build() {
+         return new Listener(instancePort, instanceProtocol, port, protocol, SSLCertificateId);
+      }
+
+      public T fromListener(Listener in) {
+         return this.instancePort(in.getInstancePort()).instanceProtocol(in.getInstanceProtocol()).port(in.getPort())
+                  .protocol(in.getProtocol()).SSLCertificateId(in.getSSLCertificateId().orNull());
+      }
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
+   }
+
+   protected final int instancePort;
+   protected final Protocol instanceProtocol;
+   protected final int port;
+   protected final Protocol protocol;
+   protected final Optional<String> SSLCertificateId;
+
+   protected Listener(int instancePort, Protocol instanceProtocol, int port, Protocol protocol,
+            Optional<String> SSLCertificateId) {
+      this.instancePort = checkNonNegative(instancePort, "instancePort");
+      this.instanceProtocol = checkNotNull(instanceProtocol, "instanceProtocol");
+      this.port = checkNonNegative(port, "port");
+      this.protocol = checkNotNull(protocol, "protocol");
+      this.SSLCertificateId = checkNotNull(SSLCertificateId, "SSLCertificateId");
+   }
+
+   static int checkNonNegative(int in, String name) {
+      checkArgument(in > 0, "%s must be non-negative", name);
+      return in;
+   }
+   
+   /**
+    * The name associated with the LoadBalancer. The name must be unique within your set of
+    * LoadBalancers.
+    */
+   public int getInstancePort() {
+      return instancePort;
+   }
+
+   /**
+    * pecifies the protocol to use for routing traffic to back-end instances - HTTP, HTTPS, TCP, or
+    * SSL. This property cannot be modified for the life of the LoadBalancer.
+    */
+   public Protocol getInstanceProtocol() {
+      return instanceProtocol;
+   }
+
+   /**
+    * Specifies the external LoadBalancer port number. This property cannot be modified for the life
+    * of the LoadBalancer.
+    */
+   public int getPort() {
+      return port;
+   }
+
+   /**
+    * Specifies the LoadBalancer transport protocol to use for routing - HTTP, HTTPS, TCP or SSL.
+    * This property cannot be modified for the life of the LoadBalancer.
+    */
+   public Protocol getProtocol() {
+      return protocol;
+   }
+
+   /**
+    * The ARN string of the server certificate. To get the ARN of the server certificate, call the
+    * AWS Identity and Access Management UploadServerCertificate API.
+    */
+   public Optional<String> getSSLCertificateId() {
+      return SSLCertificateId;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public int hashCode() {
+      return Objects.hashCode(instancePort, instanceProtocol, port, protocol, SSLCertificateId);
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public boolean equals(Object obj) {
+      if (this == obj)
+         return true;
+      if (obj == null)
+         return false;
+      if (getClass() != obj.getClass())
+         return false;
+      Listener other = (Listener) obj;
+      return Objects.equal(this.instancePort, other.instancePort)
+               && Objects.equal(this.instanceProtocol, other.instanceProtocol) && Objects.equal(this.port, other.port)
+               && Objects.equal(this.protocol, other.protocol)
+               && Objects.equal(this.SSLCertificateId, other.SSLCertificateId);
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public String toString() {
+      return string().toString();
+   }
+
+   protected ToStringHelper string() {
+      return Objects.toStringHelper(this).omitNullValues().add("instancePort", instancePort).add("instanceProtocol",
+               instanceProtocol).add("port", port).add("protocol", protocol).add("SSLCertificateId",
+               SSLCertificateId.orNull());
+   }
+
+}
diff --git a/labs/elb/src/main/java/org/jclouds/elb/domain/ListenerWithPolicies.java b/labs/elb/src/main/java/org/jclouds/elb/domain/ListenerWithPolicies.java
new file mode 100644
index 0000000..5b53fc8
--- /dev/null
+++ b/labs/elb/src/main/java/org/jclouds/elb/domain/ListenerWithPolicies.java
@@ -0,0 +1,119 @@
+/*
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.elb.domain;
+
+import static com.google.common.base.Objects.equal;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.Set;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Optional;
+import com.google.common.base.Objects.ToStringHelper;
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * 
+ * @author Adrian Cole
+ */
+public class ListenerWithPolicies extends Listener {
+
+   public static Builder<?> builder() {
+      return new ConcreteBuilder();
+   }
+
+   @Override
+   public Builder<?> toBuilder() {
+      return builder().fromListenerWithPolicies(this);
+   }
+
+   public static abstract class Builder<T extends Builder<T>> extends Listener.Builder<T> {
+
+      private ImmutableSet.Builder<String> policyNames = ImmutableSet.<String> builder();
+
+      /**
+       * @see ListenerWithPolicies#getPolicyNames()
+       */
+      public T policyNames(Iterable<String> policyNames) {
+         this.policyNames.addAll(checkNotNull(policyNames, "policyNames"));
+         return self();
+      }
+
+      /**
+       * @see ListenerWithPolicies#getPolicyNames()
+       */
+      public T policyName(String policyName) {
+         this.policyNames.add(checkNotNull(policyName, "policyName"));
+         return self();
+      }
+
+      @Override
+      public ListenerWithPolicies build() {
+         return new ListenerWithPolicies(instancePort, instanceProtocol, port, protocol, SSLCertificateId, policyNames
+                  .build());
+      }
+
+      public T fromListenerWithPolicies(ListenerWithPolicies in) {
+         return fromListener(in).policyNames(in.getPolicyNames());
+      }
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
+   }
+
+   private final Set<String> policyNames;
+
+   protected ListenerWithPolicies(int instancePort, Protocol instanceProtocol, int port, Protocol protocol,
+            Optional<String> SSLCertificateId, Iterable<String> policyNames) {
+      super(instancePort, instanceProtocol, port, protocol, SSLCertificateId);
+      this.policyNames = ImmutableSet.copyOf(checkNotNull(policyNames, "policyNames"));
+   }
+
+   /**
+    * A list of policies enabled for this listener. An empty list indicates that no policies are
+    * enabled.
+    */
+   public Set<String> getPolicyNames() {
+      return policyNames;
+   }
+
+   @Override
+   public boolean equals(Object o) {
+      if (this == o)
+         return true;
+      if (o == null || getClass() != o.getClass())
+         return false;
+      ListenerWithPolicies that = ListenerWithPolicies.class.cast(o);
+      return super.equals(that) && equal(this.policyNames, that.policyNames);
+   }
+
+   @Override
+   public int hashCode() {
+      return Objects.hashCode(super.hashCode(), policyNames);
+   }
+
+   @Override
+   public ToStringHelper string() {
+      return super.string().add("policyNames", policyNames);
+   }
+}
diff --git a/labs/elb/src/main/java/org/jclouds/elb/domain/LoadBalancer.java b/labs/elb/src/main/java/org/jclouds/elb/domain/LoadBalancer.java
index 61e00e8..a3834cb 100644
--- a/labs/elb/src/main/java/org/jclouds/elb/domain/LoadBalancer.java
+++ b/labs/elb/src/main/java/org/jclouds/elb/domain/LoadBalancer.java
@@ -18,141 +18,286 @@
  */
 package org.jclouds.elb.domain;
 
-import java.util.HashSet;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.Date;
 import java.util.Set;
 
-import com.google.common.annotations.Beta;
+import com.google.common.base.CaseFormat;
+import com.google.common.base.Objects;
+import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableSet;
 
 /**
+ * A load balancer is represented by a DNS name and a set of ports. The load balancer is the
+ * destination to which all requests intended for your application should be directed. Each load
+ * balancer can distribute requests to multiple EC2 instances. Load Balancers can span multiple
+ * Availability Zones within an EC2 region, but they cannot span multiple regions.
  * 
+ * <h3>note</h3>
  * 
- * @author Lili Nader
+ * Elastic Load Balancing automatically generates a DNS name for each load balancer. You can map any
+ * other domain name (such as www.example.com) to the automatically generated DNS name using CNAME.
+ * Or you can use an Amazon Route 53 alias for the load balancer's DNS name. Amazon Route 53
+ * provides secure and reliable routing to the infrastructure that uses AWS products, such as Amazon
+ * EC2, Amazon Simple Storage Service (Amazon S3), or Elastic Load Balancing. For more information
+ * on using Amazon Route 53 for your load balancer, see Using Domain Names with Elastic Load
+ * Balancing. For information about CNAME records, see the CNAME Record Wikipedia article.
+ * 
+ * @see <a
+ *      href="http://docs.amazonwebservices.com/ElasticLoadBalancing/latest/DeveloperGuide/arch-loadbalancing.html"
+ *      >doc</a>
+ * 
+ * @author Adrian Cole
  */
-@Beta
-// Missing fields, this class is too big, please cut out inner classes into top-level
-public class LoadBalancer implements Comparable<LoadBalancer> {
+public class LoadBalancer {
+   /**
+    * Specifies the type of LoadBalancer. This option is only available for LoadBalancers attached
+    * to an Amazon VPC.
+    */
+   public static enum Scheme {
 
-   // Missing: createdTime, healthcheck
-   private String region;
-   private String name;
-   private Set<String> instanceIds;
-   private Set<String> availabilityZones;
-   private String dnsName;
-   // TODO: this could be cleaned up to be a policy collection of subclasses of Policy. note that
-   // docs suggest there could be many
-   private AppCookieStickinessPolicy appCookieStickinessPolicy;
-   private LBCookieStickinessPolicy lBCookieStickinessPolicy;
-   private Set<LoadBalancerListener> loadBalancerListeners;
+      /**
+       * the LoadBalancer has a publicly resolvable DNS name that resolves to public IP addresses
+       */
+      INTERNET_FACING,
+      /**
+       * the LoadBalancer has a publicly resolvable DNS name that resolves to private IP addresses.
+       */
+      INTERNAL,
+      /**
+       * The scheme was returned unrecognized.
+       */
+      UNRECOGNIZED;
 
-   public LoadBalancer() {
-      super();
-      this.instanceIds = new HashSet<String>();
-      this.availabilityZones = new HashSet<String>();
-      this.loadBalancerListeners = new HashSet<LoadBalancerListener>();
+      public String value() {
+         return (CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_HYPHEN, name()));
+      }
+
+      @Override
+      public String toString() {
+         return value();
+      }
+
+      public static Scheme fromValue(String scheme) {
+         try {
+            return valueOf(CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_UNDERSCORE, checkNotNull(scheme, "scheme")));
+         } catch (IllegalArgumentException e) {
+            return UNRECOGNIZED;
+         }
+      }
    }
 
-   public LoadBalancer(String region, String name, Set<String> instanceIds, Set<String> availabilityZones,
-            String dnsName) {
-      this.region = region;
-      this.name = name;
-      this.instanceIds = instanceIds;
-      this.availabilityZones = availabilityZones;
-      this.dnsName = dnsName;
-      this.loadBalancerListeners = new HashSet<LoadBalancerListener>();
+   public static Builder<?> builder() {
+      return new ConcreteBuilder();
    }
 
-   public void setRegion(String region) {
-      this.region = region;
+   public Builder<?> toBuilder() {
+      return new ConcreteBuilder().fromLoadBalancer(this);
    }
 
-   public void setName(String name) {
-      this.name = name;
+   public static abstract class Builder<T extends Builder<T>> {
+      protected abstract T self();
+
+      protected String name;
+      protected Date createdTime;
+      protected String dnsName;
+      protected HealthCheck healthCheck;
+      protected ImmutableSet.Builder<String> instanceIds = ImmutableSet.<String> builder();
+      protected ImmutableSet.Builder<ListenerWithPolicies> listeners = ImmutableSet.<ListenerWithPolicies> builder();
+      protected Optional<Scheme> scheme = Optional.absent();
+      protected Optional<String> VPCId = Optional.absent();
+
+      /**
+       * @see LoadBalancer#getName()
+       */
+      public T name(String name) {
+         this.name = name;
+         return self();
+      }
+
+      /**
+       * @see LoadBalancer#getCreatedTime()
+       */
+      public T createdTime(Date createdTime) {
+         this.createdTime = createdTime;
+         return self();
+      }
+
+      /**
+       * @see LoadBalancer#getDnsName()
+       */
+      public T dnsName(String dnsName) {
+         this.dnsName = dnsName;
+         return self();
+      }
+
+      /**
+       * @see LoadBalancer#getHealthCheck()
+       */
+      public T healthCheck(HealthCheck healthCheck) {
+         this.healthCheck = healthCheck;
+         return self();
+      }
+
+      /**
+       * @see LoadBalancer#getInstanceIds()
+       */
+      public T instanceIds(Iterable<String> instanceIds) {
+         this.instanceIds.addAll(checkNotNull(instanceIds, "instanceIds"));
+         return self();
+      }
+
+      /**
+       * @see LoadBalancer#getInstanceIds()
+       */
+      public T instanceId(String instanceId) {
+         this.instanceIds.add(checkNotNull(instanceId, "instanceId"));
+         return self();
+      }
+
+      /**
+       * @see LoadBalancer#getListeners()
+       */
+      public T listeners(Iterable<ListenerWithPolicies> listeners) {
+         this.listeners.addAll(checkNotNull(listeners, "listeners"));
+         return self();
+      }
+
+      /**
+       * @see LoadBalancer#getListeners()
+       */
+      public T listener(ListenerWithPolicies listener) {
+         this.listeners.add(checkNotNull(listener, "listener"));
+         return self();
+      }
+
+      /**
+       * @see LoadBalancer#getScheme()
+       */
+      public T scheme(Scheme scheme) {
+         this.scheme = Optional.fromNullable(scheme);
+         return self();
+      }
+
+      /**
+       * @see LoadBalancer#getVPCId()
+       */
+      public T VPCId(String VPCId) {
+         this.VPCId = Optional.fromNullable(VPCId);
+         return self();
+      }
+
+      public LoadBalancer build() {
+         return new LoadBalancer(name, createdTime, dnsName, healthCheck, instanceIds.build(), listeners.build(),
+                  scheme, VPCId);
+      }
+
+      public T fromLoadBalancer(LoadBalancer in) {
+         return this.name(in.getName()).createdTime(in.getCreatedTime()).dnsName(in.getDnsName())
+                  .healthCheck(in.getHealthCheck()).listeners(in.getListeners()).instanceIds(in.getInstanceIds())
+                  .scheme(in.getScheme().orNull()).VPCId(in.getVPCId().orNull());
+      }
    }
 
-   public void setInstanceIds(Set<String> instanceIds) {
-      this.instanceIds = instanceIds;
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
    }
 
-   public void setAvailabilityZones(Set<String> availabilityZones) {
-      this.availabilityZones = availabilityZones;
+   protected final String name;
+   protected final Date createdTime;
+   protected final String dnsName;
+   protected final HealthCheck healthCheck;
+   protected final Set<String> instanceIds;
+   protected final Set<ListenerWithPolicies> listeners;
+   protected final Optional<Scheme> scheme;
+   protected final Optional<String> VPCId;
+
+   protected LoadBalancer(String name, Date createdTime, String dnsName, HealthCheck healthCheck,
+            Iterable<String> instanceIds, Iterable<ListenerWithPolicies> listeners, Optional<Scheme> scheme,
+            Optional<String> VPCId) {
+      this.name = checkNotNull(name, "name");
+      this.createdTime = checkNotNull(createdTime, "createdTime");
+      this.dnsName = checkNotNull(dnsName, "dnsName");
+      this.healthCheck = checkNotNull(healthCheck, "healthCheck");
+      this.instanceIds = ImmutableSet.copyOf(checkNotNull(instanceIds, "instanceIds"));
+      this.listeners = ImmutableSet.copyOf(checkNotNull(listeners, "listeners"));
+      this.scheme = checkNotNull(scheme, "scheme");
+      this.VPCId = checkNotNull(VPCId, "VPCId");
    }
 
-   public void setDnsName(String dnsName) {
-      this.dnsName = dnsName;
-   }
-
-   public void setAppCookieStickinessPolicy(AppCookieStickinessPolicy appCookieStickinessPolicy) {
-      this.appCookieStickinessPolicy = appCookieStickinessPolicy;
-   }
-
-   public void setlBCookieStickinessPolicy(LBCookieStickinessPolicy lBCookieStickinessPolicy) {
-      this.lBCookieStickinessPolicy = lBCookieStickinessPolicy;
-   }
-
-   public void setLoadBalancerListeners(Set<LoadBalancerListener> loadBalancerListeners) {
-      this.loadBalancerListeners = loadBalancerListeners;
-   }
-
+   /**
+    * The name associated with the LoadBalancer. The name must be unique within your set of
+    * LoadBalancers.
+    */
    public String getName() {
       return name;
    }
 
-   public Set<String> getInstanceIds() {
-      return instanceIds;
+   /**
+    * Provides the date and time the LoadBalancer was created.
+    */
+   public Date getCreatedTime() {
+      return createdTime;
    }
 
-   public Set<String> getAvailabilityZones() {
-      return availabilityZones;
-   }
-
+   /**
+    * Specifies the external DNS name associated with the LoadBalancer.
+    */
    public String getDnsName() {
       return dnsName;
    }
 
-   public AppCookieStickinessPolicy getAppCookieStickinessPolicy() {
-      return appCookieStickinessPolicy;
+   /**
+    * Specifies information regarding the various health probes conducted on the LoadBalancer.
+    */
+   public HealthCheck getHealthCheck() {
+      return healthCheck;
    }
 
-   public LBCookieStickinessPolicy getlBCookieStickinessPolicy() {
-      return lBCookieStickinessPolicy;
+   /**
+    * Provides a list of EC2 instance IDs for the LoadBalancer.
+    */
+   public Set<String> getInstanceIds() {
+      return instanceIds;
    }
 
-   public Set<LoadBalancerListener> getLoadBalancerListeners() {
-      return loadBalancerListeners;
+   /**
+    * Provides a list of listeners for the LoadBalancer.
+    */
+   public Set<ListenerWithPolicies> getListeners() {
+      return listeners;
    }
 
-   public String getRegion() {
-      return region;
+   /**
+    * Type of the loadbalancer; This option is only available for LoadBalancers attached to an
+    * Amazon VPC.
+    */
+   public Optional<Scheme> getScheme() {
+      return scheme;
    }
 
-   @Override
-   public int compareTo(LoadBalancer that) {
-      return name.compareTo(that.name);
+   /**
+    * Provides the ID of the VPC attached to the LoadBalancer.
+    */
+   public Optional<String> getVPCId() {
+      return VPCId;
    }
 
-   @Override
-   public String toString() {
-      return "[region=" + region + ", name=" + name + ", instanceIds=" + instanceIds + ", availabilityZones="
-               + availabilityZones + ", dnsName=" + dnsName + ", appCookieStickinessPolicy="
-               + appCookieStickinessPolicy + ", lBCookieStickinessPolicy=" + lBCookieStickinessPolicy
-               + ", loadBalancerListeners=" + loadBalancerListeners + "]";
-   }
-
+   /**
+    * {@inheritDoc}
+    */
    @Override
    public int hashCode() {
-      final int prime = 31;
-      int result = 1;
-      result = prime * result + ((appCookieStickinessPolicy == null) ? 0 : appCookieStickinessPolicy.hashCode());
-      result = prime * result + ((availabilityZones == null) ? 0 : availabilityZones.hashCode());
-      result = prime * result + ((dnsName == null) ? 0 : dnsName.hashCode());
-      result = prime * result + ((instanceIds == null) ? 0 : instanceIds.hashCode());
-      result = prime * result + ((lBCookieStickinessPolicy == null) ? 0 : lBCookieStickinessPolicy.hashCode());
-      result = prime * result + ((loadBalancerListeners == null) ? 0 : loadBalancerListeners.hashCode());
-      result = prime * result + ((name == null) ? 0 : name.hashCode());
-      result = prime * result + ((region == null) ? 0 : region.hashCode());
-      return result;
+      return Objects.hashCode(name, createdTime);
    }
 
+   /**
+    * {@inheritDoc}
+    */
    @Override
    public boolean equals(Object obj) {
       if (this == obj)
@@ -162,285 +307,17 @@
       if (getClass() != obj.getClass())
          return false;
       LoadBalancer other = (LoadBalancer) obj;
-      if (appCookieStickinessPolicy == null) {
-         if (other.appCookieStickinessPolicy != null)
-            return false;
-      } else if (!appCookieStickinessPolicy.equals(other.appCookieStickinessPolicy))
-         return false;
-      if (availabilityZones == null) {
-         if (other.availabilityZones != null)
-            return false;
-      } else if (!availabilityZones.equals(other.availabilityZones))
-         return false;
-      if (dnsName == null) {
-         if (other.dnsName != null)
-            return false;
-      } else if (!dnsName.equals(other.dnsName))
-         return false;
-      if (instanceIds == null) {
-         if (other.instanceIds != null)
-            return false;
-      } else if (!instanceIds.equals(other.instanceIds))
-         return false;
-      if (lBCookieStickinessPolicy == null) {
-         if (other.lBCookieStickinessPolicy != null)
-            return false;
-      } else if (!lBCookieStickinessPolicy.equals(other.lBCookieStickinessPolicy))
-         return false;
-      if (loadBalancerListeners == null) {
-         if (other.loadBalancerListeners != null)
-            return false;
-      } else if (!loadBalancerListeners.equals(other.loadBalancerListeners))
-         return false;
-      if (name == null) {
-         if (other.name != null)
-            return false;
-      } else if (!name.equals(other.name))
-         return false;
-      if (region == null) {
-         if (other.region != null)
-            return false;
-      } else if (!region.equals(other.region))
-         return false;
-      return true;
+      return Objects.equal(this.name, other.name) && Objects.equal(this.createdTime, other.createdTime);
    }
 
-   public static class AppCookieStickinessPolicy {
-      private String policyName;
-      private String cookieName;
-
-      public AppCookieStickinessPolicy() {
-         super();
-      }
-
-      public AppCookieStickinessPolicy(String policyName, String cookieName) {
-         super();
-         this.policyName = policyName;
-         this.cookieName = cookieName;
-      }
-
-      public String getPolicyName() {
-         return policyName;
-      }
-
-      public String getCookieName() {
-         return cookieName;
-      }
-
-      public void setPolicyName(String policyName) {
-         this.policyName = policyName;
-      }
-
-      public void setCookieName(String cookieName) {
-         this.cookieName = cookieName;
-      }
-
-      @Override
-      public int hashCode() {
-         final int prime = 31;
-         int result = 1;
-         result = prime * result + ((cookieName == null) ? 0 : cookieName.hashCode());
-         result = prime * result + ((policyName == null) ? 0 : policyName.hashCode());
-         return result;
-      }
-
-      @Override
-      public String toString() {
-         return "[policyName=" + policyName + ", cookieName=" + cookieName + "]";
-      }
-
-      @Override
-      public boolean equals(Object obj) {
-         if (this == obj)
-            return true;
-         if (obj == null)
-            return false;
-         if (getClass() != obj.getClass())
-            return false;
-         AppCookieStickinessPolicy other = (AppCookieStickinessPolicy) obj;
-         if (cookieName == null) {
-            if (other.cookieName != null)
-               return false;
-         } else if (!cookieName.equals(other.cookieName))
-            return false;
-         if (policyName == null) {
-            if (other.policyName != null)
-               return false;
-         } else if (!policyName.equals(other.policyName))
-            return false;
-         return true;
-      }
-
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public String toString() {
+      return Objects.toStringHelper(this).omitNullValues().add("name", name).add("createdTime", createdTime).add(
+               "dnsName", dnsName).add("healthCheck", healthCheck).add("instanceIds", instanceIds).add("listeners",
+               listeners).add("scheme", scheme.orNull()).add("VPCId", VPCId.orNull()).toString();
    }
 
-   public static class LBCookieStickinessPolicy {
-      private String policyName;
-      private Integer cookieExpirationPeriod;
-
-      public LBCookieStickinessPolicy() {
-         super();
-      }
-
-      public LBCookieStickinessPolicy(String policyName, Integer cookieExpirationPeriod) {
-         super();
-         this.policyName = policyName;
-         this.cookieExpirationPeriod = cookieExpirationPeriod;
-      }
-
-      public String getPolicyName() {
-         return policyName;
-      }
-
-      public Integer getCookieExpirationPeriod() {
-         return cookieExpirationPeriod;
-      }
-
-      public void setPolicyName(String policyName) {
-         this.policyName = policyName;
-      }
-
-      public void setCookieExpirationPeriod(Integer cookieExpirationPeriod) {
-         this.cookieExpirationPeriod = cookieExpirationPeriod;
-      }
-
-      @Override
-      public int hashCode() {
-         final int prime = 31;
-         int result = 1;
-         result = prime * result + ((cookieExpirationPeriod == null) ? 0 : cookieExpirationPeriod.hashCode());
-         result = prime * result + ((policyName == null) ? 0 : policyName.hashCode());
-         return result;
-      }
-
-      @Override
-      public boolean equals(Object obj) {
-         if (this == obj)
-            return true;
-         if (obj == null)
-            return false;
-         if (getClass() != obj.getClass())
-            return false;
-         LBCookieStickinessPolicy other = (LBCookieStickinessPolicy) obj;
-         if (cookieExpirationPeriod == null) {
-            if (other.cookieExpirationPeriod != null)
-               return false;
-         } else if (!cookieExpirationPeriod.equals(other.cookieExpirationPeriod))
-            return false;
-         if (policyName == null) {
-            if (other.policyName != null)
-               return false;
-         } else if (!policyName.equals(other.policyName))
-            return false;
-         return true;
-      }
-
-      @Override
-      public String toString() {
-         return "[policyName=" + policyName + ", cookieExpirationPeriod=" + cookieExpirationPeriod + "]";
-      }
-
-   }
-
-   public static class LoadBalancerListener {
-      // TODO: missing SSLCertificateId
-      private Set<String> policyNames;
-      private Integer instancePort;
-      private Integer loadBalancerPort;
-      private String protocol;
-
-      public LoadBalancerListener(Set<String> policyNames, Integer instancePort, Integer loadBalancerPort,
-               String protocol) {
-         super();
-         this.policyNames = policyNames;
-         this.instancePort = instancePort;
-         this.loadBalancerPort = loadBalancerPort;
-         this.protocol = protocol;
-      }
-
-      public LoadBalancerListener() {
-         super();
-      }
-
-      public Set<String> getPolicyNames() {
-         return policyNames;
-      }
-
-      public Integer getInstancePort() {
-         return instancePort;
-      }
-
-      public Integer getLoadBalancerPort() {
-         return loadBalancerPort;
-      }
-
-      public String getProtocol() {
-         return protocol;
-      }
-
-      public void setPolicyNames(Set<String> policyNames) {
-         this.policyNames = policyNames;
-      }
-
-      public void setInstancePort(Integer instancePort) {
-         this.instancePort = instancePort;
-      }
-
-      public void setLoadBalancerPort(Integer loadBalancerPort) {
-         this.loadBalancerPort = loadBalancerPort;
-      }
-
-      public void setProtocol(String protocol) {
-         this.protocol = protocol;
-      }
-
-      @Override
-      public int hashCode() {
-         final int prime = 31;
-         int result = 1;
-         result = prime * result + ((instancePort == null) ? 0 : instancePort.hashCode());
-         result = prime * result + ((loadBalancerPort == null) ? 0 : loadBalancerPort.hashCode());
-         result = prime * result + ((policyNames == null) ? 0 : policyNames.hashCode());
-         result = prime * result + ((protocol == null) ? 0 : protocol.hashCode());
-         return result;
-      }
-
-      @Override
-      public boolean equals(Object obj) {
-         if (this == obj)
-            return true;
-         if (obj == null)
-            return false;
-         if (getClass() != obj.getClass())
-            return false;
-         LoadBalancerListener other = (LoadBalancerListener) obj;
-         if (instancePort == null) {
-            if (other.instancePort != null)
-               return false;
-         } else if (!instancePort.equals(other.instancePort))
-            return false;
-         if (loadBalancerPort == null) {
-            if (other.loadBalancerPort != null)
-               return false;
-         } else if (!loadBalancerPort.equals(other.loadBalancerPort))
-            return false;
-         if (policyNames == null) {
-            if (other.policyNames != null)
-               return false;
-         } else if (!policyNames.equals(other.policyNames))
-            return false;
-         if (protocol == null) {
-            if (other.protocol != null)
-               return false;
-         } else if (!protocol.equals(other.protocol))
-            return false;
-         return true;
-      }
-
-      @Override
-      public String toString() {
-         return "[policyNames=" + policyNames + ", instancePort=" + instancePort + ", loadBalancerPort="
-                  + loadBalancerPort + ", protocol=" + protocol + "]";
-      }
-
-   }
 }
diff --git a/labs/elb/src/main/java/org/jclouds/elb/domain/Policy.java b/labs/elb/src/main/java/org/jclouds/elb/domain/Policy.java
new file mode 100644
index 0000000..84b895e
--- /dev/null
+++ b/labs/elb/src/main/java/org/jclouds/elb/domain/Policy.java
@@ -0,0 +1,164 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.elb.domain;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.Multimap;
+
+/**
+ * 
+ * @see <a
+ *      href="http://docs.amazonwebservices.com/ElasticLoadBalancing/latest/APIReference/API_DescribeLoadBalancerPolicies.html"
+ *      >doc</a>
+ * 
+ * @author Adrian Cole
+ */
+public class Policy {
+
+   public static Builder<?> builder() {
+      return new ConcreteBuilder();
+   }
+
+   public Builder<?> toBuilder() {
+      return new ConcreteBuilder().fromPolicy(this);
+   }
+
+   public static abstract class Builder<T extends Builder<T>> {
+      protected abstract T self();
+
+      protected String name;
+      protected String typeName;
+      protected ImmutableMultimap.Builder<String, Object> attributes = ImmutableMultimap.<String, Object> builder();
+
+      /**
+       * @see Policy#getName()
+       */
+      public T name(String name) {
+         this.name = name;
+         return self();
+      }
+
+      /**
+       * @see Policy#getTypeName()
+       */
+      public T typeName(String typeName) {
+         this.typeName = typeName;
+         return self();
+      }
+
+      /**
+       * @see Policy#getAttributes()
+       */
+      public T attributes(Multimap<String, Object> attributes) {
+         this.attributes.putAll(checkNotNull(attributes, "attributes"));
+         return self();
+      }
+
+      /**
+       * @see Policy#getAttributes()
+       */
+      public T attribute(String key, Object value) {
+         this.attributes.put(checkNotNull(key, "key"), checkNotNull(value, "value"));
+         return self();
+      }
+
+      public Policy build() {
+         return new Policy(name, typeName, attributes.build());
+      }
+
+      public T fromPolicy(Policy in) {
+         return this.name(in.getName()).typeName(in.getTypeName()).attributes(in.getAttributes());
+      }
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
+   }
+
+   protected final String name;
+   protected final String typeName;
+   protected final Multimap<String, Object> attributes;
+
+   protected Policy(String name, String typeName, Multimap<String, Object> attributes) {
+      this.name = checkNotNull(name, "name");
+      this.typeName = checkNotNull(typeName, "typeName");
+      this.attributes = ImmutableMultimap.copyOf(checkNotNull(attributes, "attributes"));
+   }
+
+   /**
+    * The name of the policy associated with the LoadBalancer
+    */
+   public String getName() {
+      return name;
+   }
+
+   /**
+    * The name of the policy type associated with the LoadBalancer.
+    */
+   public String getTypeName() {
+      return typeName;
+   }
+
+   /**
+    * A list of policy attribute description structures. Note that values are either Long, Boolean,
+    * or String, depending on {@link AttributeMetadata#getType()}
+    */
+   public Multimap<String, Object> getAttributes() {
+      return attributes;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public int hashCode() {
+      return Objects.hashCode(name, typeName);
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public boolean equals(Object obj) {
+      if (this == obj)
+         return true;
+      if (obj == null)
+         return false;
+      if (getClass() != obj.getClass())
+         return false;
+      Policy other = (Policy) obj;
+      return Objects.equal(this.name, other.name) && Objects.equal(this.typeName, other.typeName);
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public String toString() {
+      return Objects.toStringHelper(this).omitNullValues().add("name", name).add("typeName", typeName)
+               .add("attributes", attributes).toString();
+   }
+
+}
diff --git a/labs/elb/src/main/java/org/jclouds/elb/domain/PolicyType.java b/labs/elb/src/main/java/org/jclouds/elb/domain/PolicyType.java
new file mode 100644
index 0000000..84d60f1
--- /dev/null
+++ b/labs/elb/src/main/java/org/jclouds/elb/domain/PolicyType.java
@@ -0,0 +1,165 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.elb.domain;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.Set;
+
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * 
+ * @see <a
+ *      href="http://docs.amazonwebservices.com/ElasticLoadBalancing/latest/APIReference/API_PolicyTypeDescription.html"
+ *      >doc</a>
+ * 
+ * @author Adrian Cole
+ */
+public class PolicyType {
+
+   public static Builder<?> builder() {
+      return new ConcreteBuilder();
+   }
+
+   public Builder<?> toBuilder() {
+      return new ConcreteBuilder().fromPolicyType(this);
+   }
+
+   public static abstract class Builder<T extends Builder<T>> {
+      protected abstract T self();
+
+      protected String name;
+      protected String description;
+      protected ImmutableSet.Builder<AttributeMetadata<?>> attributeMetadata = ImmutableSet.<AttributeMetadata<?>> builder();
+
+      /**
+       * @see PolicyType#getName()
+       */
+      public T name(String name) {
+         this.name = name;
+         return self();
+      }
+
+      /**
+       * @see PolicyType#getDescription()
+       */
+      public T description(String description) {
+         this.description = description;
+         return self();
+      }
+
+      /**
+       * @see PolicyType#getAttributeMetadata()
+       */
+      public T attributeMetadata(Iterable<AttributeMetadata<?>> attributeMetadata) {
+         this.attributeMetadata.addAll(checkNotNull(attributeMetadata, "attributeMetadata"));
+         return self();
+      }
+
+      /**
+       * @see PolicyType#getAttributeMetadata()
+       */
+      public T attributeMetadata(AttributeMetadata<?> attributeMetadata) {
+         this.attributeMetadata.add(checkNotNull(attributeMetadata, "attributeMetadata"));
+         return self();
+      }
+
+      public PolicyType build() {
+         return new PolicyType(name, description, attributeMetadata.build());
+      }
+
+      public T fromPolicyType(PolicyType in) {
+         return this.name(in.getName()).description(in.getDescription()).attributeMetadata(in.getAttributeMetadata());
+      }
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
+   }
+
+   protected final String name;
+   protected final String description;
+   protected final Set<AttributeMetadata<?>> attributeMetadata;
+
+   protected PolicyType(String name, String description, Iterable<AttributeMetadata<?>> attributeMetadata) {
+      this.name = checkNotNull(name, "name");
+      this.description = checkNotNull(description, "description");
+      this.attributeMetadata = ImmutableSet.copyOf(checkNotNull(attributeMetadata, "attributeMetadata"));
+   }
+
+   /**
+    * The name of the policy type.
+    */
+   public String getName() {
+      return name;
+   }
+
+   /**
+    * A human-readable description of the policy type.
+    */
+   public String getDescription() {
+      return description;
+   }
+
+   /**
+    * The description of the policy attributes associated with the LoadBalancer policies defined by
+    * the Elastic Load Balancing service.
+    */
+   public Set<AttributeMetadata<?>> getAttributeMetadata() {
+      return attributeMetadata;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public int hashCode() {
+      return Objects.hashCode(name);
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public boolean equals(Object obj) {
+      if (this == obj)
+         return true;
+      if (obj == null)
+         return false;
+      if (getClass() != obj.getClass())
+         return false;
+      PolicyType other = (PolicyType) obj;
+      return Objects.equal(this.name, other.name);
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public String toString() {
+      return Objects.toStringHelper(this).omitNullValues().add("name", name).add("description", description)
+               .add("attributeMetadata", attributeMetadata).toString();
+   }
+
+}
diff --git a/labs/elb/src/main/java/org/jclouds/elb/domain/Protocol.java b/labs/elb/src/main/java/org/jclouds/elb/domain/Protocol.java
new file mode 100644
index 0000000..508386c
--- /dev/null
+++ b/labs/elb/src/main/java/org/jclouds/elb/domain/Protocol.java
@@ -0,0 +1,48 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.jclouds.elb.domain;
+
+/**
+ * Specifies transport protocol to use for routing or the protocol to use for routing traffic to
+ * back-end instances.
+ * 
+ * <h3>Note</h3> If the front-end protocol is HTTP or HTTPS, InstanceProtocol has to be at the same
+ * protocol layer, i.e., HTTP or HTTPS. Likewise, if the front-end protocol is TCP or SSL,
+ * InstanceProtocol has to be TCP or SSL.
+ * 
+ * <h3>Note</h3> If there is another listener with the same InstancePort whose InstanceProtocol is
+ * secure, i.e., HTTPS or SSL, the listener's InstanceProtocol has to be secure, i.e., HTTPS or SSL.
+ * If there is another listener with the same InstancePort whose InstanceProtocol is HTTP or TCP,
+ * the listener's InstanceProtocol must be either HTTP or TCP.
+ * 
+ * @author Adrian Cole
+ * @see <a href =
+ *      "http://docs.amazonwebservices.com/ElasticLoadBalancing/latest/APIReference/API_Listener.html">
+ *      docs</a>
+ */
+public enum Protocol {
+
+   HTTP, HTTPS, TCP, SSL,
+   /**
+    * The protocol was returned unrecognized.
+    */
+   UNRECOGNIZED;
+
+}
diff --git a/labs/elb/src/main/java/org/jclouds/elb/features/LoadBalancerAsyncClient.java b/labs/elb/src/main/java/org/jclouds/elb/features/LoadBalancerAsyncClient.java
new file mode 100644
index 0000000..7c6ffc2
--- /dev/null
+++ b/labs/elb/src/main/java/org/jclouds/elb/features/LoadBalancerAsyncClient.java
@@ -0,0 +1,80 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.elb.features;
+
+import javax.ws.rs.FormParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+
+import org.jclouds.aws.filters.FormSigner;
+import org.jclouds.collect.PaginatedSet;
+import org.jclouds.elb.domain.LoadBalancer;
+import org.jclouds.elb.options.ListLoadBalancersOptions;
+import org.jclouds.elb.xml.DescribeLoadBalancersResultHandler;
+import org.jclouds.elb.xml.LoadBalancerHandler;
+import org.jclouds.rest.annotations.ExceptionParser;
+import org.jclouds.rest.annotations.FormParams;
+import org.jclouds.rest.annotations.RequestFilters;
+import org.jclouds.rest.annotations.VirtualHost;
+import org.jclouds.rest.annotations.XMLResponseParser;
+import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ * Provides access to Amazon ELB via the Query API
+ * <p/>
+ * 
+ * @see <a href="http://docs.amazonwebservices.com/ElasticLoadBalancing/latest/APIReference" >doc</a>
+ * @see LoadBalancerClient
+ * @author Adrian Cole
+ */
+@RequestFilters(FormSigner.class)
+@VirtualHost
+public interface LoadBalancerAsyncClient {
+   
+   /**
+    * @see LoadBalancerClient#get()
+    */
+   @POST
+   @Path("/")
+   @XMLResponseParser(LoadBalancerHandler.class)
+   @FormParams(keys = "Action", values = "DescribeLoadBalancers")
+   @ExceptionParser(ReturnNullOnNotFoundOr404.class)
+   ListenableFuture<LoadBalancer> get(@FormParam("LoadBalancerNames.member.1") String name);
+   
+   /**
+    * @see LoadBalancerClient#list()
+    */
+   @POST
+   @Path("/")
+   @XMLResponseParser(DescribeLoadBalancersResultHandler.class)
+   @FormParams(keys = "Action", values = "DescribeLoadBalancers")
+   ListenableFuture<PaginatedSet<LoadBalancer>> list();
+
+   /**
+    * @see LoadBalancerClient#list(ListLoadBalancersOptions)
+    */
+   @POST
+   @Path("/")
+   @XMLResponseParser(DescribeLoadBalancersResultHandler.class)
+   @FormParams(keys = "Action", values = "DescribeLoadBalancers")
+   ListenableFuture<PaginatedSet<LoadBalancer>> list(ListLoadBalancersOptions options);
+
+}
diff --git a/labs/elb/src/main/java/org/jclouds/elb/features/LoadBalancerClient.java b/labs/elb/src/main/java/org/jclouds/elb/features/LoadBalancerClient.java
new file mode 100644
index 0000000..a55642b
--- /dev/null
+++ b/labs/elb/src/main/java/org/jclouds/elb/features/LoadBalancerClient.java
@@ -0,0 +1,70 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.elb.features;
+
+import java.util.concurrent.TimeUnit;
+
+import org.jclouds.collect.PaginatedSet;
+import org.jclouds.concurrent.Timeout;
+import org.jclouds.elb.domain.LoadBalancer;
+import org.jclouds.elb.options.ListLoadBalancersOptions;
+import org.jclouds.javax.annotation.Nullable;
+
+/**
+ * Provides access to Amazon ELB via the Query API
+ * <p/>
+ * 
+ * @see <a href="http://docs.amazonwebservices.com/ElasticLoadBalancing/latest/APIReference" />
+ * @author Adrian Cole
+ */
+@Timeout(duration = 30, timeUnit = TimeUnit.SECONDS)
+public interface LoadBalancerClient {
+   
+   /**
+    * Retrieves information about the specified loadBalancer.
+    * 
+    * @param name
+    *           Name of the loadBalancer to get information about.
+    * @return null if not found
+    */
+   @Nullable
+   LoadBalancer get(String name);
+
+   /**
+    * Returns detailed configuration information for the specified LoadBalancers. If there are none, the action returns an
+    * empty list.
+    * 
+    * <br/>
+    * You can paginate the results using the {@link ListLoadBalancersOptions parameter}
+    * 
+    * @param options
+    *           the options describing the loadBalancers query
+    * 
+    * @return the response object
+    */
+   PaginatedSet<LoadBalancer> list(ListLoadBalancersOptions options);
+
+   /**
+    * Lists the loadBalancers all load balancers
+    * 
+    * @return the response object
+    */
+   PaginatedSet<LoadBalancer> list();
+
+}
diff --git a/labs/elb/src/main/java/org/jclouds/elb/features/PolicyAsyncClient.java b/labs/elb/src/main/java/org/jclouds/elb/features/PolicyAsyncClient.java
new file mode 100644
index 0000000..83858a4
--- /dev/null
+++ b/labs/elb/src/main/java/org/jclouds/elb/features/PolicyAsyncClient.java
@@ -0,0 +1,120 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.elb.features;
+
+import java.util.Set;
+
+import javax.ws.rs.FormParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+
+import org.jclouds.aws.filters.FormSigner;
+import org.jclouds.elb.binders.BindPolicyTypeNamesToIndexedFormParams;
+import org.jclouds.elb.domain.Policy;
+import org.jclouds.elb.domain.PolicyType;
+import org.jclouds.elb.options.ListPoliciesOptions;
+import org.jclouds.elb.xml.DescribeLoadBalancerPoliciesResultHandler;
+import org.jclouds.elb.xml.DescribeLoadBalancerPolicyTypesResultHandler;
+import org.jclouds.elb.xml.PolicyHandler;
+import org.jclouds.elb.xml.PolicyTypeHandler;
+import org.jclouds.rest.annotations.BinderParam;
+import org.jclouds.rest.annotations.ExceptionParser;
+import org.jclouds.rest.annotations.FormParams;
+import org.jclouds.rest.annotations.RequestFilters;
+import org.jclouds.rest.annotations.VirtualHost;
+import org.jclouds.rest.annotations.XMLResponseParser;
+import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404;
+import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ * Provides access to Amazon ELB via the Query API
+ * <p/>
+ * 
+ * @see <a href="http://docs.amazonwebservices.com/ElasticLoadBalancing/latest/APIReference" >doc</a>
+ * @see PolicyClient
+ * @author Adrian Cole
+ */
+@RequestFilters(FormSigner.class)
+@VirtualHost
+public interface PolicyAsyncClient {
+   
+   /**
+    * @see PolicyClient#get()
+    */
+   @POST
+   @Path("/")
+   @XMLResponseParser(PolicyHandler.class)
+   @FormParams(keys = "Action", values = "DescribeLoadBalancerPolicies")
+   @ExceptionParser(ReturnNullOnNotFoundOr404.class)
+   ListenableFuture<Policy> get(@FormParam("PolicyNames.member.1") String name);
+   
+   /**
+    * @see PolicyClient#list()
+    */
+   @POST
+   @Path("/")
+   @XMLResponseParser(DescribeLoadBalancerPoliciesResultHandler.class)
+   @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
+   @FormParams(keys = "Action", values = "DescribeLoadBalancerPolicies")
+   ListenableFuture<Set<Policy>> list();
+
+   /**
+    * @see PolicyClient#list(ListPoliciesOptions)
+    */
+   @POST
+   @Path("/")
+   @XMLResponseParser(DescribeLoadBalancerPoliciesResultHandler.class)
+   @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
+   @FormParams(keys = "Action", values = "DescribeLoadBalancerPolicies")
+   ListenableFuture<Set<Policy>> list(ListPoliciesOptions options);
+
+   
+   /**
+    * @see PolicyClient#getType()
+    */
+   @POST
+   @Path("/")
+   @XMLResponseParser(PolicyTypeHandler.class)
+   @FormParams(keys = "Action", values = "DescribeLoadBalancerPolicyTypes")
+   @ExceptionParser(ReturnNullOnNotFoundOr404.class)
+   ListenableFuture<PolicyType> getType(@FormParam("PolicyTypeNames.member.1") String name);
+   
+   /**
+    * @see PolicyClient#listTypes()
+    */
+   @POST
+   @Path("/")
+   @XMLResponseParser(DescribeLoadBalancerPolicyTypesResultHandler.class)
+   @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
+   @FormParams(keys = "Action", values = "DescribeLoadBalancerPolicyTypes")
+   ListenableFuture<Set<PolicyType>> listTypes();
+
+   /**
+    * @see PolicyClient#listTypes(Iterable<String>)
+    */
+   @POST
+   @Path("/")
+   @XMLResponseParser(DescribeLoadBalancerPolicyTypesResultHandler.class)
+   @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
+   @FormParams(keys = "Action", values = "DescribeLoadBalancerPolicyTypes")
+   ListenableFuture<Set<PolicyType>> listTypes(@BinderParam(BindPolicyTypeNamesToIndexedFormParams.class) Iterable<String> names);
+   
+}
diff --git a/labs/elb/src/main/java/org/jclouds/elb/features/PolicyClient.java b/labs/elb/src/main/java/org/jclouds/elb/features/PolicyClient.java
new file mode 100644
index 0000000..64da46e
--- /dev/null
+++ b/labs/elb/src/main/java/org/jclouds/elb/features/PolicyClient.java
@@ -0,0 +1,101 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.elb.features;
+
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+import org.jclouds.concurrent.Timeout;
+import org.jclouds.elb.domain.Policy;
+import org.jclouds.elb.domain.PolicyType;
+import org.jclouds.elb.options.ListPoliciesOptions;
+import org.jclouds.javax.annotation.Nullable;
+
+/**
+ * Provides access to Amazon ELB via the Query API
+ * <p/>
+ * 
+ * @see <a href="http://docs.amazonwebservices.com/ElasticLoadBalancing/latest/APIReference" />
+ * @author Adrian Cole
+ */
+@Timeout(duration = 30, timeUnit = TimeUnit.SECONDS)
+public interface PolicyClient {
+
+   /**
+    * Retrieves information about the specified policy.
+    * 
+    * @param name
+    *           Name of the policy to get information about.
+    * @return null if not found
+    */
+   @Nullable
+   Policy get(String name);
+
+   /**
+    * Returns detailed descriptions of the policies.
+    * 
+    * If you specify a LoadBalancer name, the operation returns either the descriptions of the
+    * specified policies, or descriptions of all the policies created for the LoadBalancer. If you
+    * don't specify a LoadBalancer name, the operation returns descriptions of the specified sample
+    * policies, or descriptions of all the sample policies. The names of the sample policies have
+    * the ELBSample- prefix.
+    * 
+    * @param options
+    *           the options describing the policies query
+    * 
+    * @return the response object
+    */
+   Set<Policy> list(ListPoliciesOptions options);
+
+   /**
+    * returns descriptions of the specified sample policies, or descriptions of all the sample
+    * policies.
+    * 
+    * @return the response object
+    */
+   Set<Policy> list();
+
+   /**
+    * Retrieves information about the specified policy type.
+    * 
+    * @param name
+    *           Name of the policy type to get information about.
+    * @return null if not found
+    */
+   @Nullable
+   PolicyType getType(String name);
+
+   /**
+    * Returns meta-information on the specified LoadBalancer policies defined by the Elastic Load
+    * Balancing service. The policy types that are returned from this action can be used in a
+    * CreateLoadBalancerPolicy action to instantiate specific policy configurations that will be
+    * applied to an Elastic LoadBalancer.
+    * 
+    * @return the response object
+    */
+   Set<PolicyType> listTypes();
+
+   /**
+    * @param names Specifies the name of the policy types. If no names are specified, returns the description of all the policy types defined by Elastic Load Balancing service.
+    * 
+    * @see #listTypes()
+    */
+   Set<PolicyType> listTypes(Iterable<String> names);
+
+}
diff --git a/labs/elb/src/main/java/org/jclouds/elb/loadbalancer/config/ELBLoadBalancerServiceDependenciesModule.java b/labs/elb/src/main/java/org/jclouds/elb/loadbalancer/config/ELBLoadBalancerServiceDependenciesModule.java
index 77abdd1..3cc7efe 100644
--- a/labs/elb/src/main/java/org/jclouds/elb/loadbalancer/config/ELBLoadBalancerServiceDependenciesModule.java
+++ b/labs/elb/src/main/java/org/jclouds/elb/loadbalancer/config/ELBLoadBalancerServiceDependenciesModule.java
@@ -18,7 +18,7 @@
  */
 package org.jclouds.elb.loadbalancer.config;
 
-import org.jclouds.elb.domain.LoadBalancer;
+import org.jclouds.elb.domain.CrappyLoadBalancer;
 import org.jclouds.elb.loadbalancer.functions.LoadBalancerToLoadBalancerMetadata;
 import org.jclouds.loadbalancer.domain.LoadBalancerMetadata;
 
@@ -34,7 +34,7 @@
 
    @Override
    protected void configure() {
-      bind(new TypeLiteral<Function<LoadBalancer, LoadBalancerMetadata>>() {
+      bind(new TypeLiteral<Function<CrappyLoadBalancer, LoadBalancerMetadata>>() {
       }).to(LoadBalancerToLoadBalancerMetadata.class);
    }
 
diff --git a/labs/elb/src/main/java/org/jclouds/elb/loadbalancer/functions/LoadBalancerToLoadBalancerMetadata.java b/labs/elb/src/main/java/org/jclouds/elb/loadbalancer/functions/LoadBalancerToLoadBalancerMetadata.java
index dcd09d0..33c9fd2 100644
--- a/labs/elb/src/main/java/org/jclouds/elb/loadbalancer/functions/LoadBalancerToLoadBalancerMetadata.java
+++ b/labs/elb/src/main/java/org/jclouds/elb/loadbalancer/functions/LoadBalancerToLoadBalancerMetadata.java
@@ -27,7 +27,7 @@
 
 import org.jclouds.collect.Memoized;
 import org.jclouds.domain.Location;
-import org.jclouds.elb.domain.LoadBalancer;
+import org.jclouds.elb.domain.CrappyLoadBalancer;
 import org.jclouds.loadbalancer.domain.LoadBalancerMetadata;
 import org.jclouds.loadbalancer.domain.LoadBalancerType;
 import org.jclouds.loadbalancer.domain.internal.LoadBalancerMetadataImpl;
@@ -45,7 +45,7 @@
  * @author Adrian Cole
  */
 @Singleton
-public class LoadBalancerToLoadBalancerMetadata implements Function<LoadBalancer, LoadBalancerMetadata> {
+public class LoadBalancerToLoadBalancerMetadata implements Function<CrappyLoadBalancer, LoadBalancerMetadata> {
    @Resource
    protected static Logger logger = Logger.NULL;
    
@@ -60,7 +60,7 @@
    }
 
    @Override
-   public LoadBalancerMetadata apply(LoadBalancer input) {
+   public LoadBalancerMetadata apply(CrappyLoadBalancer input) {
 
       Location location = input.getRegion() != null ? findLocationWithId(input.getRegion()) : defaultLocationSupplier
                .get();
diff --git a/labs/elb/src/main/java/org/jclouds/elb/loadbalancer/strategy/ELBGetLoadBalancerMetadataStrategy.java b/labs/elb/src/main/java/org/jclouds/elb/loadbalancer/strategy/ELBGetLoadBalancerMetadataStrategy.java
index 81e5b6a..519a243 100644
--- a/labs/elb/src/main/java/org/jclouds/elb/loadbalancer/strategy/ELBGetLoadBalancerMetadataStrategy.java
+++ b/labs/elb/src/main/java/org/jclouds/elb/loadbalancer/strategy/ELBGetLoadBalancerMetadataStrategy.java
@@ -28,7 +28,7 @@
 import javax.inject.Singleton;
 
 import org.jclouds.elb.ELBClient;
-import org.jclouds.elb.domain.LoadBalancer;
+import org.jclouds.elb.domain.CrappyLoadBalancer;
 import org.jclouds.loadbalancer.domain.LoadBalancerMetadata;
 import org.jclouds.loadbalancer.strategy.GetLoadBalancerMetadataStrategy;
 
@@ -42,10 +42,10 @@
 public class ELBGetLoadBalancerMetadataStrategy implements GetLoadBalancerMetadataStrategy {
 
    private final ELBClient client;
-   private final Function<LoadBalancer, LoadBalancerMetadata> converter;
+   private final Function<CrappyLoadBalancer, LoadBalancerMetadata> converter;
 
    @Inject
-   protected ELBGetLoadBalancerMetadataStrategy(ELBClient client, Function<LoadBalancer, LoadBalancerMetadata> converter) {
+   protected ELBGetLoadBalancerMetadataStrategy(ELBClient client, Function<CrappyLoadBalancer, LoadBalancerMetadata> converter) {
       this.client = checkNotNull(client, "client");
       this.converter = checkNotNull(converter, "converter");
    }
diff --git a/labs/elb/src/main/java/org/jclouds/elb/loadbalancer/strategy/ELBListLoadBalancersStrategy.java b/labs/elb/src/main/java/org/jclouds/elb/loadbalancer/strategy/ELBListLoadBalancersStrategy.java
index b23916e..7f82ffb 100644
--- a/labs/elb/src/main/java/org/jclouds/elb/loadbalancer/strategy/ELBListLoadBalancersStrategy.java
+++ b/labs/elb/src/main/java/org/jclouds/elb/loadbalancer/strategy/ELBListLoadBalancersStrategy.java
@@ -35,7 +35,7 @@
 import org.jclouds.Constants;
 import org.jclouds.elb.ELBAsyncClient;
 import org.jclouds.elb.ELBClient;
-import org.jclouds.elb.domain.LoadBalancer;
+import org.jclouds.elb.domain.CrappyLoadBalancer;
 import org.jclouds.loadbalancer.domain.LoadBalancerMetadata;
 import org.jclouds.loadbalancer.reference.LoadBalancerConstants;
 import org.jclouds.loadbalancer.strategy.ListLoadBalancersStrategy;
@@ -58,13 +58,13 @@
 
    private final ELBClient client;
    private final ELBAsyncClient aclient;
-   private final Function<LoadBalancer, LoadBalancerMetadata> converter;
+   private final Function<CrappyLoadBalancer, LoadBalancerMetadata> converter;
    private final ExecutorService executor;
    private final Supplier<Set<String>> regions;
 
    @Inject
    protected ELBListLoadBalancersStrategy(ELBClient client, ELBAsyncClient aclient,
-            Function<LoadBalancer, LoadBalancerMetadata> converter,
+            Function<CrappyLoadBalancer, LoadBalancerMetadata> converter,
             @Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor, @Region Supplier<Set<String>> regions) {
       this.client = checkNotNull(client, "client");
       this.aclient = checkNotNull(aclient, "aclient");
@@ -75,13 +75,13 @@
 
    @Override
    public Iterable<LoadBalancerMetadata> listLoadBalancers() {
-      Iterable<? extends LoadBalancer> loadBalancers;
+      Iterable<? extends CrappyLoadBalancer> loadBalancers;
       Set<String> regions = this.regions.get();
       if (regions.size() > 0)
-         loadBalancers = concat(transformParallel(regions, new Function<String, Future<? extends Set<? extends LoadBalancer>>>() {
+         loadBalancers = concat(transformParallel(regions, new Function<String, Future<? extends Set<? extends CrappyLoadBalancer>>>() {
 
             @Override
-            public ListenableFuture<? extends Set<? extends LoadBalancer>> apply(String from) {
+            public ListenableFuture<Set<? extends CrappyLoadBalancer>> apply(String from) {
                return aclient.describeLoadBalancersInRegion(from);
             }
 
diff --git a/labs/elb/src/main/java/org/jclouds/elb/loadbalancer/strategy/ELBLoadBalanceNodesStrategy.java b/labs/elb/src/main/java/org/jclouds/elb/loadbalancer/strategy/ELBLoadBalanceNodesStrategy.java
index 96f3a69..dc635af 100644
--- a/labs/elb/src/main/java/org/jclouds/elb/loadbalancer/strategy/ELBLoadBalanceNodesStrategy.java
+++ b/labs/elb/src/main/java/org/jclouds/elb/loadbalancer/strategy/ELBLoadBalanceNodesStrategy.java
@@ -33,7 +33,7 @@
 import org.jclouds.compute.domain.NodeMetadata;
 import org.jclouds.domain.Location;
 import org.jclouds.elb.ELBClient;
-import org.jclouds.elb.domain.LoadBalancer;
+import org.jclouds.elb.domain.CrappyLoadBalancer;
 import org.jclouds.loadbalancer.domain.LoadBalancerMetadata;
 import org.jclouds.loadbalancer.reference.LoadBalancerConstants;
 import org.jclouds.loadbalancer.strategy.LoadBalanceNodesStrategy;
@@ -53,10 +53,10 @@
    @Named(LoadBalancerConstants.LOADBALANCER_LOGGER)
    protected Logger logger = Logger.NULL;
    protected final ELBClient client;
-   protected final Function<LoadBalancer, LoadBalancerMetadata> converter;
+   protected final Function<CrappyLoadBalancer, LoadBalancerMetadata> converter;
 
    @Inject
-   protected ELBLoadBalanceNodesStrategy(ELBClient client, Function<LoadBalancer, LoadBalancerMetadata> converter) {
+   protected ELBLoadBalanceNodesStrategy(ELBClient client, Function<CrappyLoadBalancer, LoadBalancerMetadata> converter) {
       this.client = checkNotNull(client, "client");
       this.converter = checkNotNull(converter, "converter");
    }
diff --git a/labs/elb/src/main/java/org/jclouds/elb/options/ListLoadBalancersOptions.java b/labs/elb/src/main/java/org/jclouds/elb/options/ListLoadBalancersOptions.java
new file mode 100644
index 0000000..acf4aeb
--- /dev/null
+++ b/labs/elb/src/main/java/org/jclouds/elb/options/ListLoadBalancersOptions.java
@@ -0,0 +1,159 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.elb.options;
+
+import java.util.Set;
+
+import org.jclouds.http.options.BaseHttpRequestOptions;
+import org.jclouds.javax.annotation.Nullable;
+
+import com.google.common.base.Objects;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.Sets;
+
+/**
+ * Options used to list available loadBalancers.
+ * 
+ * @see <a
+ *      href="http://docs.amazonwebservices.com/ElasticLoadBalancing/latest/APIReference/API_DescribeLoadBalancers.html"
+ *      >docs</a>
+ * 
+ * @author Adrian Cole
+ */
+public class ListLoadBalancersOptions extends BaseHttpRequestOptions implements Cloneable {
+
+   private String marker;
+   private Set<String> names = Sets.newLinkedHashSet();
+
+   /**
+    * @see ListLoadBalancersOptions#getMarker()
+    */
+   public ListLoadBalancersOptions marker(String marker) {
+      this.marker = marker;
+      return this;
+   }
+
+   /**
+    * @see ListLoadBalancersOptions#getNames()
+    */
+   public ListLoadBalancersOptions names(Set<String> names) {
+      this.names = names;
+      return this;
+   }
+
+   /**
+    * @see ListLoadBalancersOptions#getNames()
+    */
+   public ListLoadBalancersOptions name(String name) {
+      this.names.add(name);
+      return this;
+   }
+
+   /**
+    * list of names associated with the LoadBalancers at creation time.
+    */
+   public Set<String> getNames() {
+      return names;
+   }
+
+   /**
+    * Use this parameter only when paginating results, and only in a subsequent request after you've
+    * received a response where the results are truncated. Set it to the value of the Marker element
+    * in the response you just received.
+    */
+   @Nullable
+   public String getMarker() {
+      return marker;
+   }
+
+   public static class Builder {
+
+      /**
+       * @see ListLoadBalancersOptions#getMarker()
+       */
+      public static ListLoadBalancersOptions marker(String marker) {
+         return new ListLoadBalancersOptions().marker(marker);
+      }
+
+      /**
+       * @see ListLoadBalancersOptions#getNames()
+       */
+      public static ListLoadBalancersOptions name(String name) {
+         return new ListLoadBalancersOptions().name(name);
+      }
+
+      /**
+       * @see ListLoadBalancersOptions#getNames()
+       */
+      public static ListLoadBalancersOptions names(Set<String> names) {
+         return new ListLoadBalancersOptions().names(names);
+      }
+   }
+
+   @Override
+   public Multimap<String, String> buildFormParameters() {
+      Multimap<String, String> params = super.buildFormParameters();
+      if (marker != null)
+         params.put("Marker", marker);
+      if (names.size() > 0) {
+         int nameIndex = 1;
+         for (String name : names) {
+            params.put("LoadBalancerNames.member." + nameIndex, name);
+            nameIndex++;
+         }
+      }
+      return params;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public int hashCode() {
+      return Objects.hashCode(marker, names);
+   }
+
+   @Override
+   public ListLoadBalancersOptions clone() {
+      return new ListLoadBalancersOptions().marker(marker).names(names);
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public boolean equals(Object obj) {
+      if (this == obj)
+         return true;
+      if (obj == null)
+         return false;
+      if (getClass() != obj.getClass())
+         return false;
+      ListLoadBalancersOptions other = ListLoadBalancersOptions.class.cast(obj);
+      return Objects.equal(this.marker, other.marker) && Objects.equal(this.names, other.names);
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public String toString() {
+      return Objects.toStringHelper(this).omitNullValues().add("marker", marker).add("names", names).toString();
+   }
+}
diff --git a/labs/elb/src/main/java/org/jclouds/elb/options/ListPoliciesOptions.java b/labs/elb/src/main/java/org/jclouds/elb/options/ListPoliciesOptions.java
new file mode 100644
index 0000000..199bcee
--- /dev/null
+++ b/labs/elb/src/main/java/org/jclouds/elb/options/ListPoliciesOptions.java
@@ -0,0 +1,159 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.elb.options;
+
+import java.util.Set;
+
+import org.jclouds.http.options.BaseHttpRequestOptions;
+
+import com.google.common.base.Objects;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.Sets;
+
+/**
+ * Options used to list available policies.
+ * 
+ * @see <a
+ *      href="http://docs.amazonwebservices.com/ElasticLoadBalancing/latest/APIReference/API_DescribeLoadBalancerPolicies.html"
+ *      >docs</a>
+ * 
+ * @author Adrian Cole
+ */
+public class ListPoliciesOptions extends BaseHttpRequestOptions implements Cloneable {
+
+   private String loadBalancerName;
+   private Set<String> names = Sets.newLinkedHashSet();
+
+   /**
+    * @see ListPoliciesOptions#getLoadBalancerName()
+    */
+   public ListPoliciesOptions loadBalancerName(String loadBalancerName) {
+      this.loadBalancerName = loadBalancerName;
+      return this;
+   }
+
+   /**
+    * @see ListPoliciesOptions#getNames()
+    */
+   public ListPoliciesOptions names(Set<String> names) {
+      this.names = names;
+      return this;
+   }
+
+   /**
+    * @see ListPoliciesOptions#getNames()
+    */
+   public ListPoliciesOptions name(String name) {
+      this.names.add(name);
+      return this;
+   }
+
+   /**
+    * The mnemonic name associated with the LoadBalancer. If no name is specified, the operation
+    * returns the attributes of either all the sample policies pre-defined by Elastic Load Balancing
+    * or the specified sample polices.
+    */
+   public String getLoadBalancerName() {
+      return loadBalancerName;
+   }
+
+   /**
+    * The names of LoadBalancer policies you've created or Elastic Load Balancing sample policy
+    * names.
+    */
+   public Set<String> getNames() {
+      return names;
+   }
+
+   public static class Builder {
+
+      /**
+       * @see ListPoliciesOptions#getLoadBalancerName()
+       */
+      public static ListPoliciesOptions loadBalancerName(String loadBalancerName) {
+         return new ListPoliciesOptions().loadBalancerName(loadBalancerName);
+      }
+
+      /**
+       * @see ListPoliciesOptions#getNames()
+       */
+      public static ListPoliciesOptions name(String name) {
+         return new ListPoliciesOptions().name(name);
+      }
+
+      /**
+       * @see ListPoliciesOptions#getNames()
+       */
+      public static ListPoliciesOptions names(Set<String> names) {
+         return new ListPoliciesOptions().names(names);
+      }
+   }
+
+   @Override
+   public Multimap<String, String> buildFormParameters() {
+      Multimap<String, String> params = super.buildFormParameters();
+      if (loadBalancerName != null)
+         params.put("LoadBalancerName", loadBalancerName);
+      if (names.size() > 0) {
+         int nameIndex = 1;
+         for (String name : names) {
+            params.put("PolicyNames.member." + nameIndex, name);
+            nameIndex++;
+         }
+      }
+      return params;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public int hashCode() {
+      return Objects.hashCode(loadBalancerName, names);
+   }
+
+   @Override
+   public ListPoliciesOptions clone() {
+      return new ListPoliciesOptions().loadBalancerName(loadBalancerName).names(names);
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public boolean equals(Object obj) {
+      if (this == obj)
+         return true;
+      if (obj == null)
+         return false;
+      if (getClass() != obj.getClass())
+         return false;
+      ListPoliciesOptions other = ListPoliciesOptions.class.cast(obj);
+      return Objects.equal(this.loadBalancerName, other.loadBalancerName) && Objects.equal(this.names, other.names);
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public String toString() {
+      return Objects.toStringHelper(this).omitNullValues().add("loadBalancerName", loadBalancerName)
+               .add("names", names).toString();
+   }
+}
diff --git a/labs/elb/src/main/java/org/jclouds/elb/xml/AttributeMetadataHandler.java b/labs/elb/src/main/java/org/jclouds/elb/xml/AttributeMetadataHandler.java
new file mode 100644
index 0000000..7164218
--- /dev/null
+++ b/labs/elb/src/main/java/org/jclouds/elb/xml/AttributeMetadataHandler.java
@@ -0,0 +1,98 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.elb.xml;
+
+import static org.jclouds.util.SaxUtils.currentOrNull;
+import static org.jclouds.util.SaxUtils.equalsOrSuffix;
+
+import org.jclouds.elb.domain.AttributeMetadata;
+import org.jclouds.elb.domain.AttributeMetadata.Cardinality;
+import org.jclouds.http.functions.ParseSax;
+import org.xml.sax.SAXException;
+
+/**
+ * @see <a
+ *      href="http://docs.amazonwebservices.com/ElasticLoadBalancing/latest/APIReference/API_PolicyAttributeTypeDescription.html"
+ *      >xml</a>
+ * 
+ * @author Adrian Cole
+ */
+public class AttributeMetadataHandler extends ParseSax.HandlerForGeneratedRequestWithResult<AttributeMetadata<?>> {
+
+   private StringBuilder currentText = new StringBuilder();
+   @SuppressWarnings("rawtypes")
+   private AttributeMetadata.Builder builder = AttributeMetadata.builder();
+   @SuppressWarnings("rawtypes")
+   private Class currentType = String.class;
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public AttributeMetadata<?> getResult() {
+      try {
+         return builder.build();
+      } finally {
+         builder = AttributeMetadata.builder();
+      }
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @SuppressWarnings("unchecked")
+   @Override
+   public void endElement(String uri, String name, String qName) throws SAXException {
+      if (equalsOrSuffix(qName, "AttributeName")) {
+         builder.name(currentOrNull(currentText));
+      } else if (equalsOrSuffix(qName, "AttributeType")) {
+         String rawType = currentOrNull(currentText);
+         if ("Long".equals(rawType)) {
+            currentType = Long.class;
+         } else if ("Boolean".equals(rawType)) {
+            currentType = Boolean.class;
+         } else {
+            currentType = String.class;
+         }
+         builder.type(currentType);
+         builder.rawType(rawType);
+      } else if (equalsOrSuffix(qName, "Cardinality")) {
+         builder.cardinality(Cardinality.valueOf(currentOrNull(currentText)));
+      } else if (equalsOrSuffix(qName, "DefaultValue")) {
+         Object value = currentOrNull(currentText);
+         if (currentType == Long.class)
+            value = Long.valueOf(currentOrNull(currentText));
+         else if (currentType == Boolean.class)
+            value = Boolean.valueOf(currentOrNull(currentText));
+         builder.defaultValue(value);
+      } else if (equalsOrSuffix(qName, "UnhealthyThreshold")) {
+         builder.description(currentOrNull(currentText));
+      }
+      currentText = new StringBuilder();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public void characters(char ch[], int start, int length) {
+      currentText.append(ch, start, length);
+   }
+
+}
diff --git a/labs/elb/src/main/java/org/jclouds/elb/xml/DescribeLoadBalancerPoliciesResultHandler.java b/labs/elb/src/main/java/org/jclouds/elb/xml/DescribeLoadBalancerPoliciesResultHandler.java
new file mode 100644
index 0000000..1f8b198
--- /dev/null
+++ b/labs/elb/src/main/java/org/jclouds/elb/xml/DescribeLoadBalancerPoliciesResultHandler.java
@@ -0,0 +1,118 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.elb.xml;
+
+import static org.jclouds.util.SaxUtils.equalsOrSuffix;
+
+import java.util.Set;
+
+import org.jclouds.elb.domain.Policy;
+import org.jclouds.http.functions.ParseSax;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSet.Builder;
+import com.google.inject.Inject;
+
+/**
+ * @see <a
+ *      href="http://docs.amazonwebservices.com/ElasticLoadBalancing/latest/APIReference/API_DescribeLoadBalancers.html"
+ *      >docs</a>
+ * 
+ * @author Adrian Cole
+ */
+public class DescribeLoadBalancerPoliciesResultHandler extends
+         ParseSax.HandlerForGeneratedRequestWithResult<Set<Policy>> {
+
+   private final PolicyHandler policyHandler;
+
+   private StringBuilder currentText = new StringBuilder();
+   private Builder<Policy> policies = ImmutableSet.<Policy> builder();
+   private boolean inPolicies;
+
+   protected int memberDepth;
+
+   @Inject
+   public DescribeLoadBalancerPoliciesResultHandler(PolicyHandler policyHandler) {
+      this.policyHandler = policyHandler;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Set<Policy> getResult() {
+      return policies.build();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public void startElement(String url, String name, String qName, Attributes attributes) throws SAXException {
+      if (equalsOrSuffix(qName, "member")) {
+         memberDepth++;
+      } else if (equalsOrSuffix(qName, "PolicyDescriptions")) {
+         inPolicies = true;
+      }
+      if (inPolicies) {
+         policyHandler.startElement(url, name, qName, attributes);
+      }
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public void endElement(String uri, String name, String qName) throws SAXException {
+      if (equalsOrSuffix(qName, "member")) {
+         endMember(uri, name, qName);
+         memberDepth--;
+      } else if (equalsOrSuffix(qName, "PolicyDescriptions")) {
+         inPolicies = false;
+      } else if (inPolicies) {
+         policyHandler.endElement(uri, name, qName);
+      }
+
+      currentText = new StringBuilder();
+   }
+
+   protected void endMember(String uri, String name, String qName) throws SAXException {
+      if (inPolicies) {
+         if (memberDepth == 1)
+            policies.add(policyHandler.getResult());
+         else
+            policyHandler.endElement(uri, name, qName);
+      }
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public void characters(char ch[], int start, int length) {
+      if (inPolicies) {
+         policyHandler.characters(ch, start, length);
+      } else {
+         currentText.append(ch, start, length);
+      }
+   }
+
+}
diff --git a/labs/elb/src/main/java/org/jclouds/elb/xml/DescribeLoadBalancerPolicyTypesResultHandler.java b/labs/elb/src/main/java/org/jclouds/elb/xml/DescribeLoadBalancerPolicyTypesResultHandler.java
new file mode 100644
index 0000000..9536f67
--- /dev/null
+++ b/labs/elb/src/main/java/org/jclouds/elb/xml/DescribeLoadBalancerPolicyTypesResultHandler.java
@@ -0,0 +1,118 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.elb.xml;
+
+import static org.jclouds.util.SaxUtils.equalsOrSuffix;
+
+import java.util.Set;
+
+import org.jclouds.elb.domain.PolicyType;
+import org.jclouds.http.functions.ParseSax;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSet.Builder;
+import com.google.inject.Inject;
+
+/**
+ * @see <a
+ *      href="http://docs.amazonwebservices.com/ElasticLoadBalancing/latest/APIReference/API_DescribeLoadBalancerPolicyTypeTypes.html"
+ *      >docs</a>
+ * 
+ * @author Adrian Cole
+ */
+public class DescribeLoadBalancerPolicyTypesResultHandler extends
+         ParseSax.HandlerForGeneratedRequestWithResult<Set<PolicyType>> {
+
+   private final PolicyTypeHandler policyTypeHandler;
+
+   private StringBuilder currentText = new StringBuilder();
+   private Builder<PolicyType> policyTypes = ImmutableSet.<PolicyType> builder();
+   private boolean inPolicyTypeTypes;
+
+   protected int memberDepth;
+
+   @Inject
+   public DescribeLoadBalancerPolicyTypesResultHandler(PolicyTypeHandler policyTypeHandler) {
+      this.policyTypeHandler = policyTypeHandler;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Set<PolicyType> getResult() {
+      return policyTypes.build();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public void startElement(String url, String name, String qName, Attributes attributes) throws SAXException {
+      if (equalsOrSuffix(qName, "member")) {
+         memberDepth++;
+      } else if (equalsOrSuffix(qName, "PolicyTypeDescriptions")) {
+         inPolicyTypeTypes = true;
+      }
+      if (inPolicyTypeTypes) {
+         policyTypeHandler.startElement(url, name, qName, attributes);
+      }
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public void endElement(String uri, String name, String qName) throws SAXException {
+      if (equalsOrSuffix(qName, "member")) {
+         endMember(uri, name, qName);
+         memberDepth--;
+      } else if (equalsOrSuffix(qName, "PolicyTypeDescriptions")) {
+         inPolicyTypeTypes = false;
+      } else if (inPolicyTypeTypes) {
+         policyTypeHandler.endElement(uri, name, qName);
+      }
+
+      currentText = new StringBuilder();
+   }
+
+   protected void endMember(String uri, String name, String qName) throws SAXException {
+      if (inPolicyTypeTypes) {
+         if (memberDepth == 1)
+            policyTypes.add(policyTypeHandler.getResult());
+         else
+            policyTypeHandler.endElement(uri, name, qName);
+      }
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public void characters(char ch[], int start, int length) {
+      if (inPolicyTypeTypes) {
+         policyTypeHandler.characters(ch, start, length);
+      } else {
+         currentText.append(ch, start, length);
+      }
+   }
+
+}
diff --git a/labs/elb/src/main/java/org/jclouds/elb/xml/DescribeLoadBalancersResponseHandler.java b/labs/elb/src/main/java/org/jclouds/elb/xml/DescribeLoadBalancersResponseHandler.java
index e22b9c6..19923e1 100644
--- a/labs/elb/src/main/java/org/jclouds/elb/xml/DescribeLoadBalancersResponseHandler.java
+++ b/labs/elb/src/main/java/org/jclouds/elb/xml/DescribeLoadBalancersResponseHandler.java
@@ -25,10 +25,10 @@
 
 import org.jclouds.aws.util.AWSUtils;
 import org.jclouds.date.DateService;
-import org.jclouds.elb.domain.LoadBalancer;
-import org.jclouds.elb.domain.LoadBalancer.AppCookieStickinessPolicy;
-import org.jclouds.elb.domain.LoadBalancer.LBCookieStickinessPolicy;
-import org.jclouds.elb.domain.LoadBalancer.LoadBalancerListener;
+import org.jclouds.elb.domain.CrappyLoadBalancer;
+import org.jclouds.elb.domain.CrappyLoadBalancer.AppCookieStickinessPolicy;
+import org.jclouds.elb.domain.CrappyLoadBalancer.LBCookieStickinessPolicy;
+import org.jclouds.elb.domain.CrappyLoadBalancer.LoadBalancerListener;
 import org.jclouds.http.HttpRequest;
 import org.jclouds.http.functions.ParseSax;
 import org.jclouds.logging.Logger;
@@ -41,7 +41,7 @@
  * @author Lili Nadar
  */
 public class DescribeLoadBalancersResponseHandler extends
-      ParseSax.HandlerForGeneratedRequestWithResult<Set<LoadBalancer>> {
+      ParseSax.HandlerForGeneratedRequestWithResult<Set<CrappyLoadBalancer>> {
    private final DateService dateService;
    private final LoadBalancerListenerHandler listenerHandler;
 
@@ -54,7 +54,7 @@
    @Resource
    protected Logger logger = Logger.NULL;
 
-   private Set<LoadBalancer> contents = Sets.newLinkedHashSet();
+   private Set<CrappyLoadBalancer> contents = Sets.newLinkedHashSet();
    private StringBuilder currentText = new StringBuilder();
 
    private boolean inListenerDescriptions = false;
@@ -65,7 +65,7 @@
    // TODO unused?
    private boolean inLoadBalancerDescriptions = false;
 
-   private LoadBalancer elb;
+   private CrappyLoadBalancer elb;
    // TODO unused?
    private AppCookieStickinessPolicy appCookieStickinessPolicy;
    // TODO unused?
@@ -89,7 +89,7 @@
 
       if (qName.equals("member")) {
          if (!(inListenerDescriptions || inAppCookieStickinessPolicies || inInstances || inLBCookieStickinessPolicies || inAvailabilityZones)) {
-            elb = new LoadBalancer();
+            elb = new CrappyLoadBalancer();
          }
       }
    }
@@ -141,7 +141,7 @@
    }
 
    @Override
-   public Set<LoadBalancer> getResult() {
+   public Set<CrappyLoadBalancer> getResult() {
       return contents;
    }
 
diff --git a/labs/elb/src/main/java/org/jclouds/elb/xml/DescribeLoadBalancersResultHandler.java b/labs/elb/src/main/java/org/jclouds/elb/xml/DescribeLoadBalancersResultHandler.java
new file mode 100644
index 0000000..923c3e2
--- /dev/null
+++ b/labs/elb/src/main/java/org/jclouds/elb/xml/DescribeLoadBalancersResultHandler.java
@@ -0,0 +1,122 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.elb.xml;
+
+import static org.jclouds.util.SaxUtils.currentOrNull;
+import static org.jclouds.util.SaxUtils.equalsOrSuffix;
+
+import java.util.Set;
+
+import org.jclouds.collect.PaginatedSet;
+import org.jclouds.elb.domain.LoadBalancer;
+import org.jclouds.http.functions.ParseSax;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+
+import com.google.common.collect.Sets;
+import com.google.inject.Inject;
+
+/**
+ * @see <a
+ *      href="http://docs.amazonwebservices.com/ElasticLoadBalancing/latest/APIReference/API_DescribeLoadBalancers.html"
+ *      >docs</a>
+ * 
+ * @author Adrian Cole
+ */
+public class DescribeLoadBalancersResultHandler extends
+         ParseSax.HandlerForGeneratedRequestWithResult<PaginatedSet<LoadBalancer>> {
+
+   private final LoadBalancerHandler loadBalancerHandler;
+
+   private StringBuilder currentText = new StringBuilder();
+   private Set<LoadBalancer> loadBalancers = Sets.newLinkedHashSet();
+   private boolean inLoadBalancers;
+   private String marker;
+
+   protected int memberDepth;
+
+   @Inject
+   public DescribeLoadBalancersResultHandler(LoadBalancerHandler loadBalancerHandler) {
+      this.loadBalancerHandler = loadBalancerHandler;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public PaginatedSet<LoadBalancer> getResult() {
+      return PaginatedSet.copyOfWithMarker(loadBalancers, marker);
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public void startElement(String url, String name, String qName, Attributes attributes) throws SAXException {
+      if (equalsOrSuffix(qName, "member")) {
+         memberDepth++;
+      } else if (equalsOrSuffix(qName, "LoadBalancerDescriptions")) {
+         inLoadBalancers = true;
+      }
+      if (inLoadBalancers) {
+         loadBalancerHandler.startElement(url, name, qName, attributes);
+      }
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public void endElement(String uri, String name, String qName) throws SAXException {
+      if (equalsOrSuffix(qName, "member")) {
+         endMember( uri,  name,  qName);
+         memberDepth--;
+      } else if (equalsOrSuffix(qName, "LoadBalancerDescriptions")) {
+         inLoadBalancers = false;
+      } else if (equalsOrSuffix(qName, "Marker")) {
+         marker = currentOrNull(currentText);
+      } else if (inLoadBalancers) {
+         loadBalancerHandler.endElement(uri, name, qName);
+      }
+
+      currentText = new StringBuilder();
+   }
+
+   protected void endMember(String uri, String name, String qName) throws SAXException {
+      if (inLoadBalancers) {
+         if (memberDepth == 1)
+            loadBalancers.add(loadBalancerHandler.getResult());
+         else
+            loadBalancerHandler.endElement(uri, name, qName);
+      }
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public void characters(char ch[], int start, int length) {
+      if (inLoadBalancers) {
+         loadBalancerHandler.characters(ch, start, length);
+      } else {
+         currentText.append(ch, start, length);
+      }
+   }
+
+}
diff --git a/labs/elb/src/main/java/org/jclouds/elb/xml/HealthCheckHandler.java b/labs/elb/src/main/java/org/jclouds/elb/xml/HealthCheckHandler.java
new file mode 100644
index 0000000..7bab62e
--- /dev/null
+++ b/labs/elb/src/main/java/org/jclouds/elb/xml/HealthCheckHandler.java
@@ -0,0 +1,79 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.elb.xml;
+
+import static org.jclouds.util.SaxUtils.currentOrNull;
+import static org.jclouds.util.SaxUtils.equalsOrSuffix;
+
+import org.jclouds.elb.domain.HealthCheck;
+import org.jclouds.http.functions.ParseSax;
+import org.xml.sax.SAXException;
+
+/**
+ * @see <a
+ *      href="http://docs.amazonwebservices.com/ElasticLoadBalancing/latest/APIReference/API_HealthCheck.html"
+ *      >xml</a>
+ * 
+ * @author Adrian Cole
+ */
+public class HealthCheckHandler extends ParseSax.HandlerForGeneratedRequestWithResult<HealthCheck> {
+
+   private StringBuilder currentText = new StringBuilder();
+   private HealthCheck.Builder<?> builder = HealthCheck.builder();
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public HealthCheck getResult() {
+      try {
+         return builder.build();
+      } finally {
+         builder = HealthCheck.builder();
+      }
+   }
+   
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public void endElement(String uri, String name, String qName) throws SAXException {
+      if (equalsOrSuffix(qName, "HealthyThreshold")) {
+         builder.healthyThreshold(Integer.parseInt(currentOrNull(currentText)));
+      } else if (equalsOrSuffix(qName, "Interval")) {
+         builder.interval(Integer.parseInt(currentOrNull(currentText)));
+      } else if (equalsOrSuffix(qName, "Target")) {
+         builder.target(currentOrNull(currentText));
+      } else if (equalsOrSuffix(qName, "Timeout")) {
+         builder.timeout(Integer.parseInt(currentOrNull(currentText)));
+      } else if (equalsOrSuffix(qName, "UnhealthyThreshold")) {
+         builder.unhealthyThreshold(Integer.parseInt(currentOrNull(currentText)));
+      }
+      currentText = new StringBuilder();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public void characters(char ch[], int start, int length) {
+      currentText.append(ch, start, length);
+   }
+
+}
diff --git a/labs/elb/src/main/java/org/jclouds/elb/xml/ListenerWithPoliciesHandler.java b/labs/elb/src/main/java/org/jclouds/elb/xml/ListenerWithPoliciesHandler.java
new file mode 100644
index 0000000..6877a8f
--- /dev/null
+++ b/labs/elb/src/main/java/org/jclouds/elb/xml/ListenerWithPoliciesHandler.java
@@ -0,0 +1,82 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.elb.xml;
+
+import static org.jclouds.util.SaxUtils.currentOrNull;
+import static org.jclouds.util.SaxUtils.equalsOrSuffix;
+
+import org.jclouds.elb.domain.ListenerWithPolicies;
+import org.jclouds.elb.domain.Protocol;
+import org.jclouds.http.functions.ParseSax;
+import org.xml.sax.SAXException;
+
+/**
+ * @see <a
+ *      href="http://docs.amazonwebservices.com/ElasticLoadBalancing/latest/APIReference/API_ListenerDescription.html"
+ *      >xml</a>
+ * 
+ * @author Adrian Cole
+ */
+public class ListenerWithPoliciesHandler extends ParseSax.HandlerForGeneratedRequestWithResult<ListenerWithPolicies> {
+
+   private StringBuilder currentText = new StringBuilder();
+   private ListenerWithPolicies.Builder<?> builder = ListenerWithPolicies.builder();
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public ListenerWithPolicies getResult() {
+      try {
+         return builder.build();
+      } finally {
+         builder = ListenerWithPolicies.builder();
+      }
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public void endElement(String uri, String name, String qName) throws SAXException {
+      if (equalsOrSuffix(qName, "PolicyName")) {
+         builder.policyName(currentOrNull(currentText));
+      } else if (equalsOrSuffix(qName, "InstancePort")) {
+         builder.instancePort(Integer.parseInt(currentOrNull(currentText)));
+      } else if (equalsOrSuffix(qName, "InstanceProtocol")) {
+         builder.instanceProtocol(Protocol.valueOf(currentOrNull(currentText)));
+      } else if (equalsOrSuffix(qName, "LoadBalancerPort")) {
+         builder.port(Integer.parseInt(currentOrNull(currentText)));
+      } else if (equalsOrSuffix(qName, "Protocol")) {
+         builder.protocol(Protocol.valueOf(currentOrNull(currentText)));
+      } else if (equalsOrSuffix(qName, "SSLCertificateId")) {
+         builder.SSLCertificateId(currentOrNull(currentText));
+      }
+      currentText = new StringBuilder();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public void characters(char ch[], int start, int length) {
+      currentText.append(ch, start, length);
+   }
+
+}
diff --git a/labs/elb/src/main/java/org/jclouds/elb/xml/LoadBalancerHandler.java b/labs/elb/src/main/java/org/jclouds/elb/xml/LoadBalancerHandler.java
new file mode 100644
index 0000000..5f8ac78
--- /dev/null
+++ b/labs/elb/src/main/java/org/jclouds/elb/xml/LoadBalancerHandler.java
@@ -0,0 +1,141 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.elb.xml;
+
+import static org.jclouds.util.SaxUtils.currentOrNull;
+import static org.jclouds.util.SaxUtils.equalsOrSuffix;
+
+import javax.inject.Inject;
+
+import org.jclouds.date.DateService;
+import org.jclouds.elb.domain.LoadBalancer;
+import org.jclouds.elb.domain.LoadBalancer.Scheme;
+import org.jclouds.http.functions.ParseSax;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+
+/**
+ * @see <a href="http://docs.amazonwebservices.com/ElasticLoadBalancing/latest/APIReference/API_GetLoadBalancer.html"
+ *      />
+ * 
+ * @author Adrian Cole
+ */
+public class LoadBalancerHandler extends ParseSax.HandlerForGeneratedRequestWithResult<LoadBalancer> {
+   protected final DateService dateService;
+   protected final HealthCheckHandler healthCheckHandler;
+   protected final ListenerWithPoliciesHandler listenerHandler;
+
+   @Inject
+   protected LoadBalancerHandler(DateService dateService, HealthCheckHandler healthCheckHandler, ListenerWithPoliciesHandler listenerHandler) {
+      this.dateService = dateService;
+      this.healthCheckHandler = healthCheckHandler;
+      this.listenerHandler = listenerHandler;
+   }
+
+   private StringBuilder currentText = new StringBuilder();
+   private LoadBalancer.Builder<?> builder = LoadBalancer.builder();
+
+   private boolean inHealthCheck;
+   private boolean inListeners;
+
+   protected int memberDepth;
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public LoadBalancer getResult() {
+      try {
+         return builder.build();
+      } finally {
+         builder = LoadBalancer.builder();
+      }
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public void startElement(String url, String name, String qName, Attributes attributes) throws SAXException {
+      if (equalsOrSuffix(qName, "member")) {
+         memberDepth++;
+      } else if (equalsOrSuffix(qName, "HealthCheck")) {
+         inHealthCheck = true;
+      } else if (equalsOrSuffix(qName, "ListenerDescriptions")) {
+         inListeners = true;
+      }
+      if (inListeners) {
+         listenerHandler.startElement(url, name, qName, attributes);
+      }
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public void endElement(String uri, String name, String qName) throws SAXException {
+      if (equalsOrSuffix(qName, "member")) {
+         endMember(uri, name, qName);
+         memberDepth--;
+      } else if (equalsOrSuffix(qName, "ListenerDescriptions")) {
+         inListeners = false;
+      } else if (equalsOrSuffix(qName, "HealthCheck")) {
+         builder.healthCheck(healthCheckHandler.getResult());
+         inHealthCheck = false;
+      } else if (equalsOrSuffix(qName, "LoadBalancerName")) {
+         builder.name(currentOrNull(currentText));
+      } else if (equalsOrSuffix(qName, "CreatedTime")) {
+         builder.createdTime(dateService.iso8601DateParse(currentOrNull(currentText)));
+      } else if (equalsOrSuffix(qName, "DNSName")) {
+         builder.dnsName(currentOrNull(currentText));
+      } else if (equalsOrSuffix(qName, "InstanceId")) {
+         builder.instanceId(currentOrNull(currentText));
+      } else if (equalsOrSuffix(qName, "Scheme")) {
+         builder.scheme(Scheme.fromValue(currentOrNull(currentText)));
+      } else if (equalsOrSuffix(qName, "VPCId")) {
+         builder.VPCId(currentOrNull(currentText));
+      } else if (inHealthCheck) {
+         healthCheckHandler.endElement(uri, name, qName);
+      } else if (inListeners) {
+         listenerHandler.endElement(uri, name, qName);
+      }
+      currentText = new StringBuilder();
+   }
+
+   protected void endMember(String uri, String name, String qName) throws SAXException {
+      if (inListeners) {
+         builder.listener(listenerHandler.getResult());
+      }
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public void characters(char ch[], int start, int length) {
+      if (inListeners) {
+         listenerHandler.characters(ch, start, length);
+      } else if (inHealthCheck) {
+         healthCheckHandler.characters(ch, start, length);
+      } else {
+         currentText.append(ch, start, length);
+      }
+   }
+
+}
diff --git a/labs/elb/src/main/java/org/jclouds/elb/xml/PolicyHandler.java b/labs/elb/src/main/java/org/jclouds/elb/xml/PolicyHandler.java
new file mode 100644
index 0000000..d90999c
--- /dev/null
+++ b/labs/elb/src/main/java/org/jclouds/elb/xml/PolicyHandler.java
@@ -0,0 +1,87 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.elb.xml;
+
+import static org.jclouds.util.SaxUtils.currentOrNull;
+import static org.jclouds.util.SaxUtils.equalsOrSuffix;
+
+import org.jclouds.elb.domain.Policy;
+import org.jclouds.http.functions.ParseSax;
+import org.xml.sax.SAXException;
+
+import com.google.common.primitives.Ints;
+
+/**
+ * @see <a
+ *      href="http://docs.amazonwebservices.com/ElasticLoadBalancing/latest/APIReference/API_PolicyDescription.html"
+ *      >xml</a>
+ * 
+ * @author Adrian Cole
+ */
+public class PolicyHandler extends ParseSax.HandlerForGeneratedRequestWithResult<Policy> {
+
+   private StringBuilder currentText = new StringBuilder();
+   private Policy.Builder<?> builder = Policy.builder();
+   private String key;
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Policy getResult() {
+      try {
+         return builder.build();
+      } finally {
+         builder = Policy.builder();
+      }
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public void endElement(String uri, String name, String qName) throws SAXException {
+      if (equalsOrSuffix(qName, "PolicyName")) {
+         builder.name(currentOrNull(currentText));
+      } else if (equalsOrSuffix(qName, "PolicyTypeName")) {
+         builder.typeName(currentOrNull(currentText));
+      } else if (equalsOrSuffix(qName, "AttributeName")) {
+         key = currentOrNull(currentText);
+      } else if (equalsOrSuffix(qName, "AttributeValue")) {
+         String value = currentOrNull(currentText);
+         if ("true".equalsIgnoreCase(value) || "false".equalsIgnoreCase(value))
+            builder.attribute(key, Boolean.valueOf(value));
+         else if (Ints.tryParse(value) != null)
+            builder.attribute(key, Long.valueOf(value));
+         else
+            builder.attribute(key, value);
+         key = null;
+      }
+      currentText = new StringBuilder();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public void characters(char ch[], int start, int length) {
+      currentText.append(ch, start, length);
+   }
+
+}
diff --git a/labs/elb/src/main/java/org/jclouds/elb/xml/PolicyTypeHandler.java b/labs/elb/src/main/java/org/jclouds/elb/xml/PolicyTypeHandler.java
new file mode 100644
index 0000000..69d3e49
--- /dev/null
+++ b/labs/elb/src/main/java/org/jclouds/elb/xml/PolicyTypeHandler.java
@@ -0,0 +1,113 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.elb.xml;
+
+import static org.jclouds.util.SaxUtils.currentOrNull;
+import static org.jclouds.util.SaxUtils.equalsOrSuffix;
+
+import javax.inject.Inject;
+
+import org.jclouds.elb.domain.PolicyType;
+import org.jclouds.http.functions.ParseSax;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+
+/**
+ * @see <a
+ *      href="http://docs.amazonwebservices.com/ElasticLoadBalancing/latest/APIReference/API_PolicyTypeDescription.html"
+ *      >doc</a>
+ * 
+ * @author Adrian Cole
+ */
+public class PolicyTypeHandler extends ParseSax.HandlerForGeneratedRequestWithResult<PolicyType> {
+   protected final AttributeMetadataHandler attributeTypeHandler;
+
+   @Inject
+   protected PolicyTypeHandler(AttributeMetadataHandler attributeTypeHandler) {
+      this.attributeTypeHandler = attributeTypeHandler;
+   }
+
+   private StringBuilder currentText = new StringBuilder();
+   private PolicyType.Builder<?> builder = PolicyType.builder();
+
+   private boolean inAttributeTypes;
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public PolicyType getResult() {
+      try {
+         return builder.build();
+      } finally {
+         builder = PolicyType.builder();
+      }
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public void startElement(String url, String name, String qName, Attributes attributes) throws SAXException {
+      if (equalsOrSuffix(qName, "PolicyAttributeTypeDescriptions")) {
+         inAttributeTypes = true;
+      }
+      if (inAttributeTypes) {
+         attributeTypeHandler.startElement(url, name, qName, attributes);
+      }
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public void endElement(String uri, String name, String qName) throws SAXException {
+      if (equalsOrSuffix(qName, "member")) {
+         endMember(uri, name, qName);
+      } else if (equalsOrSuffix(qName, "PolicyAttributeTypeDescriptions")) {
+         inAttributeTypes = false;
+      } else if (equalsOrSuffix(qName, "PolicyTypeName")) {
+         builder.name(currentOrNull(currentText));
+      } else if (equalsOrSuffix(qName, "Description")) {
+         builder.description(currentOrNull(currentText));
+      } else if (inAttributeTypes) {
+         attributeTypeHandler.endElement(uri, name, qName);
+      }
+      currentText = new StringBuilder();
+   }
+
+   protected void endMember(String uri, String name, String qName) throws SAXException {
+      if (inAttributeTypes) {
+         builder.attributeMetadata(attributeTypeHandler.getResult());
+      }
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public void characters(char ch[], int start, int length) {
+      if (inAttributeTypes) {
+         attributeTypeHandler.characters(ch, start, length);
+      } else {
+         currentText.append(ch, start, length);
+      }
+   }
+
+}
diff --git a/labs/elb/src/test/java/org/jclouds/elb/ELBClientLiveTest.java b/labs/elb/src/test/java/org/jclouds/elb/ELBClientLiveTest.java
index effd8e5..a0e187b 100644
--- a/labs/elb/src/test/java/org/jclouds/elb/ELBClientLiveTest.java
+++ b/labs/elb/src/test/java/org/jclouds/elb/ELBClientLiveTest.java
@@ -24,7 +24,7 @@
 import java.util.Set;
 
 import org.jclouds.apis.BaseContextLiveTest;
-import org.jclouds.elb.domain.LoadBalancer;
+import org.jclouds.elb.domain.CrappyLoadBalancer;
 import org.jclouds.rest.RestContext;
 import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeClass;
@@ -73,7 +73,7 @@
    }
 
    protected void describeLoadBalancerInRegion(String region) {
-      Set<? extends LoadBalancer> allResults = client.describeLoadBalancersInRegion(region);
+      Set<? extends CrappyLoadBalancer> allResults = client.describeLoadBalancersInRegion(region);
       assertNotNull(allResults);
       assert (allResults.size() >= 1) : region;
    }
diff --git a/labs/elb/src/test/java/org/jclouds/elb/ELBTest.java b/labs/elb/src/test/java/org/jclouds/elb/ELBTest.java
new file mode 100644
index 0000000..1a52fb5
--- /dev/null
+++ b/labs/elb/src/test/java/org/jclouds/elb/ELBTest.java
@@ -0,0 +1,62 @@
+package org.jclouds.elb;
+
+import static org.easymock.EasyMock.anyObject;
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+
+import org.easymock.EasyMock;
+import org.jclouds.collect.PaginatedSet;
+import org.jclouds.elb.domain.LoadBalancer;
+import org.jclouds.elb.features.LoadBalancerClient;
+import org.jclouds.elb.options.ListLoadBalancersOptions;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+
+/**
+ * Tests behavior of {@code ELB}.
+ *
+ * @author Adrian Cole
+ */
+@Test(testName = "ELBTest")
+public class ELBTest {
+
+
+   @Test
+   public void testSinglePageResult() throws Exception {
+      LoadBalancerClient loadBalancerClient = createMock(LoadBalancerClient.class);
+      ListLoadBalancersOptions options = new ListLoadBalancersOptions();
+      PaginatedSet<LoadBalancer> response = PaginatedSet.copyOf(ImmutableSet.of(createMock(LoadBalancer.class)));
+      
+      expect(loadBalancerClient.list(options))
+            .andReturn(response)
+            .once();
+
+      EasyMock.replay(loadBalancerClient);
+
+      Assert.assertEquals(1, Iterables.size(ELB.listLoadBalancers(loadBalancerClient, options)));
+   }
+
+
+   @Test
+   public void testMultiPageResult() throws Exception {
+      LoadBalancerClient loadBalancerClient = createMock(LoadBalancerClient.class);
+      ListLoadBalancersOptions options = new ListLoadBalancersOptions();
+      PaginatedSet<LoadBalancer> response1 = PaginatedSet.copyOfWithMarker(ImmutableSet.of(createMock(LoadBalancer.class)), "NEXTTOKEN");
+      PaginatedSet<LoadBalancer> response2 = PaginatedSet.copyOf(ImmutableSet.of(createMock(LoadBalancer.class)));
+
+      expect(loadBalancerClient.list(anyObject(ListLoadBalancersOptions.class)))
+            .andReturn(response1)
+            .once();
+      expect(loadBalancerClient.list(anyObject(ListLoadBalancersOptions.class)))
+            .andReturn(response2)
+            .once();
+
+      EasyMock.replay(loadBalancerClient);
+
+      Assert.assertEquals(2, Iterables.size(ELB.listLoadBalancers(loadBalancerClient, options)));
+   }
+
+}
diff --git a/labs/elb/src/test/java/org/jclouds/elb/features/LoadBalancerClientExpectTest.java b/labs/elb/src/test/java/org/jclouds/elb/features/LoadBalancerClientExpectTest.java
new file mode 100644
index 0000000..8638a51
--- /dev/null
+++ b/labs/elb/src/test/java/org/jclouds/elb/features/LoadBalancerClientExpectTest.java
@@ -0,0 +1,160 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unles 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 expres or implied.  See the License for the
+ * specific language governing permisions and limitations
+ * under the License.
+ */
+package org.jclouds.elb.features;
+
+import static org.jclouds.elb.options.ListLoadBalancersOptions.Builder.marker;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNull;
+
+import java.net.URI;
+import java.util.TimeZone;
+
+import org.jclouds.elb.ELBClient;
+import org.jclouds.elb.internal.BaseELBClientExpectTest;
+import org.jclouds.elb.parse.DescribeLoadBalancersResponseTest;
+import org.jclouds.elb.parse.GetLoadBalancerResponseTest;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.rest.ResourceNotFoundException;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableMultimap;
+
+/**
+ * @author Adrian Cole
+ */
+@Test(groups = "unit", testName = "LoadBalancerClientExpectTest")
+public class LoadBalancerClientExpectTest extends BaseELBClientExpectTest {
+
+   public LoadBalancerClientExpectTest() {
+      TimeZone.setDefault(TimeZone.getTimeZone("America/Los_Angeles"));
+   }
+
+   HttpRequest get = HttpRequest.builder()
+                                       .method("POST")
+                                       .endpoint(URI.create("https://elasticloadbalancing.us-east-1.amazonaws.com/"))
+                                       .headers(ImmutableMultimap.<String, String> builder()
+                                                .put("Host", "elasticloadbalancing.us-east-1.amazonaws.com")
+                                                .build())
+                                       .payload(
+                                          payloadFromStringWithContentType(
+                                                "Action=DescribeLoadBalancers" +
+                                                      "&LoadBalancerNames.member.1=name" +
+                                                      "&Signature=EYzZgYDMGi9uFZU%2BVh%2FmmsJ9KmHxm5vEAF%2BhGF12BP4%3D" +
+                                                      "&SignatureMethod=HmacSHA256" +
+                                                      "&SignatureVersion=2" +
+                                                      "&Timestamp=2009-11-08T15%3A54%3A08.897Z" +
+                                                      "&Version=2012-06-01" +
+                                                      "&AWSAccessKeyId=identity",
+                                                "application/x-www-form-urlencoded"))
+                                       .build();
+   
+   
+   public void testGetWhenResponseIs2xx() throws Exception {
+
+      HttpResponse getResponse = HttpResponse.builder().statusCode(200)
+            .payload(payloadFromResourceWithContentType("/describe_loadbalancers.xml", "text/xml")).build();
+
+      ELBClient clientWhenExist = requestSendsResponse(
+            get, getResponse);
+
+      assertEquals(clientWhenExist.getLoadBalancerClientForRegion(null).get("name").toString(), new GetLoadBalancerResponseTest().expected().toString());
+   }
+
+   public void testGetWhenResponseIs404() throws Exception {
+
+      HttpResponse getResponse = HttpResponse.builder().statusCode(404).build();
+
+      ELBClient clientWhenDontExist = requestSendsResponse(
+            get, getResponse);
+
+      assertNull(clientWhenDontExist.getLoadBalancerClientForRegion(null).get("name"));
+   }
+
+   HttpRequest list = HttpRequest.builder()
+                                       .method("POST")
+                                       .endpoint(URI.create("https://elasticloadbalancing.us-east-1.amazonaws.com/"))
+                                       .headers(ImmutableMultimap.<String, String> builder()
+                                                .put("Host", "elasticloadbalancing.us-east-1.amazonaws.com")
+                                                .build())
+                                       .payload(
+                                          payloadFromStringWithContentType(
+                                                   "Action=DescribeLoadBalancers" +
+                                                   "&Signature=3pErfVJXXe4EndOr3nPMu2%2F5eO8aCvwcOaI%2BL64VMqg%3D" +
+                                                   "&SignatureMethod=HmacSHA256" +
+                                                   "&SignatureVersion=2" +
+                                                   "&Timestamp=2009-11-08T15%3A54%3A08.897Z" +
+                                                   "&Version=2012-06-01" +
+                                                   "&AWSAccessKeyId=identity",
+                                                "application/x-www-form-urlencoded"))
+                                       .build();
+   
+   public void testListWhenResponseIs2xx() throws Exception {
+
+      HttpResponse listResponse = HttpResponse.builder().statusCode(200)
+            .payload(payloadFromResourceWithContentType("/describe_loadbalancers.xml", "text/xml")).build();
+
+      ELBClient clientWhenExist = requestSendsResponse(
+            list, listResponse);
+
+      assertEquals(clientWhenExist.getLoadBalancerClientForRegion(null).list().toString(), new DescribeLoadBalancersResponseTest().expected().toString());
+   }
+
+   // TODO: this should really be an empty set
+   @Test(expectedExceptions = ResourceNotFoundException.class)
+   public void testListWhenResponseIs404() throws Exception {
+
+      HttpResponse listResponse = HttpResponse.builder().statusCode(404).build();
+
+      ELBClient clientWhenDontExist = requestSendsResponse(
+            list, listResponse);
+
+      clientWhenDontExist.getLoadBalancerClientForRegion(null).list();
+   }
+   
+   public void testListWithOptionsWhenResponseIs2xx() throws Exception {
+      HttpRequest listWithOptions =
+            HttpRequest.builder()
+                       .method("POST")
+                       .endpoint(URI.create("https://elasticloadbalancing.us-east-1.amazonaws.com/"))
+                       .headers(ImmutableMultimap.<String, String>builder()
+                                                 .put("Host", "elasticloadbalancing.us-east-1.amazonaws.com")
+                                                 .build())
+                       .payload(payloadFromStringWithContentType(
+                                                  "Action=DescribeLoadBalancers" +
+                                                  "&Marker=MARKER" +
+                                                  "&Signature=%2FJttkIXuYljhZLJOPYyn%2BYIkDhD9skmePH3LYEnqmes%3D" +
+                                                  "&SignatureMethod=HmacSHA256" +
+                                                  "&SignatureVersion=2" +
+                                                  "&Timestamp=2009-11-08T15%3A54%3A08.897Z" +
+                                                  "&Version=2012-06-01" +
+                                                  "&AWSAccessKeyId=identity",
+                                            "application/x-www-form-urlencoded"))
+                       .build();
+      
+      HttpResponse listWithOptionsResponse = HttpResponse.builder().statusCode(200)
+               .payload(payloadFromResourceWithContentType("/describe_loadbalancers.xml", "text/xml")).build();
+
+      ELBClient clientWhenWithOptionsExist = requestSendsResponse(listWithOptions,
+               listWithOptionsResponse);
+
+      assertEquals(clientWhenWithOptionsExist.getLoadBalancerClientForRegion(null).list(marker("MARKER")).toString(),
+               new DescribeLoadBalancersResponseTest().expected().toString());
+   }
+}
diff --git a/labs/elb/src/test/java/org/jclouds/elb/features/LoadBalancerClientLiveTest.java b/labs/elb/src/test/java/org/jclouds/elb/features/LoadBalancerClientLiveTest.java
new file mode 100644
index 0000000..836cd58
--- /dev/null
+++ b/labs/elb/src/test/java/org/jclouds/elb/features/LoadBalancerClientLiveTest.java
@@ -0,0 +1,83 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.elb.features;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import org.jclouds.collect.PaginatedSet;
+import org.jclouds.elb.domain.ListenerWithPolicies;
+import org.jclouds.elb.domain.LoadBalancer;
+import org.jclouds.elb.internal.BaseELBClientLiveTest;
+import org.jclouds.elb.options.ListLoadBalancersOptions;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+/**
+ * @author Adrian Cole
+ */
+@Test(groups = "live", testName = "LoadBalancerClientLiveTest")
+public class LoadBalancerClientLiveTest extends BaseELBClientLiveTest {
+
+   private void checkLoadBalancer(LoadBalancer loadBalancer) {
+      checkNotNull(loadBalancer.getName(), "While Name can be null for a LoadBalancer, its Optional wrapper cannot.");
+      checkNotNull(loadBalancer.getCreatedTime(), "CreatedTime cannot be null for a LoadBalancer.");
+      checkNotNull(loadBalancer.getDnsName(), "DnsName cannot be null for a LoadBalancer.");
+      checkNotNull(loadBalancer.getHealthCheck(), "HealthCheck cannot be null for a LoadBalancer.");
+      checkNotNull(loadBalancer.getScheme(),
+               "While Scheme can be null for a LoadBalancer, its Optional wrapper cannot.");
+      checkNotNull(loadBalancer.getVPCId(), "While VPCId can be null for a LoadBalancer, its Optional wrapper cannot.");
+   }
+
+   private void checkListener(ListenerWithPolicies listener) {
+      checkNotNull(listener.getPolicyNames(), "While PolicyNames can be empty, it cannot be null.");
+      assert listener.getInstancePort() > 0 : "InstancePort must be positive";
+      checkNotNull(listener.getInstanceProtocol(), "InstanceProtocol cannot be null");
+      assert listener.getPort() > 0 : "Port must be positive";
+      checkNotNull(listener.getProtocol(), "Protocol cannot be null");
+      checkNotNull(listener.getSSLCertificateId(),
+               "While SSLCertificateId can be null for a ListenerWithPolicies, its Optional wrapper cannot.");
+   }
+
+   @Test
+   protected void testDescribeLoadBalancers() {
+      PaginatedSet<LoadBalancer> response = client().list();
+
+      for (LoadBalancer loadBalancer : response) {
+         checkLoadBalancer(loadBalancer);
+         for (ListenerWithPolicies listener : loadBalancer.getListeners()) {
+            checkListener(listener);
+         }
+      }
+
+      if (response.size() > 0) {
+         LoadBalancer loadBalancer = response.iterator().next();
+         Assert.assertEquals(client().get(loadBalancer.getName()), loadBalancer);
+      }
+
+      // Test with a Marker, even if it's null
+      response = client().list(ListLoadBalancersOptions.Builder.marker(response.getNextMarker()));
+      for (LoadBalancer loadBalancer : response) {
+         checkLoadBalancer(loadBalancer);
+      }
+   }
+
+   protected LoadBalancerClient client() {
+      return context.getApi().getLoadBalancerClientForRegion(null);
+   }
+}
diff --git a/labs/elb/src/test/java/org/jclouds/elb/features/PolicyClientExpectTest.java b/labs/elb/src/test/java/org/jclouds/elb/features/PolicyClientExpectTest.java
new file mode 100644
index 0000000..92b444b
--- /dev/null
+++ b/labs/elb/src/test/java/org/jclouds/elb/features/PolicyClientExpectTest.java
@@ -0,0 +1,271 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unles 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 expres or implied.  See the License for the
+ * specific language governing permisions and limitations
+ * under the License.
+ */
+package org.jclouds.elb.features;
+
+import static org.jclouds.elb.options.ListPoliciesOptions.Builder.loadBalancerName;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNull;
+
+import java.net.URI;
+import java.util.TimeZone;
+
+import org.jclouds.elb.ELBClient;
+import org.jclouds.elb.internal.BaseELBClientExpectTest;
+import org.jclouds.elb.parse.DescribeLoadBalancerPoliciesResponseTest;
+import org.jclouds.elb.parse.DescribeLoadBalancerPolicyTypesResponseTest;
+import org.jclouds.elb.parse.GetPolicyResponseTest;
+import org.jclouds.elb.parse.GetPolicyTypeResponseTest;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.http.HttpResponse;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * @author Adrian Cole
+ */
+@Test(groups = "unit", testName = "PolicyClientExpectTest")
+public class PolicyClientExpectTest extends BaseELBClientExpectTest {
+
+   public PolicyClientExpectTest() {
+      TimeZone.setDefault(TimeZone.getTimeZone("America/Los_Angeles"));
+   }
+
+   HttpRequest get = HttpRequest.builder()
+                                .method("POST")
+                                .endpoint(URI.create("https://elasticloadbalancing.us-east-1.amazonaws.com/"))
+                                .headers(ImmutableMultimap.<String, String> builder()
+                                          .put("Host", "elasticloadbalancing.us-east-1.amazonaws.com")
+                                          .build())
+                                .payload(
+                                    payloadFromStringWithContentType(
+                                          "Action=DescribeLoadBalancerPolicies" +
+                                                "&PolicyNames.member.1=name" +
+                                                "&Signature=kroGA7XRZYqiw4zgAXkWRdF9ff3RcnZKgvfcPG5f%2Bjs%3D" +
+                                                "&SignatureMethod=HmacSHA256" +
+                                                "&SignatureVersion=2" +
+                                                "&Timestamp=2009-11-08T15%3A54%3A08.897Z" +
+                                                "&Version=2012-06-01" +
+                                                "&AWSAccessKeyId=identity",
+                                          "application/x-www-form-urlencoded"))
+                                .build();
+   
+   
+   public void testGetWhenResponseIs2xx() throws Exception {
+
+      HttpResponse getResponse = HttpResponse.builder().statusCode(200)
+            .payload(payloadFromResourceWithContentType("/describe_policy.xml", "text/xml")).build();
+
+      ELBClient clientWhenExist = requestSendsResponse(
+            get, getResponse);
+
+      assertEquals(clientWhenExist.getPolicyClientForRegion(null).get("name").toString(), new GetPolicyResponseTest().expected().toString());
+   }
+
+   public void testGetWhenResponseIs404() throws Exception {
+
+      HttpResponse getResponse = HttpResponse.builder().statusCode(404).build();
+
+      ELBClient clientWhenDontExist = requestSendsResponse(
+            get, getResponse);
+
+      assertNull(clientWhenDontExist.getPolicyClientForRegion(null).get("name"));
+   }
+
+   HttpRequest list = HttpRequest.builder()
+                                 .method("POST")
+                                 .endpoint(URI.create("https://elasticloadbalancing.us-east-1.amazonaws.com/"))
+                                 .headers(ImmutableMultimap.<String, String> builder()
+                                          .put("Host", "elasticloadbalancing.us-east-1.amazonaws.com")
+                                          .build())
+                                 .payload(
+                                    payloadFromStringWithContentType(
+                                             "Action=DescribeLoadBalancerPolicies" +
+                                             "&Signature=0LPrgeysYoQe6PyK2nh3mCgo0lxPNiERxm46W%2FN5GpU%3D" +
+                                             "&SignatureMethod=HmacSHA256" +
+                                             "&SignatureVersion=2" +
+                                             "&Timestamp=2009-11-08T15%3A54%3A08.897Z" +
+                                             "&Version=2012-06-01" +
+                                             "&AWSAccessKeyId=identity",
+                                          "application/x-www-form-urlencoded"))
+                                 .build();
+
+   public void testListWhenResponseIs2xx() throws Exception {
+
+      HttpResponse listResponse = HttpResponse.builder().statusCode(200)
+            .payload(payloadFromResourceWithContentType("/describe_policies.xml", "text/xml")).build();
+
+      ELBClient clientWhenExist = requestSendsResponse(
+            list, listResponse);
+
+      assertEquals(clientWhenExist.getPolicyClientForRegion(null).list().toString(), new DescribeLoadBalancerPoliciesResponseTest().expected().toString());
+   }
+
+   public void testListWhenResponseIs404() throws Exception {
+
+      HttpResponse listResponse = HttpResponse.builder().statusCode(404).build();
+
+      ELBClient clientWhenDontExist = requestSendsResponse(
+            list, listResponse);
+
+      clientWhenDontExist.getPolicyClientForRegion(null).list();
+   }
+   
+   public void testListWithOptionsWhenResponseIs2xx() throws Exception {
+      HttpRequest listWithOptions =
+            HttpRequest.builder()
+                       .method("POST")
+                       .endpoint(URI.create("https://elasticloadbalancing.us-east-1.amazonaws.com/"))
+                       .headers(ImmutableMultimap.<String, String>builder()
+                                                 .put("Host", "elasticloadbalancing.us-east-1.amazonaws.com")
+                                                 .build())
+                       .payload(payloadFromStringWithContentType(
+                                                  "Action=DescribeLoadBalancerPolicies" +
+                                                  "&LoadBalancerName=moo" +
+                                                  "&Signature=c8PG1b5wI5YMU0motVEo5Mz7d5w8gy8u51kfCR6SnRI%3D" +
+                                                  "&SignatureMethod=HmacSHA256" +
+                                                  "&SignatureVersion=2" +
+                                                  "&Timestamp=2009-11-08T15%3A54%3A08.897Z" +
+                                                  "&Version=2012-06-01" +
+                                                  "&AWSAccessKeyId=identity",
+                                            "application/x-www-form-urlencoded"))
+                       .build();
+      
+      HttpResponse listWithOptionsResponse = HttpResponse.builder().statusCode(200)
+               .payload(payloadFromResourceWithContentType("/describe_policies.xml", "text/xml")).build();
+
+      ELBClient clientWhenWithOptionsExist = requestSendsResponse(listWithOptions,
+               listWithOptionsResponse);
+
+      assertEquals(clientWhenWithOptionsExist.getPolicyClientForRegion(null).list(loadBalancerName("moo")).toString(),
+               new DescribeLoadBalancerPoliciesResponseTest().expected().toString());
+   }
+   
+
+   HttpRequest getType = HttpRequest.builder()
+                                    .method("POST")
+                                    .endpoint(URI.create("https://elasticloadbalancing.us-east-1.amazonaws.com/"))
+                                    .headers(ImmutableMultimap.<String, String> builder()
+                                             .put("Host", "elasticloadbalancing.us-east-1.amazonaws.com")
+                                             .build())
+                                    .payload(
+                                       payloadFromStringWithContentType(
+                                             "Action=DescribeLoadBalancerPolicyTypes" +
+                                                   "&PolicyTypeNames.member.1=name" +
+                                                   "&Signature=WC5tQK0TacaxSRrCEKqbpIPFrrrgsBV4I1%2B9W2Lx58M%3D" +
+                                                   "&SignatureMethod=HmacSHA256" +
+                                                   "&SignatureVersion=2" +
+                                                   "&Timestamp=2009-11-08T15%3A54%3A08.897Z" +
+                                                   "&Version=2012-06-01" +
+                                                   "&AWSAccessKeyId=identity",
+                                             "application/x-www-form-urlencoded"))
+                                    .build();
+   
+   
+   public void testGetTypeWhenResponseIs2xx() throws Exception {
+
+      HttpResponse getResponse = HttpResponse.builder().statusCode(200)
+            .payload(payloadFromResourceWithContentType("/describe_policy_type.xml", "text/xml")).build();
+
+      ELBClient clientWhenExist = requestSendsResponse(
+            getType, getResponse);
+
+      assertEquals(clientWhenExist.getPolicyClientForRegion(null).getType("name").toString(), new GetPolicyTypeResponseTest().expected().toString());
+   }
+
+   public void testGetTypeWhenResponseIs404() throws Exception {
+
+      HttpResponse getResponse = HttpResponse.builder().statusCode(404).build();
+
+      ELBClient clientWhenDontExist = requestSendsResponse(
+            getType, getResponse);
+
+      assertNull(clientWhenDontExist.getPolicyClientForRegion(null).getType("name"));
+   }
+
+   HttpRequest listTypes = HttpRequest.builder()
+                                      .method("POST")
+                                      .endpoint(URI.create("https://elasticloadbalancing.us-east-1.amazonaws.com/"))
+                                      .headers(ImmutableMultimap.<String, String> builder()
+                                                .put("Host", "elasticloadbalancing.us-east-1.amazonaws.com")
+                                                .build())
+                                      .payload(
+                                          payloadFromStringWithContentType(
+                                                   "Action=DescribeLoadBalancerPolicyTypes" +
+                                                   "&Signature=%2F1mMjugJD8Zvb%2BK%2FQgOZMYVenlveCKtvGBiHaZVc%2B9w%3D" +
+                                                   "&SignatureMethod=HmacSHA256" +
+                                                   "&SignatureVersion=2" +
+                                                   "&Timestamp=2009-11-08T15%3A54%3A08.897Z" +
+                                                   "&Version=2012-06-01" +
+                                                   "&AWSAccessKeyId=identity",
+                                                "application/x-www-form-urlencoded"))
+                                      .build();
+
+   public void testListTypeWhenResponseIs2xx() throws Exception {
+
+      HttpResponse listResponse = HttpResponse.builder().statusCode(200)
+            .payload(payloadFromResourceWithContentType("/describe_policy_types.xml", "text/xml")).build();
+
+      ELBClient clientWhenExist = requestSendsResponse(
+            listTypes, listResponse);
+
+      assertEquals(clientWhenExist.getPolicyClientForRegion(null).listTypes().toString(), new DescribeLoadBalancerPolicyTypesResponseTest().expected().toString());
+   }
+
+   public void testListTypesWhenResponseIs404() throws Exception {
+
+      HttpResponse listResponse = HttpResponse.builder().statusCode(404).build();
+
+      ELBClient clientWhenDontExist = requestSendsResponse(
+            listTypes, listResponse);
+
+      clientWhenDontExist.getPolicyClientForRegion(null).listTypes();
+   }
+   
+   public void testListTypesByNamesWhenResponseIs2xx() throws Exception {
+      HttpRequest listWithOptions =
+            HttpRequest.builder()
+                       .method("POST")
+                       .endpoint(URI.create("https://elasticloadbalancing.us-east-1.amazonaws.com/"))
+                       .headers(ImmutableMultimap.<String, String>builder()
+                                                 .put("Host", "elasticloadbalancing.us-east-1.amazonaws.com")
+                                                 .build())
+                       .payload(payloadFromStringWithContentType(
+                                                  "Action=DescribeLoadBalancerPolicyTypes" +
+                                                  "&PolicyTypeNames.member.1=moo" +
+                                                  "&Signature=cX8twwn2E6%2B7V3CGZ4ac69NhyolJLsV1nzpQl3wQXW8%3D" +
+                                                  "&SignatureMethod=HmacSHA256" +
+                                                  "&SignatureVersion=2" +
+                                                  "&Timestamp=2009-11-08T15%3A54%3A08.897Z" +
+                                                  "&Version=2012-06-01" +
+                                                  "&AWSAccessKeyId=identity",
+                                            "application/x-www-form-urlencoded"))
+                       .build();
+      
+      HttpResponse listWithOptionsResponse = HttpResponse.builder().statusCode(200)
+               .payload(payloadFromResourceWithContentType("/describe_policy_types.xml", "text/xml")).build();
+
+      ELBClient clientWhenWithOptionsExist = requestSendsResponse(listWithOptions,
+               listWithOptionsResponse);
+
+      assertEquals(clientWhenWithOptionsExist.getPolicyClientForRegion(null).listTypes(ImmutableSet.of("moo")).toString(),
+               new DescribeLoadBalancerPolicyTypesResponseTest().expected().toString());
+   }
+}
diff --git a/labs/elb/src/test/java/org/jclouds/elb/features/PolicyClientLiveTest.java b/labs/elb/src/test/java/org/jclouds/elb/features/PolicyClientLiveTest.java
new file mode 100644
index 0000000..a453df0
--- /dev/null
+++ b/labs/elb/src/test/java/org/jclouds/elb/features/PolicyClientLiveTest.java
@@ -0,0 +1,110 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.elb.features;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.Set;
+
+import javax.annotation.Nullable;
+
+import org.jclouds.elb.domain.AttributeMetadata;
+import org.jclouds.elb.domain.Policy;
+import org.jclouds.elb.domain.PolicyType;
+import org.jclouds.elb.internal.BaseELBClientLiveTest;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Function;
+import com.google.common.collect.Iterables;
+
+/**
+ * @author Adrian Cole
+ */
+@Test(groups = "live", testName = "PolicyClientLiveTest")
+public class PolicyClientLiveTest extends BaseELBClientLiveTest {
+
+   private void checkPolicy(Policy policy) {
+      checkNotNull(policy.getName(), "Name cannot be null for a Policy.");
+      checkNotNull(policy.getTypeName(), "TypeName cannot be null for a Policy.");
+      checkNotNull(policy.getAttributes(), "While it is ok to have no attributes, the map cannot be null.");
+   }
+
+   @Test
+   protected void testDescribePolicies() {
+      Set<Policy> response = client().list();
+
+      for (Policy policy : response) {
+         checkPolicy(policy);
+      }
+
+      if (response.size() > 0) {
+         Policy policy = response.iterator().next();
+         Assert.assertEquals(client().get(policy.getName()), policy);
+      }
+
+   }
+
+   private void checkPolicyType(PolicyType policyType) {
+      checkNotNull(policyType.getName(), "Name cannot be null for a PolicyType.");
+      checkNotNull(policyType.getDescription(), "Description cannot be null for a PolicyType.");
+      checkNotNull(policyType.getAttributeMetadata(), "While it is ok to have no attributes, the set cannot be null.");
+      for (AttributeMetadata<?> attributeMetadata: policyType.getAttributeMetadata()) {
+         checkAttributeMetadata(attributeMetadata);
+      }
+   }
+
+   private void checkAttributeMetadata(AttributeMetadata<?> attributeMetadata) {
+      checkNotNull(attributeMetadata.getName(), "Name cannot be null for a AttributeMetadata.");
+      checkNotNull(attributeMetadata.getType(), "Type cannot be null for a AttributeMetadata.");
+      checkNotNull(attributeMetadata.getCardinality(), "Cardinality cannot be null for a AttributeMetadata.");
+      checkNotNull(attributeMetadata.getDefaultValue(), "While DefaultValue can be null, its optional wrapper cannot");
+      checkNotNull(attributeMetadata.getDescription(),  "While Description can be null, its optional wrapper cannot");
+   }
+
+   @Test
+   protected void testDescribePolicyTypes() {
+      Set<PolicyType> response = client().listTypes();
+
+      for (PolicyType policyType : response) {
+         checkPolicyType(policyType);
+      }
+
+      if (response.size() > 0) {
+         PolicyType policyType = response.iterator().next();
+         Assert.assertEquals(client().getType(policyType.getName()), policyType);
+      }
+
+      if (response.size() > 0) {
+         Iterable<String> names = Iterables.transform(response, new Function<PolicyType, String>() {
+
+            @Override
+            public String apply(@Nullable PolicyType input) {
+               return input.getName();
+            }
+
+         });
+         Assert.assertEquals(client().listTypes(names), response);
+      }
+   }
+
+   protected PolicyClient client() {
+      return context.getApi().getPolicyClientForRegion(null);
+   }
+}
diff --git a/labs/elb/src/test/java/org/jclouds/elb/internal/BaseELBAsyncClientExpectTest.java b/labs/elb/src/test/java/org/jclouds/elb/internal/BaseELBAsyncClientExpectTest.java
new file mode 100644
index 0000000..df56165
--- /dev/null
+++ b/labs/elb/src/test/java/org/jclouds/elb/internal/BaseELBAsyncClientExpectTest.java
@@ -0,0 +1,38 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.elb.internal;
+
+import java.util.Properties;
+
+import org.jclouds.elb.ELBAsyncClient;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.http.HttpResponse;
+
+import com.google.common.base.Function;
+import com.google.inject.Module;
+
+/**
+ * 
+ * @author Adrian Cole
+ */
+public class BaseELBAsyncClientExpectTest extends BaseELBExpectTest<ELBAsyncClient> {
+   public ELBAsyncClient createClient(Function<HttpRequest, HttpResponse> fn, Module module, Properties props) {
+      return createInjector(fn, module, props).getInstance(ELBAsyncClient.class);
+   }
+}
diff --git a/labs/elb/src/test/java/org/jclouds/elb/internal/BaseELBClientExpectTest.java b/labs/elb/src/test/java/org/jclouds/elb/internal/BaseELBClientExpectTest.java
new file mode 100644
index 0000000..c713868
--- /dev/null
+++ b/labs/elb/src/test/java/org/jclouds/elb/internal/BaseELBClientExpectTest.java
@@ -0,0 +1,30 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.elb.internal;
+
+import org.jclouds.elb.ELBClient;
+
+
+/**
+ * 
+ * @author Adrian Cole
+ */
+public class BaseELBClientExpectTest extends BaseELBExpectTest<ELBClient> {
+
+}
diff --git a/labs/elb/src/test/java/org/jclouds/elb/internal/BaseELBClientLiveTest.java b/labs/elb/src/test/java/org/jclouds/elb/internal/BaseELBClientLiveTest.java
new file mode 100644
index 0000000..95c757d
--- /dev/null
+++ b/labs/elb/src/test/java/org/jclouds/elb/internal/BaseELBClientLiveTest.java
@@ -0,0 +1,46 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.elb.internal;
+
+import org.jclouds.apis.BaseContextLiveTest;
+import org.jclouds.elb.ELBApiMetadata;
+import org.jclouds.elb.ELBAsyncClient;
+import org.jclouds.elb.ELBClient;
+import org.jclouds.rest.RestContext;
+import org.testng.annotations.Test;
+
+import com.google.common.reflect.TypeToken;
+
+/**
+ * 
+ * @author Adrian Cole
+ */
+@Test(groups = "live")
+public class BaseELBClientLiveTest extends BaseContextLiveTest<RestContext<ELBClient, ELBAsyncClient>> {
+
+   public BaseELBClientLiveTest() {
+      provider = "elb";
+   }
+   
+   @Override
+   protected TypeToken<RestContext<ELBClient, ELBAsyncClient>> contextType() {
+      return ELBApiMetadata.CONTEXT_TOKEN;
+   }
+
+}
diff --git a/labs/elb/src/test/java/org/jclouds/elb/internal/BaseELBExpectTest.java b/labs/elb/src/test/java/org/jclouds/elb/internal/BaseELBExpectTest.java
new file mode 100644
index 0000000..9da28ef
--- /dev/null
+++ b/labs/elb/src/test/java/org/jclouds/elb/internal/BaseELBExpectTest.java
@@ -0,0 +1,78 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.elb.internal;
+
+import static com.google.common.collect.Maps.transformValues;
+
+import java.net.URI;
+import java.util.Map;
+
+import org.jclouds.aws.domain.Region;
+import org.jclouds.date.DateService;
+import org.jclouds.elb.config.ELBRestClientModule;
+import org.jclouds.location.config.LocationModule;
+import org.jclouds.location.suppliers.RegionIdToURISupplier;
+import org.jclouds.rest.ConfiguresRestClient;
+import org.jclouds.rest.internal.BaseRestClientExpectTest;
+import org.jclouds.util.Suppliers2;
+
+import com.google.common.base.Supplier;
+import com.google.common.collect.ImmutableMap;
+import com.google.inject.Module;
+
+/**
+ * 
+ * @author Adrian Cole
+ */
+public class BaseELBExpectTest<T> extends BaseRestClientExpectTest<T> {
+
+   public BaseELBExpectTest() {
+      provider = "elb";
+   }
+   
+   @ConfiguresRestClient
+   private static final class TestELBRestClientModule extends ELBRestClientModule {
+
+      @Override
+      protected void installLocations() {
+         install(new LocationModule());
+         bind(RegionIdToURISupplier.class).toInstance(new RegionIdToURISupplier() {
+
+            @Override
+            public Map<String, Supplier<URI>> get() {
+               return transformValues(ImmutableMap.<String, URI> of(Region.EU_WEST_1, URI
+                        .create("https://elasticloadbalancing.eu-west-1.amazonaws.com"), Region.US_EAST_1, URI
+                        .create("https://elasticloadbalancing.us-east-1.amazonaws.com"), Region.US_WEST_1, URI
+                        .create("https://elasticloadbalancing.us-west-1.amazonaws.com")), Suppliers2.<URI> ofInstanceFunction());
+            }
+
+         });
+      }
+
+      @Override
+      protected String provideTimeStamp(final DateService dateService) {
+         return "2009-11-08T15:54:08.897Z";
+      }
+   }
+
+   @Override
+   protected Module createModule() {
+      return new TestELBRestClientModule();
+   }
+}
diff --git a/labs/elb/src/test/java/org/jclouds/elb/loadbalancer/ELBLoadBalancerServiceLiveTest.java b/labs/elb/src/test/java/org/jclouds/elb/loadbalancer/ELBLoadBalancerServiceLiveTest.java
index 89334a2..9d23ff6 100644
--- a/labs/elb/src/test/java/org/jclouds/elb/loadbalancer/ELBLoadBalancerServiceLiveTest.java
+++ b/labs/elb/src/test/java/org/jclouds/elb/loadbalancer/ELBLoadBalancerServiceLiveTest.java
@@ -27,7 +27,7 @@
 import org.jclouds.compute.domain.NodeMetadata;
 import org.jclouds.elb.ELBAsyncClient;
 import org.jclouds.elb.ELBClient;
-import org.jclouds.elb.domain.LoadBalancer;
+import org.jclouds.elb.domain.CrappyLoadBalancer;
 import org.jclouds.loadbalancer.BaseLoadBalancerServiceLiveTest;
 import org.jclouds.rest.RestContext;
 import org.jclouds.sshj.config.SshjSshClientModule;
@@ -61,9 +61,9 @@
       for (NodeMetadata node : nodes) {
          instanceIds.add(node.getProviderId());
       }
-      Set<? extends LoadBalancer> elbs = elbClient.describeLoadBalancersInRegion(null);
+      Set<? extends CrappyLoadBalancer> elbs = elbClient.describeLoadBalancersInRegion(null);
       assertNotNull(elbs);
-      for (LoadBalancer elb : elbs) {
+      for (CrappyLoadBalancer elb : elbs) {
          if (elb.getName().equals(group))
             assertEquals(elb.getInstanceIds(), instanceIds);
       }
diff --git a/labs/elb/src/test/java/org/jclouds/elb/options/ListLoadBalancersOptionsTest.java b/labs/elb/src/test/java/org/jclouds/elb/options/ListLoadBalancersOptionsTest.java
new file mode 100644
index 0000000..490d537
--- /dev/null
+++ b/labs/elb/src/test/java/org/jclouds/elb/options/ListLoadBalancersOptionsTest.java
@@ -0,0 +1,57 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.elb.options;
+
+import static org.jclouds.elb.options.ListLoadBalancersOptions.Builder.marker;
+import static org.jclouds.elb.options.ListLoadBalancersOptions.Builder.name;
+import static org.testng.Assert.assertEquals;
+
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * Tests behavior of {@code ListLoadBalancersOptions}
+ * 
+ * @author Adrian Cole
+ */
+@Test(groups = "unit", testName = "ListLoadBalancersOptionsTest")
+public class ListLoadBalancersOptionsTest {
+
+   public void testMarker() {
+      ListLoadBalancersOptions options = new ListLoadBalancersOptions().marker("FFFFF");
+      assertEquals(ImmutableSet.of("FFFFF"), options.buildFormParameters().get("Marker"));
+   }
+
+   public void testMarkerStatic() {
+      ListLoadBalancersOptions options = marker("FFFFF");
+      assertEquals(ImmutableSet.of("FFFFF"), options.buildFormParameters().get("Marker"));
+   }
+
+   public void testName() {
+      ListLoadBalancersOptions options = new ListLoadBalancersOptions().name("my-load-balancer");
+      assertEquals(ImmutableSet.of("my-load-balancer"), options.buildFormParameters().get("LoadBalancerNames.member.1"));
+   }
+
+   public void testNameStatic() {
+      ListLoadBalancersOptions options = name("my-load-balancer");
+      assertEquals(ImmutableSet.of("my-load-balancer"), options.buildFormParameters().get("LoadBalancerNames.member.1"));
+   }
+
+}
diff --git a/labs/elb/src/test/java/org/jclouds/elb/options/ListPoliciesOptionsTest.java b/labs/elb/src/test/java/org/jclouds/elb/options/ListPoliciesOptionsTest.java
new file mode 100644
index 0000000..dcfe15e
--- /dev/null
+++ b/labs/elb/src/test/java/org/jclouds/elb/options/ListPoliciesOptionsTest.java
@@ -0,0 +1,57 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.elb.options;
+
+import static org.jclouds.elb.options.ListPoliciesOptions.Builder.loadBalancerName;
+import static org.jclouds.elb.options.ListPoliciesOptions.Builder.name;
+import static org.testng.Assert.assertEquals;
+
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * Tests behavior of {@code ListPoliciesOptions}
+ * 
+ * @author Adrian Cole
+ */
+@Test(groups = "unit", testName = "ListPoliciesOptionsTest")
+public class ListPoliciesOptionsTest {
+   
+   public void testLoadBalancerName() {
+      ListPoliciesOptions options = new ListPoliciesOptions().loadBalancerName("FFFFF");
+      assertEquals(ImmutableSet.of("FFFFF"), options.buildFormParameters().get("LoadBalancerName"));
+   }
+
+   public void testLoadBalancerNameStatic() {
+      ListPoliciesOptions options = loadBalancerName("FFFFF");
+      assertEquals(ImmutableSet.of("FFFFF"), options.buildFormParameters().get("LoadBalancerName"));
+   }
+
+   public void testName() {
+      ListPoliciesOptions options = new ListPoliciesOptions().name("my-load-balancer");
+      assertEquals(ImmutableSet.of("my-load-balancer"), options.buildFormParameters().get("PolicyNames.member.1"));
+   }
+
+   public void testNameStatic() {
+      ListPoliciesOptions options = name("my-load-balancer");
+      assertEquals(ImmutableSet.of("my-load-balancer"), options.buildFormParameters().get("PolicyNames.member.1"));
+   }
+
+}
diff --git a/labs/elb/src/test/java/org/jclouds/elb/parse/DescribeLoadBalancerPoliciesResponseTest.java b/labs/elb/src/test/java/org/jclouds/elb/parse/DescribeLoadBalancerPoliciesResponseTest.java
new file mode 100644
index 0000000..b28f0fc
--- /dev/null
+++ b/labs/elb/src/test/java/org/jclouds/elb/parse/DescribeLoadBalancerPoliciesResponseTest.java
@@ -0,0 +1,69 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.elb.parse;
+
+import static org.testng.Assert.assertEquals;
+
+import java.io.InputStream;
+import java.util.Set;
+
+import org.jclouds.elb.domain.Policy;
+import org.jclouds.elb.xml.DescribeLoadBalancerPoliciesResultHandler;
+import org.jclouds.http.functions.BaseHandlerTest;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * @author Adrian Cole
+ */
+// NOTE:without testName, this will not call @Before* and fail w/NPE during surefire
+@Test(groups = "unit", testName = "DescribePoliciesResponseTest")
+public class DescribeLoadBalancerPoliciesResponseTest extends BaseHandlerTest {
+
+   public void test() {
+      InputStream is = getClass().getResourceAsStream("/describe_policies.xml");
+
+      Set<Policy> expected = expected();
+
+      DescribeLoadBalancerPoliciesResultHandler handler = injector.getInstance(DescribeLoadBalancerPoliciesResultHandler.class);
+      Set<Policy> result = factory.create(handler).parse(is);
+
+      assertEquals(result.toString(), expected.toString());
+
+   }
+
+   public Set<Policy> expected() {
+      return ImmutableSet.of(
+               Policy.builder()
+                     .name("ELBSample-OpenSSLDefaultNegotiationPolicy")
+                     .typeName("SSLNegotiationPolicyType")
+                     .attribute("Protocol-SSLv2", false)
+                     .attribute("ADH-AES256-SHA", false)
+                     .attribute("DHE-RSA-AES256-SHA", true)
+                     .build(),
+               Policy.builder()
+                     .name("ELBSample-ELBDefaultNegotiationPolicy")
+                     .typeName("SSLNegotiationPolicyType")
+                     .attribute("Protocol-SSLv2", false)
+                     .attribute("EDH-DSS-DES-CBC3-SHA", false)
+                     .build()
+               );
+   }
+}
diff --git a/labs/elb/src/test/java/org/jclouds/elb/parse/DescribeLoadBalancerPolicyTypesResponseTest.java b/labs/elb/src/test/java/org/jclouds/elb/parse/DescribeLoadBalancerPolicyTypesResponseTest.java
new file mode 100644
index 0000000..efd5458
--- /dev/null
+++ b/labs/elb/src/test/java/org/jclouds/elb/parse/DescribeLoadBalancerPolicyTypesResponseTest.java
@@ -0,0 +1,110 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.elb.parse;
+
+import static org.testng.Assert.assertEquals;
+
+import java.io.InputStream;
+import java.util.Set;
+
+import org.jclouds.elb.domain.AttributeMetadata;
+import org.jclouds.elb.domain.AttributeMetadata.Cardinality;
+import org.jclouds.elb.domain.PolicyType;
+import org.jclouds.elb.xml.DescribeLoadBalancerPolicyTypesResultHandler;
+import org.jclouds.http.functions.BaseHandlerTest;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * @author Adrian Cole
+ */
+// NOTE:without testName, this will not call @Before* and fail w/NPE during surefire
+@Test(groups = "unit", testName = "DescribeLoadBalancerPolicyTypesResponseTest")
+public class DescribeLoadBalancerPolicyTypesResponseTest extends BaseHandlerTest {
+
+   public void test() {
+      InputStream is = getClass().getResourceAsStream("/describe_policy_types.xml");
+
+      Set<PolicyType> expected = expected();
+
+      DescribeLoadBalancerPolicyTypesResultHandler handler = injector.getInstance(DescribeLoadBalancerPolicyTypesResultHandler.class);
+      Set<PolicyType> result = factory.create(handler).parse(is);
+
+      assertEquals(result.toString(), expected.toString());
+
+   }
+
+   public Set<PolicyType> expected() {
+      return ImmutableSet.<PolicyType>builder()
+            .add(PolicyType.builder()
+                           .attributeMetadata(AttributeMetadata.<String>builder()
+                                                               .name("PublicKey")
+                                                               .type(String.class)
+                                                               .rawType("String")
+                                                               .cardinality(Cardinality.ONE)
+                                                               .build())
+                           .name("PublicKeyPolicyType")
+                           .description("Policy containing a list of public keys to accept when authenticating the back-end server(s). This policy cannot be applied directly to back-end servers or listeners but must be part of a BackendServerAuthenticationPolicyType.")
+                           .build())
+            .add(PolicyType.builder()
+                           .attributeMetadata(AttributeMetadata.<String>builder()
+                                                               .name("CookieName")
+                                                               .type(String.class)
+                                                               .rawType("String")
+                                                               .cardinality(Cardinality.ONE).build())
+                           .name("AppCookieStickinessPolicyType")
+                           .description("Stickiness policy with session lifetimes controlled by the lifetime of the application-generated cookie. This policy can be associated only with HTTP/HTTPS listeners.")
+                           .build())
+            .add(PolicyType.builder()
+                           .attributeMetadata(AttributeMetadata.<Long>builder()
+                                                               .name("CookieExpirationPeriod")
+                                                               .type(Long.class)
+                                                               .rawType("Long")
+                                                               .cardinality(Cardinality.ZERO_OR_ONE).build())
+                           .name("LBCookieStickinessPolicyType")
+                           .description("Stickiness policy with session lifetimes controlled by the browser (user-agent) or a specified expiration period. This policy can be associated only with HTTP/HTTPS listeners.")
+                           .build())
+            .add(PolicyType.builder()
+                           .attributeMetadata(AttributeMetadata.<Boolean>builder()
+                                                               .name("Protocol-SSLv2")
+                                                               .type(Boolean.class)
+                                                               .rawType("Boolean")
+                                                               .defaultValue(false)
+                                                               .cardinality(Cardinality.ZERO_OR_ONE).build())
+                           .attributeMetadata(AttributeMetadata.<Boolean>builder()
+                                                               .name("Protocol-TLSv1")
+                                                               .type(Boolean.class)
+                                                               .rawType("Boolean")
+                                                               .defaultValue(true)
+                                                               .cardinality(Cardinality.ZERO_OR_ONE).build())
+                           .name("SSLNegotiationPolicyType")
+                           .description("Listener policy that defines the ciphers and protocols that will be accepted by the load balancer. This policy can be associated only with HTTPS/SSL listeners.")
+                           .build())
+            .add(PolicyType.builder()
+                           .attributeMetadata(AttributeMetadata.<String>builder()
+                                                               .name("PublicKeyPolicyName")
+                                                               .type(String.class)
+                                                               .rawType("PolicyName")
+                                                               .cardinality(Cardinality.ONE_OR_MORE).build())
+                           .name("BackendServerAuthenticationPolicyType")
+                           .description("Policy that controls authentication to back-end server(s) and contains one or more policies, such as an instance of a PublicKeyPolicyType. This policy can be associated only with back-end servers that are using HTTPS/SSL.")
+                           .build()).build();
+   }
+}
diff --git a/labs/elb/src/test/java/org/jclouds/elb/parse/DescribeLoadBalancersResponseTest.java b/labs/elb/src/test/java/org/jclouds/elb/parse/DescribeLoadBalancersResponseTest.java
new file mode 100644
index 0000000..2d303ec
--- /dev/null
+++ b/labs/elb/src/test/java/org/jclouds/elb/parse/DescribeLoadBalancersResponseTest.java
@@ -0,0 +1,74 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.elb.parse;
+
+import static org.testng.Assert.assertEquals;
+
+import java.io.InputStream;
+
+import org.jclouds.collect.PaginatedSet;
+import org.jclouds.date.internal.SimpleDateFormatDateService;
+import org.jclouds.elb.domain.HealthCheck;
+import org.jclouds.elb.domain.ListenerWithPolicies;
+import org.jclouds.elb.domain.LoadBalancer;
+import org.jclouds.elb.domain.Protocol;
+import org.jclouds.elb.xml.DescribeLoadBalancersResultHandler;
+import org.jclouds.http.functions.BaseHandlerTest;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * @author Adrian Cole
+ */
+// NOTE:without testName, this will not call @Before* and fail w/NPE during surefire
+@Test(groups = "unit", testName = "DescribeLoadBalancersResponseTest")
+public class DescribeLoadBalancersResponseTest extends BaseHandlerTest {
+
+   public void test() {
+      InputStream is = getClass().getResourceAsStream("/describe_loadbalancers.xml");
+
+      PaginatedSet<LoadBalancer> expected = expected();
+
+      DescribeLoadBalancersResultHandler handler = injector.getInstance(DescribeLoadBalancersResultHandler.class);
+      PaginatedSet<LoadBalancer> result = factory.create(handler).parse(is);
+
+      assertEquals(result.toString(), expected.toString());
+
+   }
+
+   public PaginatedSet<LoadBalancer> expected() {
+      return PaginatedSet.copyOf(ImmutableSet.of(
+               LoadBalancer.builder()
+                    .name("my-load-balancer")
+                    .createdTime(new SimpleDateFormatDateService().iso8601DateParse("2010-03-03T20:54:45.110Z"))
+                    .dnsName("my-load-balancer-1400212309.us-east-1.elb.amazonaws.com")
+                    .healthCheck(HealthCheck.builder()
+                                            .interval(300)
+                                            .target("HTTP:80/index.html")
+                                            .healthyThreshold(3)
+                                            .timeout(30)
+                                            .unhealthyThreshold(5).build())
+                    .instanceIds(ImmutableSet.of("i-5b33e630", "i-8f26d7e4", "i-5933e632"))
+                    .listener(ListenerWithPolicies.builder().protocol(Protocol.HTTP).port(80).instancePort(80).build())
+                    .listener(ListenerWithPolicies.builder().protocol(Protocol.TCP).port(443).instancePort(443).build())
+                    .build()));
+   }
+
+}
diff --git a/labs/elb/src/test/java/org/jclouds/elb/parse/GetLoadBalancerResponseTest.java b/labs/elb/src/test/java/org/jclouds/elb/parse/GetLoadBalancerResponseTest.java
new file mode 100644
index 0000000..c24e0ad
--- /dev/null
+++ b/labs/elb/src/test/java/org/jclouds/elb/parse/GetLoadBalancerResponseTest.java
@@ -0,0 +1,75 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.elb.parse;
+
+import static org.testng.Assert.assertEquals;
+
+import java.io.InputStream;
+
+import org.jclouds.date.internal.SimpleDateFormatDateService;
+import org.jclouds.elb.domain.HealthCheck;
+import org.jclouds.elb.domain.ListenerWithPolicies;
+import org.jclouds.elb.domain.LoadBalancer;
+import org.jclouds.elb.domain.Protocol;
+import org.jclouds.elb.xml.LoadBalancerHandler;
+import org.jclouds.http.functions.BaseHandlerTest;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * @author Adrian Cole
+ */
+// NOTE:without testName, this will not call @Before* and fail w/NPE during surefire
+@Test(groups = "unit", testName = "GetLoadBalancerResponseTest")
+public class GetLoadBalancerResponseTest extends BaseHandlerTest {
+
+   public void test() {
+      InputStream is = getClass().getResourceAsStream("/describe_loadbalancers.xml");
+
+      LoadBalancer expected = expected();
+
+      LoadBalancerHandler handler = injector.getInstance(LoadBalancerHandler.class);
+      LoadBalancer result = factory.create(handler).parse(is);
+
+      assertEquals(result, expected);
+      assertEquals(result.getCreatedTime(), expected.getCreatedTime());
+      assertEquals(result.getDnsName(), expected.getDnsName());
+      assertEquals(result.getHealthCheck(), expected.getHealthCheck());
+      assertEquals(result.getScheme(), expected.getScheme());
+      assertEquals(result.getVPCId(), expected.getVPCId());
+   }
+
+   public LoadBalancer expected() {
+      return LoadBalancer.builder()
+                 .name("my-load-balancer")
+                 .createdTime(new SimpleDateFormatDateService().iso8601DateParse("2010-03-03T20:54:45.110Z"))
+                 .dnsName("my-load-balancer-1400212309.us-east-1.elb.amazonaws.com")
+                 .healthCheck(HealthCheck.builder()
+                                         .interval(300)
+                                         .target("HTTP:80/index.html")
+                                         .healthyThreshold(3)
+                                         .timeout(30)
+                                         .unhealthyThreshold(5).build())
+                 .instanceIds(ImmutableSet.of("i-5b33e630", "i-8f26d7e4", "i-5933e632"))
+                 .listener(ListenerWithPolicies.builder().protocol(Protocol.HTTP).port(80).instancePort(80).build())
+                 .listener(ListenerWithPolicies.builder().protocol(Protocol.TCP).port(443).instancePort(443).build())
+                 .build();
+   }
+}
diff --git a/labs/elb/src/test/java/org/jclouds/elb/parse/GetPolicyResponseTest.java b/labs/elb/src/test/java/org/jclouds/elb/parse/GetPolicyResponseTest.java
new file mode 100644
index 0000000..25813c1
--- /dev/null
+++ b/labs/elb/src/test/java/org/jclouds/elb/parse/GetPolicyResponseTest.java
@@ -0,0 +1,58 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.elb.parse;
+
+import static org.testng.Assert.assertEquals;
+
+import java.io.InputStream;
+
+import org.jclouds.elb.domain.Policy;
+import org.jclouds.elb.xml.PolicyHandler;
+import org.jclouds.http.functions.BaseHandlerTest;
+import org.testng.annotations.Test;
+
+/**
+ * @author Adrian Cole
+ */
+// NOTE:without testName, this will not call @Before* and fail w/NPE during surefire
+@Test(groups = "unit", testName = "GetPolicyResponseTest")
+public class GetPolicyResponseTest extends BaseHandlerTest {
+
+   public void test() {
+      InputStream is = getClass().getResourceAsStream("/describe_policy.xml");
+
+      Policy expected = expected();
+
+      PolicyHandler handler = injector.getInstance(PolicyHandler.class);
+      Policy result = factory.create(handler).parse(is);
+
+      assertEquals(result, expected);
+      assertEquals(result.getAttributes(), expected.getAttributes());
+   }
+
+   public Policy expected() {
+      return Policy.builder()
+                   .name("ELBSample-OpenSSLDefaultNegotiationPolicy")
+                   .typeName("SSLNegotiationPolicyType")
+                   .attribute("Protocol-SSLv2", false)
+                   .attribute("ADH-AES256-SHA", false)
+                   .attribute("DHE-RSA-AES256-SHA", true)
+                   .build();
+   }
+}
diff --git a/labs/elb/src/test/java/org/jclouds/elb/parse/GetPolicyTypeResponseTest.java b/labs/elb/src/test/java/org/jclouds/elb/parse/GetPolicyTypeResponseTest.java
new file mode 100644
index 0000000..04fd370
--- /dev/null
+++ b/labs/elb/src/test/java/org/jclouds/elb/parse/GetPolicyTypeResponseTest.java
@@ -0,0 +1,62 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.elb.parse;
+
+import static org.testng.Assert.assertEquals;
+
+import java.io.InputStream;
+
+import org.jclouds.elb.domain.AttributeMetadata;
+import org.jclouds.elb.domain.AttributeMetadata.Cardinality;
+import org.jclouds.elb.domain.PolicyType;
+import org.jclouds.elb.xml.PolicyTypeHandler;
+import org.jclouds.http.functions.BaseHandlerTest;
+import org.testng.annotations.Test;
+
+/**
+ * @author Adrian Cole
+ */
+// NOTE:without testName, this will not call @Before* and fail w/NPE during surefire
+@Test(groups = "unit", testName = "GetPolicyTypeResponseTest")
+public class GetPolicyTypeResponseTest extends BaseHandlerTest {
+
+   public void test() {
+      InputStream is = getClass().getResourceAsStream("/describe_policy_type.xml");
+
+      PolicyType expected = expected();
+
+      PolicyTypeHandler handler = injector.getInstance(PolicyTypeHandler.class);
+      PolicyType result = factory.create(handler).parse(is);
+
+      assertEquals(result, expected);
+   }
+
+   public PolicyType expected() {
+      return PolicyType.builder()
+                       .attributeMetadata(AttributeMetadata.<String>builder()
+                                 .name("PublicKey")
+                                 .type(String.class)
+                                 .rawType("String")
+                                 .cardinality(Cardinality.ONE)
+                                 .build())
+                       .name("PublicKeyPolicyType")
+                       .description("Policy containing a list of public keys to accept when authenticating the back-end server(s). This policy cannot be applied directly to back-end servers or listeners but must be part of a BackendServerAuthenticationPolicyType.")
+                       .build();
+   }
+}
diff --git a/labs/elb/src/test/java/org/jclouds/elb/xml/DescribeLoadBalancerResponseHandlerTest.java b/labs/elb/src/test/java/org/jclouds/elb/xml/DescribeLoadBalancerResponseHandlerTest.java
index 6577a92..fb78e05 100644
--- a/labs/elb/src/test/java/org/jclouds/elb/xml/DescribeLoadBalancerResponseHandlerTest.java
+++ b/labs/elb/src/test/java/org/jclouds/elb/xml/DescribeLoadBalancerResponseHandlerTest.java
@@ -26,7 +26,7 @@
 import java.io.InputStream;
 import java.util.Set;
 
-import org.jclouds.elb.domain.LoadBalancer;
+import org.jclouds.elb.domain.CrappyLoadBalancer;
 import org.jclouds.http.functions.BaseHandlerTest;
 import org.jclouds.http.functions.ParseSax;
 import org.jclouds.rest.internal.GeneratedHttpRequest;
@@ -48,21 +48,21 @@
    public void testParse() {
       InputStream is = getClass().getResourceAsStream("/describe_loadbalancers.xml");
 
-      Set<LoadBalancer> contents = Sets.newHashSet();
-      LoadBalancer dummy = new LoadBalancer(null, "my-load-balancer", ImmutableSet.of("i-5b33e630",
+      Set<CrappyLoadBalancer> contents = Sets.newHashSet();
+      CrappyLoadBalancer dummy = new CrappyLoadBalancer(null, "my-load-balancer", ImmutableSet.of("i-5b33e630",
             "i-8f26d7e4", "i-5933e632"), ImmutableSet.of("us-east-1a"),
             "my-load-balancer-1400212309.us-east-1.elb.amazonaws.com");
       contents.add(dummy);
 
-      Set<LoadBalancer> result = parseLoadBalancers(is);
+      Set<CrappyLoadBalancer> result = parseLoadBalancers(is);
 
       assertEquals(result, contents);
    }
 
-   private Set<LoadBalancer> parseLoadBalancers(InputStream is) {
+   private Set<CrappyLoadBalancer> parseLoadBalancers(InputStream is) {
       DescribeLoadBalancersResponseHandler handler = injector.getInstance(DescribeLoadBalancersResponseHandler.class);
       addDefaultRegionToHandler(handler);
-      Set<LoadBalancer> result = factory.create(handler).parse(is);
+      Set<CrappyLoadBalancer> result = factory.create(handler).parse(is);
       return result;
    }
 
diff --git a/labs/elb/src/test/resources/describe_policies.xml b/labs/elb/src/test/resources/describe_policies.xml
new file mode 100644
index 0000000..9e8f6a5
--- /dev/null
+++ b/labs/elb/src/test/resources/describe_policies.xml
@@ -0,0 +1,41 @@
+<DescribeLoadBalancerPoliciesResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/">
+  <DescribeLoadBalancerPoliciesResult>
+    <PolicyDescriptions>
+      <member>
+        <PolicyName>ELBSample-OpenSSLDefaultNegotiationPolicy</PolicyName>
+        <PolicyTypeName>SSLNegotiationPolicyType</PolicyTypeName>
+        <PolicyAttributeDescriptions>
+          <member>
+            <AttributeName>Protocol-SSLv2</AttributeName>
+            <AttributeValue>false</AttributeValue>
+          </member>
+          <member>
+            <AttributeName>ADH-AES256-SHA</AttributeName>
+            <AttributeValue>false</AttributeValue>
+          </member>
+          <member>
+            <AttributeName>DHE-RSA-AES256-SHA</AttributeName>
+            <AttributeValue>true</AttributeValue>
+          </member>
+        </PolicyAttributeDescriptions>
+      </member>
+      <member>
+        <PolicyName>ELBSample-ELBDefaultNegotiationPolicy</PolicyName>
+        <PolicyTypeName>SSLNegotiationPolicyType</PolicyTypeName>
+        <PolicyAttributeDescriptions>
+          <member>
+            <AttributeName>Protocol-SSLv2</AttributeName>
+            <AttributeValue>false</AttributeValue>
+          </member>
+          <member>
+            <AttributeName>EDH-DSS-DES-CBC3-SHA</AttributeName>
+            <AttributeValue>false</AttributeValue>
+          </member>
+        </PolicyAttributeDescriptions>
+      </member>
+    </PolicyDescriptions>
+  </DescribeLoadBalancerPoliciesResult>
+  <ResponseMetadata>
+    <RequestId>a60aacd5-c3c0-11e1-afb7-8f588567225c</RequestId>
+  </ResponseMetadata>
+</DescribeLoadBalancerPoliciesResponse>
\ No newline at end of file
diff --git a/labs/elb/src/test/resources/describe_policy.xml b/labs/elb/src/test/resources/describe_policy.xml
new file mode 100644
index 0000000..76ca5e2
--- /dev/null
+++ b/labs/elb/src/test/resources/describe_policy.xml
@@ -0,0 +1,27 @@
+<DescribeLoadBalancerPoliciesResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/">
+  <DescribeLoadBalancerPoliciesResult>
+    <PolicyDescriptions>
+      <member>
+        <PolicyName>ELBSample-OpenSSLDefaultNegotiationPolicy</PolicyName>
+        <PolicyTypeName>SSLNegotiationPolicyType</PolicyTypeName>
+        <PolicyAttributeDescriptions>
+          <member>
+            <AttributeName>Protocol-SSLv2</AttributeName>
+            <AttributeValue>false</AttributeValue>
+          </member>
+          <member>
+            <AttributeName>ADH-AES256-SHA</AttributeName>
+            <AttributeValue>false</AttributeValue>
+          </member>
+          <member>
+            <AttributeName>DHE-RSA-AES256-SHA</AttributeName>
+            <AttributeValue>true</AttributeValue>
+          </member>
+        </PolicyAttributeDescriptions>
+      </member>
+    </PolicyDescriptions>
+  </DescribeLoadBalancerPoliciesResult>
+  <ResponseMetadata>
+    <RequestId>a60aacd5-c3c0-11e1-afb7-8f588567225c</RequestId>
+  </ResponseMetadata>
+</DescribeLoadBalancerPoliciesResponse>
\ No newline at end of file
diff --git a/labs/elb/src/test/resources/describe_policy_type.xml b/labs/elb/src/test/resources/describe_policy_type.xml
new file mode 100644
index 0000000..656e50f
--- /dev/null
+++ b/labs/elb/src/test/resources/describe_policy_type.xml
@@ -0,0 +1,20 @@
+<DescribeLoadBalancerPolicyTypesResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/">
+  <DescribeLoadBalancerPolicyTypesResult>
+    <PolicyTypeDescriptions>
+      <member>
+        <PolicyAttributeTypeDescriptions>
+          <member>
+            <AttributeName>PublicKey</AttributeName>
+            <AttributeType>String</AttributeType>
+            <Cardinality>ONE</Cardinality>
+          </member>
+        </PolicyAttributeTypeDescriptions>
+        <PolicyTypeName>PublicKeyPolicyType</PolicyTypeName>
+        <Description>Policy containing a list of public keys to accept when authenticating the back-end server(s). This policy cannot be applied directly to back-end servers or listeners but must be part of a BackendServerAuthenticationPolicyType.</Description>
+      </member>
+    </PolicyTypeDescriptions>
+  </DescribeLoadBalancerPolicyTypesResult>
+  <ResponseMetadata>
+    <RequestId>261327c5-c3d7-11e1-8582-35377554a47d</RequestId>
+  </ResponseMetadata>
+</DescribeLoadBalancerPolicyTypesResponse>
\ No newline at end of file
diff --git a/labs/elb/src/test/resources/describe_policy_types.xml b/labs/elb/src/test/resources/describe_policy_types.xml
new file mode 100644
index 0000000..fb5480c
--- /dev/null
+++ b/labs/elb/src/test/resources/describe_policy_types.xml
@@ -0,0 +1,71 @@
+<DescribeLoadBalancerPolicyTypesResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/">
+  <DescribeLoadBalancerPolicyTypesResult>
+    <PolicyTypeDescriptions>
+      <member>
+        <PolicyAttributeTypeDescriptions>
+          <member>
+            <AttributeName>PublicKey</AttributeName>
+            <AttributeType>String</AttributeType>
+            <Cardinality>ONE</Cardinality>
+          </member>
+        </PolicyAttributeTypeDescriptions>
+        <PolicyTypeName>PublicKeyPolicyType</PolicyTypeName>
+        <Description>Policy containing a list of public keys to accept when authenticating the back-end server(s). This policy cannot be applied directly to back-end servers or listeners but must be part of a BackendServerAuthenticationPolicyType.</Description>
+      </member>
+      <member>
+        <PolicyAttributeTypeDescriptions>
+          <member>
+            <AttributeName>CookieName</AttributeName>
+            <AttributeType>String</AttributeType>
+            <Cardinality>ONE</Cardinality>
+          </member>
+        </PolicyAttributeTypeDescriptions>
+        <PolicyTypeName>AppCookieStickinessPolicyType</PolicyTypeName>
+        <Description>Stickiness policy with session lifetimes controlled by the lifetime of the application-generated cookie. This policy can be associated only with HTTP/HTTPS listeners.</Description>
+      </member>
+      <member>
+        <PolicyAttributeTypeDescriptions>
+          <member>
+            <AttributeName>CookieExpirationPeriod</AttributeName>
+            <AttributeType>Long</AttributeType>
+            <Cardinality>ZERO_OR_ONE</Cardinality>
+          </member>
+        </PolicyAttributeTypeDescriptions>
+        <PolicyTypeName>LBCookieStickinessPolicyType</PolicyTypeName>
+        <Description>Stickiness policy with session lifetimes controlled by the browser (user-agent) or a specified expiration period. This policy can be associated only with HTTP/HTTPS listeners.</Description>
+      </member>
+      <member>
+        <PolicyAttributeTypeDescriptions>
+          <member>
+            <AttributeName>Protocol-SSLv2</AttributeName>
+            <AttributeType>Boolean</AttributeType>
+            <DefaultValue>false</DefaultValue>
+            <Cardinality>ZERO_OR_ONE</Cardinality>
+          </member>
+          <member>
+            <AttributeName>Protocol-TLSv1</AttributeName>
+            <AttributeType>Boolean</AttributeType>
+            <DefaultValue>true</DefaultValue>
+            <Cardinality>ZERO_OR_ONE</Cardinality>
+          </member>
+        </PolicyAttributeTypeDescriptions>
+        <PolicyTypeName>SSLNegotiationPolicyType</PolicyTypeName>
+        <Description>Listener policy that defines the ciphers and protocols that will be accepted by the load balancer. This policy can be associated only with HTTPS/SSL listeners.</Description>
+      </member>
+      <member>
+        <PolicyAttributeTypeDescriptions>
+          <member>
+            <AttributeName>PublicKeyPolicyName</AttributeName>
+            <AttributeType>PolicyName</AttributeType>
+            <Cardinality>ONE_OR_MORE</Cardinality>
+          </member>
+        </PolicyAttributeTypeDescriptions>
+        <PolicyTypeName>BackendServerAuthenticationPolicyType</PolicyTypeName>
+        <Description>Policy that controls authentication to back-end server(s) and contains one or more policies, such as an instance of a PublicKeyPolicyType. This policy can be associated only with back-end servers that are using HTTPS/SSL.</Description>
+      </member>
+    </PolicyTypeDescriptions>
+  </DescribeLoadBalancerPolicyTypesResult>
+  <ResponseMetadata>
+    <RequestId>261327c5-c3d7-11e1-8582-35377554a47d</RequestId>
+  </ResponseMetadata>
+</DescribeLoadBalancerPolicyTypesResponse>
\ No newline at end of file
diff --git a/labs/elb/src/test/resources/logback.xml b/labs/elb/src/test/resources/logback.xml
new file mode 100644
index 0000000..8f628c3
--- /dev/null
+++ b/labs/elb/src/test/resources/logback.xml
@@ -0,0 +1,64 @@
+<?xml version="1.0"?>

+<configuration scan="false">

+    <appender name="FILE" class="ch.qos.logback.core.FileAppender">

+        <file>target/test-data/jclouds.log</file>

+

+        <encoder>

+            <Pattern>%d %-5p [%c] [%thread] %m%n</Pattern>

+        </encoder>

+    </appender>

+

+    <appender name="WIREFILE" class="ch.qos.logback.core.FileAppender">

+        <file>target/test-data/jclouds-wire.log</file>

+

+        <encoder>

+            <Pattern>%d %-5p [%c] [%thread] %m%n</Pattern>

+        </encoder>

+    </appender>

+

+    <appender name="COMPUTEFILE" class="ch.qos.logback.core.FileAppender">

+        <file>target/test-data/jclouds-compute.log</file>

+

+        <encoder>

+            <Pattern>%d %-5p [%c] [%thread] %m%n</Pattern>

+        </encoder>

+    </appender>

+    

+    <appender name="SSHFILE" class="ch.qos.logback.core.FileAppender">

+        <file>target/test-data/jclouds-ssh.log</file>

+

+        <encoder>

+            <Pattern>%d %-5p [%c] [%thread] %m%n</Pattern>

+        </encoder>

+    </appender>

+

+    <root>

+        <level value="warn" />

+    </root>

+

+    <logger name="org.jclouds">

+        <level value="DEBUG" />

+        <appender-ref ref="FILE" />

+    </logger>

+

+    <logger name="jclouds.wire">

+        <level value="DEBUG" />

+        <appender-ref ref="WIREFILE" />

+    </logger>

+

+    <logger name="jclouds.headers">

+        <level value="DEBUG" />

+        <appender-ref ref="WIREFILE" />

+    </logger>

+

+    <logger name="jclouds.compute">

+        <level value="DEBUG" />

+        <appender-ref ref="COMPUTEFILE" />

+    </logger>

+    

+    <logger name="jclouds.ssh">

+        <level value="DEBUG" />

+        <appender-ref ref="SSHFILE" />

+    </logger>

+

+</configuration>

diff --git a/labs/glesys/src/main/java/org/jclouds/glesys/config/GleSYSParserModule.java b/labs/glesys/src/main/java/org/jclouds/glesys/config/GleSYSParserModule.java
index ea77989..e429296 100644
--- a/labs/glesys/src/main/java/org/jclouds/glesys/config/GleSYSParserModule.java
+++ b/labs/glesys/src/main/java/org/jclouds/glesys/config/GleSYSParserModule.java
@@ -23,9 +23,10 @@
 
 import javax.inject.Singleton;
 
+import org.jclouds.glesys.domain.GleSYSBoolean;
 import org.jclouds.glesys.domain.Server;
 import org.jclouds.glesys.functions.internal.GleSYSTypeAdapters;
-import org.jclouds.glesys.functions.internal.GlesysDateAdapter;
+import org.jclouds.json.config.GsonModule.Iso8601DateAdapter;
 import org.jclouds.json.config.GsonModule.DateAdapter;
 
 import com.google.common.collect.ImmutableMap;
@@ -34,18 +35,20 @@
 
 /**
  * @author Adrian Cole
+ * @author Adam Lowe
  */
 public class GleSYSParserModule extends AbstractModule {
 
    @Provides
    @Singleton
    public Map<Type, Object> provideCustomAdapterBindings() {
-      return ImmutableMap.<Type, Object> of(Server.State.class, new GleSYSTypeAdapters.ServerStateAdapter());
+      return ImmutableMap.<Type, Object>of(Server.State.class, new GleSYSTypeAdapters.ServerStateAdapter(),
+                                           GleSYSBoolean.class, new GleSYSTypeAdapters.GleSYSBooleanAdapter());
    }
 
    @Override
    protected void configure() {
-      bind(DateAdapter.class).to(GlesysDateAdapter.class);
+      bind(DateAdapter.class).to(Iso8601DateAdapter.class);
    }
 
 }
diff --git a/labs/glesys/src/main/java/org/jclouds/glesys/domain/AllowedArgumentsForCreateServer.java b/labs/glesys/src/main/java/org/jclouds/glesys/domain/AllowedArgumentsForCreateServer.java
index 8769902..8c17e9b 100644
--- a/labs/glesys/src/main/java/org/jclouds/glesys/domain/AllowedArgumentsForCreateServer.java
+++ b/labs/glesys/src/main/java/org/jclouds/glesys/domain/AllowedArgumentsForCreateServer.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -20,12 +20,12 @@
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import java.beans.ConstructorProperties;
 import java.util.Set;
 
-import com.google.common.base.Joiner;
 import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
 import com.google.common.collect.ImmutableSet;
-import com.google.gson.annotations.SerializedName;
 
 /**
  * Sets the allowed arguments for some of the functions in this module such as disksize, cpucores etc.
@@ -34,79 +34,104 @@
  * @see <a href="https://customer.glesys.com/api.php?a=doc#server_allowedarguments" />
  */
 public class AllowedArgumentsForCreateServer {
-   public static Builder builder() {
-      return new Builder();
+
+   public static Builder<?> builder() {
+      return new ConcreteBuilder();
    }
 
-   public static class Builder {
-      private Set<Integer> diskSizes;
-      private Set<Integer> memorySizes;
-      private Set<Integer> cpuCores;
-      private Set<String> templates;
-      private Set<Integer> transfers;
-      private Set<String> dataCenters;
+   public Builder<?> toBuilder() {
+      return new ConcreteBuilder().fromAllowedArgumentsForCreateServer(this);
+   }
 
-      public Builder diskSizes(Integer... sizes) {
-         return diskSizes(ImmutableSet.<Integer>copyOf(sizes));
+   public static abstract class Builder<T extends Builder<T>> {
+      protected abstract T self();
+
+      protected Set<Integer> diskSizes = ImmutableSet.of();
+      protected Set<Integer> memorySizes = ImmutableSet.of();
+      protected Set<Integer> cpuCores = ImmutableSet.of();
+      protected Set<String> templates = ImmutableSet.of();
+      protected Set<Integer> transfers = ImmutableSet.of();
+      protected Set<String> dataCenters = ImmutableSet.of();
+
+      /**
+       * @see AllowedArgumentsForCreateServer#getDiskSizesInGB()
+       */
+      public T diskSizes(Set<Integer> diskSizes) {
+         this.diskSizes = ImmutableSet.copyOf(checkNotNull(diskSizes, "diskSizesInGB"));
+         return self();
       }
 
-      public Builder diskSizes(Set<Integer> sizes) {
-         this.diskSizes = sizes;
-         return this;
+      public T diskSizes(Integer... in) {
+         return diskSizes(ImmutableSet.copyOf(in));
       }
 
-      public Builder memorySizes(Integer... sizes) {
-         return memorySizes(ImmutableSet.<Integer>copyOf(sizes));
+      /**
+       * @see AllowedArgumentsForCreateServer#getMemorySizesInMB()
+       */
+      public T memorySizes(Set<Integer> memorySizes) {
+         this.memorySizes = ImmutableSet.copyOf(checkNotNull(memorySizes, "memorySizesInMB"));
+         return self();
       }
 
-      public Builder memorySizes(Set<Integer> sizes) {
-         this.memorySizes = sizes;
-         return this;
+      public T memorySizes(Integer... in) {
+         return memorySizes(ImmutableSet.copyOf(in));
       }
 
-      public Builder cpuCores(Integer... cpuCores) {
-         this.cpuCores = ImmutableSet.<Integer>copyOf(cpuCores);
-         return this;
+      /**
+       * @see AllowedArgumentsForCreateServer#getCpuCoreOptions()
+       */
+      public T cpuCores(Set<Integer> cpuCores) {
+         this.cpuCores = ImmutableSet.copyOf(checkNotNull(cpuCores, "cpuCoreOptions"));
+         return self();
       }
 
-      public Builder cpuCores(Set<Integer> cpuCores) {
-         this.cpuCores = cpuCores;
-         return this;
+      public T cpuCores(Integer... in) {
+         return cpuCores(ImmutableSet.copyOf(in));
       }
 
-      public Builder templates(String... templates) {
-         return templates(ImmutableSet.<String>copyOf(templates));
+      /**
+       * @see AllowedArgumentsForCreateServer#getTemplateNames()
+       */
+      public T templates(Set<String> templates) {
+         this.templates = ImmutableSet.copyOf(checkNotNull(templates, "templateNames"));
+         return self();
       }
 
-      public Builder templates(Set<String> templates) {
-         this.templates = templates;
-         return this;
+      public T templates(String... in) {
+         return templates(ImmutableSet.copyOf(in));
       }
 
-      public Builder transfers(Integer... transfers) {
-         return transfers(ImmutableSet.<Integer>copyOf(transfers));
+      /**
+       * @see AllowedArgumentsForCreateServer#getTransfersInGB()
+       */
+      public T transfers(Set<Integer> transfers) {
+         this.transfers = ImmutableSet.copyOf(checkNotNull(transfers, "transfersInGB"));
+         return self();
       }
 
-      public Builder transfers(Set<Integer> transfers) {
-         this.transfers = transfers;
-         return this;
+      public T transfers(Integer... in) {
+         return transfers(ImmutableSet.copyOf(in));
       }
 
-      public Builder dataCenters(String... dataCenters) {
-         return dataCenters(ImmutableSet.<String>copyOf(dataCenters));
+      /**
+       * @see AllowedArgumentsForCreateServer#getDataCenters()
+       */
+      public T dataCenters(Set<String> dataCenters) {
+         this.dataCenters = ImmutableSet.copyOf(checkNotNull(dataCenters, "dataCenters"));
+         return self();
       }
 
-      public Builder dataCenters(Set<String> dataCenters) {
-         this.dataCenters = dataCenters;
-         return this;
+      public T dataCenters(String... in) {
+         return dataCenters(ImmutableSet.copyOf(in));
       }
 
       public AllowedArgumentsForCreateServer build() {
          return new AllowedArgumentsForCreateServer(diskSizes, memorySizes, cpuCores, templates, transfers, dataCenters);
       }
 
-      public Builder fromAllowedArguments(AllowedArgumentsForCreateServer in) {
-         return diskSizes(in.getDiskSizesInGB())
+      public T fromAllowedArgumentsForCreateServer(AllowedArgumentsForCreateServer in) {
+         return this
+               .diskSizes(in.getDiskSizesInGB())
                .memorySizes(in.getMemorySizesInMB())
                .cpuCores(in.getCpuCoreOptions())
                .templates(in.getTemplateNames())
@@ -115,34 +140,30 @@
       }
    }
 
-   @SerializedName("disksize")
-   private final Set<Integer> diskSizes;
-   @SerializedName("memorysize")
-   private final Set<Integer> memorySizes;
-   @SerializedName("cpucores")
-   private final Set<Integer> cpuCores;
-   @SerializedName("template")
-   private final Set<String> templates;
-   @SerializedName("transfer")
-   private final Set<Integer> transfers;
-   @SerializedName("datacenter")
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
+   }
+
+   private final Set<Integer> diskSizesInGB;
+   private final Set<Integer> memorySizesInMB;
+   private final Set<Integer> cpuCoreOptions;
+   private final Set<String> templateNames;
+   private final Set<Integer> transfersInGB;
    private final Set<String> dataCenters;
 
-   public AllowedArgumentsForCreateServer(Set<Integer> diskSizes, Set<Integer> memorySizes, Set<Integer> cpuCores,
-                                 Set<String> templates, Set<Integer> transfers, Set<String> dataCenters) {
-      checkNotNull(diskSizes, "diskSizes");
-      checkNotNull(memorySizes, "memorySizes");
-      checkNotNull(cpuCores, "cpuCores");
-      checkNotNull(templates, "templates");
-      checkNotNull(transfers, "transfers");
-      checkNotNull(dataCenters, "dataCenters");
-
-      this.diskSizes = diskSizes;
-      this.memorySizes = memorySizes;
-      this.cpuCores = cpuCores;
-      this.templates = templates;
-      this.transfers = transfers;
-      this.dataCenters = dataCenters;
+   @ConstructorProperties({
+         "disksize", "memorysize", "cpucores", "template", "transfer", "datacenter"
+   })
+   protected AllowedArgumentsForCreateServer(Set<Integer> diskSizesInGB, Set<Integer> memorySizesInMB, Set<Integer> cpuCoreOptions, Set<String> templateNames, Set<Integer> transfersInGB, Set<String> dataCenters) {
+      this.diskSizesInGB = ImmutableSet.copyOf(checkNotNull(diskSizesInGB, "diskSizesInGB"));
+      this.memorySizesInMB = ImmutableSet.copyOf(checkNotNull(memorySizesInMB, "memorySizesInMB"));
+      this.cpuCoreOptions = ImmutableSet.copyOf(checkNotNull(cpuCoreOptions, "cpuCoreOptions"));
+      this.templateNames = ImmutableSet.copyOf(checkNotNull(templateNames, "templateNames"));
+      this.transfersInGB = ImmutableSet.copyOf(checkNotNull(transfersInGB, "transfersInGB"));
+      this.dataCenters = ImmutableSet.copyOf(checkNotNull(dataCenters, "dataCenters"));
    }
 
    /**
@@ -150,82 +171,73 @@
     * @see org.jclouds.glesys.domain.OSTemplate#getMinDiskSize()
     */
    public Set<Integer> getDiskSizesInGB() {
-      return diskSizes;
+      return this.diskSizesInGB;
    }
 
    /**
     * @return a list of memory sizes, in MB, that can be used for creating servers on this platform
-    * @see  org.jclouds.glesys.domain.OSTemplate#getMinMemSize()
+    * @see org.jclouds.glesys.domain.OSTemplate#getMinMemSize()
     */
    public Set<Integer> getMemorySizesInMB() {
-      return memorySizes;
+      return this.memorySizesInMB;
    }
 
    /**
     * @return a list of which core counts can be used for creating servers on this platform
     */
    public Set<Integer> getCpuCoreOptions() {
-      return cpuCores;
+      return this.cpuCoreOptions;
    }
 
    /**
     * @return a list of template names available for creating servers on this platform
-    * @see org.jclouds.glesys.domain.OSTemplate#getName() 
+    * @see org.jclouds.glesys.domain.OSTemplate#getName()
     */
    public Set<String> getTemplateNames() {
-      return templates;
+      return this.templateNames;
    }
 
    /**
     * @return the list of transfer settings available for creating servers on this platform
     */
    public Set<Integer> getTransfersInGB() {
-      return transfers;
+      return this.transfersInGB;
    }
 
    /**
     * @return the list of datacenters available that support creating servers on this platform
     */
    public Set<String> getDataCenters() {
-      return dataCenters;
-   }
-
-   @Override
-   public boolean equals(Object object) {
-      if (this == object) {
-         return true;
-      }
-      if (object instanceof AllowedArgumentsForCreateServer) {
-         final AllowedArgumentsForCreateServer other = (AllowedArgumentsForCreateServer) object;
-         return Objects.equal(diskSizes, other.diskSizes)
-               && Objects.equal(memorySizes, other.memorySizes)
-               && Objects.equal(cpuCores, other.cpuCores)
-               && Objects.equal(templates, other.templates)
-               && Objects.equal(transfers, other.transfers)
-               && Objects.equal(dataCenters, other.dataCenters);
-      } else {
-         return false;
-      }
+      return this.dataCenters;
    }
 
    @Override
    public int hashCode() {
-      return Objects.hashCode(diskSizes, memorySizes, cpuCores, templates, transfers, dataCenters);
+      return Objects.hashCode(diskSizesInGB, memorySizesInMB, cpuCoreOptions, templateNames, transfersInGB, dataCenters);
+   }
+
+   @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      AllowedArgumentsForCreateServer that = AllowedArgumentsForCreateServer.class.cast(obj);
+      return Objects.equal(this.diskSizesInGB, that.diskSizesInGB)
+            && Objects.equal(this.memorySizesInMB, that.memorySizesInMB)
+            && Objects.equal(this.cpuCoreOptions, that.cpuCoreOptions)
+            && Objects.equal(this.templateNames, that.templateNames)
+            && Objects.equal(this.transfersInGB, that.transfersInGB)
+            && Objects.equal(this.dataCenters, that.dataCenters);
+   }
+
+   protected ToStringHelper string() {
+      return Objects.toStringHelper("").add("diskSizesInGB", diskSizesInGB).add("memorySizesInMB", memorySizesInMB)
+            .add("cpuCoreOptions", cpuCoreOptions).add("templateNames", templateNames)
+            .add("transfersInGB", transfersInGB).add("dataCenters", dataCenters);
    }
 
    @Override
    public String toString() {
-      checkNotNull(diskSizes, "diskSizes");
-      checkNotNull(memorySizes, "memorySizes");
-      checkNotNull(cpuCores, "cpuCores");
-      checkNotNull(templates, "templates");
-      checkNotNull(transfers, "transfers");
-      checkNotNull(dataCenters, "dataCenters");
-
-      Joiner commaJoiner = Joiner.on(", ");
-      return String.format("[disksize=[%s], memorysize=[%s], cpuCores=[%s], templates=[%s], transfers=[%s], datacenters=[%s]]",
-            commaJoiner.join(diskSizes), commaJoiner.join(memorySizes), commaJoiner.join(cpuCores), commaJoiner.join(templates),
-            commaJoiner.join(transfers), commaJoiner.join(dataCenters));
+      return string().toString();
    }
 
-}
+}
\ No newline at end of file
diff --git a/labs/glesys/src/main/java/org/jclouds/glesys/domain/Archive.java b/labs/glesys/src/main/java/org/jclouds/glesys/domain/Archive.java
index cfbb059..a3cf7ed 100644
--- a/labs/glesys/src/main/java/org/jclouds/glesys/domain/Archive.java
+++ b/labs/glesys/src/main/java/org/jclouds/glesys/domain/Archive.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -18,8 +18,12 @@
  */
 package org.jclouds.glesys.domain;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.beans.ConstructorProperties;
+
 import com.google.common.base.Objects;
-import com.google.gson.annotations.SerializedName;
+import com.google.common.base.Objects.ToStringHelper;
 
 /**
  * Information about an archive
@@ -27,102 +31,143 @@
  * @author Adam Lowe
  * @see <a href= "https://customer.glesys.com/api.php?a=doc#archive_list" />
  */
-public class Archive implements Comparable<Archive> {
-   public static Builder builder() {
-      return new Builder();
+public class Archive {
+
+   public static Builder<?> builder() {
+      return new ConcreteBuilder();
    }
 
-   public static class Builder {
+   public Builder<?> toBuilder() {
+      return new ConcreteBuilder().fromArchive(this);
+   }
+
+   public static abstract class Builder<T extends Builder<T>> {
+      protected abstract T self();
+
       protected String username;
       protected String totalSize;
       protected String freeSize;
       protected boolean locked;
 
-      public Builder username(String username) {
-         this.username = username;
-         return this;
+      /**
+       * @see Archive#getUsername()
+       */
+      public T username(String username) {
+         this.username = checkNotNull(username, "username");
+         return self();
       }
 
-      public Builder totalSize(String totalSize) {
-         this.totalSize = totalSize;
-         return this;
+      /**
+       * @see Archive#getTotalSize()
+       */
+      public T totalSize(String totalSize) {
+         this.totalSize = checkNotNull(totalSize, "totalSize");
+         return self();
       }
 
-      public Builder freeSize(String freeSize) {
-         this.freeSize = freeSize;
-         return this;
+      /**
+       * @see Archive#getFreeSize()
+       */
+      public T freeSize(String freeSize) {
+         this.freeSize = checkNotNull(freeSize, "freeSize");
+         return self();
       }
 
-      public Builder locked(boolean locked) {
+      /**
+       * @see Archive#isLocked()
+       */
+      public T locked(boolean locked) {
          this.locked = locked;
-         return this;
+         return self();
       }
 
       public Archive build() {
-         return new Archive(username, totalSize, freeSize, locked);
+         return new Archive(username, totalSize, freeSize, new GleSYSBoolean(locked));
       }
 
-      public Builder fromArchive(Archive in) {
-         return username(in.getUsername()).totalSize(in.getTotalSize()).freeSize(in.getFreeSize()).locked(in.isLocked());
+      public T fromArchive(Archive in) {
+         return this
+               .username(in.getUsername())
+               .totalSize(in.getTotalSize())
+               .freeSize(in.getFreeSize())
+               .locked(in.isLocked());
+      }
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
       }
    }
 
    private final String username;
-   @SerializedName("size_total")
    private final String totalSize;
-   @SerializedName("size_free")
    private final String freeSize;
    private final boolean locked;
 
-   /** @return the name (username) of the archive */
+   @ConstructorProperties({
+         "username", "sizetotal", "sizefree", "locked"
+   })
+   protected Archive(String username, String totalSize, String freeSize, GleSYSBoolean locked) {
+      this.username = checkNotNull(username, "username");
+      this.totalSize = checkNotNull(totalSize, "totalSize");
+      this.freeSize = checkNotNull(freeSize, "freeSize");
+      this.locked = checkNotNull(locked, "locked").getValue();
+   }
+
+   /**
+    * @return the name (username) of the archive
+    */
    public String getUsername() {
-      return username;
+      return this.username;
    }
 
-   /** @return the total size of the archive, ex. "10 GB" */
+   /**
+    * @return the total size of the archive, ex. "10 GB"
+    */
    public String getTotalSize() {
-      return totalSize;
+      return this.totalSize;
    }
 
-   /** @return the free space left of the archive */
+   /**
+    * @return the free space left of the archive
+    */
    public String getFreeSize() {
-      return freeSize;
+      return this.freeSize;
    }
 
-   /** @return true if the archive is locked */
+   /**
+    * @return true if the archive is locked
+    */
    public boolean isLocked() {
-      return locked;
-   }
-
-   public Archive(String username, String totalSize, String freeSize, boolean locked) {
-      this.username = username;
-      this.totalSize = totalSize;
-      this.freeSize = freeSize;
-      this.locked = locked;
+      return this.locked;
    }
 
    @Override
    public int hashCode() {
-      return Objects.hashCode(username);
+      return Objects.hashCode(username, totalSize, freeSize, locked);
    }
 
    @Override
-   public int compareTo(Archive other) {
-      return username.compareTo(other.getUsername());
-   }
-   
-   @Override
    public boolean equals(Object obj) {
-      if (this == obj) {
-         return true;
-      }
-      return obj instanceof Archive
-            && Objects.equal(username, ((Archive) obj).username);
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      Archive that = Archive.class.cast(obj);
+      return Objects.equal(this.username, that.username)
+            && Objects.equal(this.totalSize, that.totalSize)
+            && Objects.equal(this.freeSize, that.freeSize)
+            && Objects.equal(this.locked, that.locked);
+   }
+
+   protected ToStringHelper string() {
+      return Objects.toStringHelper("")
+            .add("username", username).add("totalSize", totalSize).add("freeSize", freeSize).add("locked", locked);
    }
 
    @Override
    public String toString() {
-      return String.format("[username=%s, totalSize=%s, freeSize=%s, locked=%b]", username, totalSize, freeSize, locked);
+      return string().toString();
    }
 
-}
+}
\ No newline at end of file
diff --git a/labs/glesys/src/main/java/org/jclouds/glesys/domain/ArchiveAllowedArguments.java b/labs/glesys/src/main/java/org/jclouds/glesys/domain/ArchiveAllowedArguments.java
index 8cdb47b..15a0150 100644
--- a/labs/glesys/src/main/java/org/jclouds/glesys/domain/ArchiveAllowedArguments.java
+++ b/labs/glesys/src/main/java/org/jclouds/glesys/domain/ArchiveAllowedArguments.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -18,14 +18,14 @@
  */
 package org.jclouds.glesys.domain;
 
-import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
 
-import java.util.Arrays;
+import java.beans.ConstructorProperties;
 import java.util.List;
 
-import com.google.common.base.Joiner;
 import com.google.common.base.Objects;
-import com.google.gson.annotations.SerializedName;
+import com.google.common.base.Objects.ToStringHelper;
+import com.google.common.collect.ImmutableList;
 
 /**
  * The allowed arguments for archive manipulation, such as archivesize
@@ -34,53 +34,62 @@
  * @see <a href= "https://customer.glesys.com/api.php?a=doc#archive_allowedarguments" />
  */
 public class ArchiveAllowedArguments {
-   public static Builder builder() {
-      return new Builder();
+
+   public static Builder<?> builder() {
+      return new ConcreteBuilder();
    }
 
-   public static class Builder {
-      private List<Integer> archiveSizes;
+   public Builder<?> toBuilder() {
+      return new ConcreteBuilder().fromArchiveAllowedArguments(this);
+   }
 
-      public Builder archiveSizes(List<Integer> archiveSizes) {
-         this.archiveSizes = archiveSizes;
-         return this;
+   public static abstract class Builder<T extends Builder<T>> {
+      protected abstract T self();
+
+      protected List<Integer> archiveSizes = ImmutableList.of();
+
+      /**
+       * @see ArchiveAllowedArguments#getArchiveSizes()
+       */
+      public T archiveSizes(List<Integer> archiveSizes) {
+         this.archiveSizes = ImmutableList.copyOf(checkNotNull(archiveSizes, "archiveSizes"));
+         return self();
       }
 
-      public Builder archiveSizes(Integer... archiveSizes) {
-         return archiveSizes(Arrays.asList(archiveSizes));
+      public T archiveSizes(Integer... in) {
+         return archiveSizes(ImmutableList.copyOf(in));
       }
 
       public ArchiveAllowedArguments build() {
          return new ArchiveAllowedArguments(archiveSizes);
       }
-      
-      public Builder fromArchiveAllowedArguments(ArchiveAllowedArguments in) {
-         return archiveSizes(in.getArchiveSizes());
+
+      public T fromArchiveAllowedArguments(ArchiveAllowedArguments in) {
+         return this.archiveSizes(in.getArchiveSizes());
       }
    }
 
-   @SerializedName("archivesize")
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
+   }
+
    private final List<Integer> archiveSizes;
 
-   public ArchiveAllowedArguments(List<Integer> archiveSizes) {
-      checkArgument(archiveSizes != null, "archiveSizes");
-      this.archiveSizes = archiveSizes;
+   @ConstructorProperties({
+         "archivesize"
+   })
+   protected ArchiveAllowedArguments(List<Integer> archiveSizes) {
+      this.archiveSizes = ImmutableList.copyOf(checkNotNull(archiveSizes, "archiveSizes"));
    }
 
    /**
     * @return the list of allowed archive sizes, in GB
     */
    public List<Integer> getArchiveSizes() {
-      return archiveSizes;
-   }
-
-   @Override
-   public boolean equals(Object object) {
-      if (this == object) {
-         return true;
-      }
-      return object instanceof ArchiveAllowedArguments
-         && Objects.equal(archiveSizes, ((ArchiveAllowedArguments) object).archiveSizes);
+      return this.archiveSizes;
    }
 
    @Override
@@ -89,10 +98,21 @@
    }
 
    @Override
-   public String toString() {
-      Joiner commaJoiner = Joiner.on(", ");
-      return String.format(
-            "[archiveSizes=[%s]]", commaJoiner.join(archiveSizes));
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      ArchiveAllowedArguments that = ArchiveAllowedArguments.class.cast(obj);
+      return Objects.equal(this.archiveSizes, that.archiveSizes);
    }
 
-}
+   protected ToStringHelper string() {
+      return Objects.toStringHelper("")
+            .add("archiveSizes", archiveSizes);
+   }
+
+   @Override
+   public String toString() {
+      return string().toString();
+   }
+
+}
\ No newline at end of file
diff --git a/labs/glesys/src/main/java/org/jclouds/glesys/domain/ArchiveDetails.java b/labs/glesys/src/main/java/org/jclouds/glesys/domain/ArchiveDetails.java
deleted file mode 100644
index c3530a7..0000000
--- a/labs/glesys/src/main/java/org/jclouds/glesys/domain/ArchiveDetails.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/**
- * Licensed to jclouds, Inc. (jclouds) under one or more
- * contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  jclouds licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.jclouds.glesys.domain;
-
-/**
- * Detailed information about an archive volume.
- * 
- * @author Adam Lowe
- * @see <a href= "https://customer.glesys.com/api.php?a=doc#archive_details" />
- */
-public class ArchiveDetails extends Archive {
-   public static Builder builder() {
-      return new Builder();
-   }
-
-   public static class Builder extends Archive.Builder {
-      public ArchiveDetails build() {
-         return new ArchiveDetails(username, totalSize, freeSize, locked);
-      }
-
-      public Builder fromArchiveDetails(ArchiveDetails in) {
-         return username(in.getUsername()).totalSize(in.getTotalSize()).freeSize(in.getFreeSize()).locked(in.isLocked());
-      }
-
-      @Override
-      public Builder username(String username) {
-         return Builder.class.cast(super.username(username));
-      }
-
-      @Override
-      public Builder totalSize(String size) {
-         return Builder.class.cast(super.totalSize(size));
-      }
-
-      @Override
-      public Builder freeSize(String size) {
-         return Builder.class.cast(super.freeSize(size));
-      }
-      
-      @Override
-      public Builder locked(boolean locked) {
-         return Builder.class.cast(super.locked(locked));
-      }
-   }
-   
-   public ArchiveDetails(String username, String totalSize, String freeSize, boolean locked) {
-      super(username, totalSize, freeSize, locked);
-   }
-}
diff --git a/labs/glesys/src/main/java/org/jclouds/glesys/domain/Console.java b/labs/glesys/src/main/java/org/jclouds/glesys/domain/Console.java
index ddcddf8..851abc3 100644
--- a/labs/glesys/src/main/java/org/jclouds/glesys/domain/Console.java
+++ b/labs/glesys/src/main/java/org/jclouds/glesys/domain/Console.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -18,7 +18,12 @@
  */
 package org.jclouds.glesys.domain;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.beans.ConstructorProperties;
+
 import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
 
 /**
  * Connection information to connect to a server with VNC.
@@ -27,44 +32,69 @@
  * @see <a href="https://customer.glesys.com/api.php?a=doc#server_console" />
  */
 public class Console {
-   public static Builder builder() {
-      return new Builder();
+
+   public static Builder<?> builder() {
+      return new ConcreteBuilder();
    }
 
-   public static class Builder {
-      private String host;
-      private int port;
-      private String protocol;
-      private String password;
+   public Builder<?> toBuilder() {
+      return new ConcreteBuilder().fromConsole(this);
+   }
 
-      public Builder host(String host) {
-         this.host = host;
-         return this;
+   public static abstract class Builder<T extends Builder<T>> {
+      protected abstract T self();
+
+      protected String host;
+      protected int port;
+      protected String protocol;
+      protected String password;
+
+      /**
+       * @see Console#getHost()
+       */
+      public T host(String host) {
+         this.host = checkNotNull(host, "host");
+         return self();
       }
 
-      public Builder port(int port) {
+      /**
+       * @see Console#getPort()
+       */
+      public T port(int port) {
          this.port = port;
-         return this;
+         return self();
       }
 
-      public Builder password(String password) {
-         this.password = password;
-         return this;
+      /**
+       * @see Console#getProtocol()
+       */
+      public T protocol(String protocol) {
+         this.protocol = checkNotNull(protocol, "protocol");
+         return self();
       }
 
-      public Builder protocol(String protocol) {
-         this.protocol = protocol;
-         return this;
+      /**
+       * @see Console#getPassword()
+       */
+      public T password(String password) {
+         this.password = checkNotNull(password, "password");
+         return self();
       }
 
       public Console build() {
          return new Console(host, port, protocol, password);
       }
-      
-      public Builder fromConsole(Console in) {
-         return host(in.getHost()).port(in.getPort()).password(in.getPassword()).protocol(in.getProtocol());
-      }
 
+      public T fromConsole(Console in) {
+         return this.host(in.getHost()).port(in.getPort()).protocol(in.getProtocol()).password(in.getPassword());
+      }
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
    }
 
    private final String host;
@@ -72,54 +102,42 @@
    private final String protocol;
    private final String password;
 
-   public Console(String host, int port, String protocol, String password) {
-      this.host = host;
+   @ConstructorProperties({
+         "host", "port", "protocol", "password"
+   })
+   protected Console(String host, int port, String protocol, String password) {
+      this.host = checkNotNull(host, "host");
       this.port = port;
-      this.protocol = protocol;
-      this.password = password;
+      this.protocol = checkNotNull(protocol, "protocol");
+      this.password = checkNotNull(password, "password");
    }
 
    /**
     * @return the host name to use to connect to the server
     */
    public String getHost() {
-      return host;
+      return this.host;
    }
 
    /**
     * @return the port to use to connect to the server
     */
    public int getPort() {
-      return port;
+      return this.port;
    }
 
    /**
     * @return the protocol to use to connect to the server
     */
    public String getProtocol() {
-      return protocol;
+      return this.protocol;
    }
-   
+
    /**
     * @return the password to use to connect to the server
     */
    public String getPassword() {
-      return password;
-   }
-
-   @Override
-   public boolean equals(Object object) {
-      if (this == object) {
-         return true;
-      }
-      if (object instanceof Console) {
-         final Console other = (Console) object;
-         return Objects.equal(host, other.host)
-               && Objects.equal(port, other.port)
-               && Objects.equal(protocol, other.protocol);
-      } else {
-         return false;
-      }
+      return this.password;
    }
 
    @Override
@@ -128,8 +146,23 @@
    }
 
    @Override
-   public String toString() {
-      return String.format("[host=%s, port=%s, protocol=%s, password=%s]", host, port, protocol, password);
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      Console that = Console.class.cast(obj);
+      return Objects.equal(this.host, that.host)
+            && Objects.equal(this.port, that.port)
+            && Objects.equal(this.protocol, that.protocol);
    }
 
-}
+   protected ToStringHelper string() {
+      return Objects.toStringHelper("").add("host", host).add("port", port).add("protocol", protocol)
+            .add("password", password);
+   }
+
+   @Override
+   public String toString() {
+      return string().toString();
+   }
+
+}
\ No newline at end of file
diff --git a/labs/glesys/src/main/java/org/jclouds/glesys/domain/Cost.java b/labs/glesys/src/main/java/org/jclouds/glesys/domain/Cost.java
index 8f5af3c..a868d4d 100644
--- a/labs/glesys/src/main/java/org/jclouds/glesys/domain/Cost.java
+++ b/labs/glesys/src/main/java/org/jclouds/glesys/domain/Cost.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -20,8 +20,10 @@
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import java.beans.ConstructorProperties;
+
 import com.google.common.base.Objects;
-import com.google.gson.annotations.SerializedName;
+import com.google.common.base.Objects.ToStringHelper;
 
 /**
  * The Cost class contains information about the cost of a server
@@ -30,84 +32,94 @@
  * @see ServerDetails
  */
 public class Cost {
-   public static Builder builder() {
-      return new Builder();
+
+   public static Builder<?> builder() {
+      return new ConcreteBuilder();
    }
 
-   public static class Builder {
-      private double amount;
-      private String currency;
-      private String timePeriod;
+   public Builder<?> toBuilder() {
+      return new ConcreteBuilder().fromCost(this);
+   }
 
-      public Builder amount(double amount) {
+   public static abstract class Builder<T extends Builder<T>> {
+      protected abstract T self();
+
+      protected double amount;
+      protected String currency;
+      protected String timePeriod;
+
+      /**
+       * @see Cost#getAmount()
+       */
+      public T amount(double amount) {
          this.amount = amount;
-         return this;
+         return self();
       }
 
-      public Builder currency(String currency) {
-         this.currency = currency;
-         return this;
+      /**
+       * @see Cost#getCurrency()
+       */
+      public T currency(String currency) {
+         this.currency = checkNotNull(currency, "currency");
+         return self();
       }
 
-      public Builder timePeriod(String timePeriod) {
-         this.timePeriod = timePeriod;
-         return this;
+      /**
+       * @see Cost#getTimePeriod()
+       */
+      public T timePeriod(String timePeriod) {
+         this.timePeriod = checkNotNull(timePeriod, "timePeriod");
+         return self();
       }
 
       public Cost build() {
          return new Cost(amount, currency, timePeriod);
       }
 
-      public Builder fromCost(Cost cost) {
-         return amount(cost.getAmount()).currency(cost.getCurrency()).timePeriod(cost.getTimePeriod());
+      public T fromCost(Cost in) {
+         return this.amount(in.getAmount()).currency(in.getCurrency()).timePeriod(in.getTimePeriod());
+      }
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
       }
    }
 
    private final double amount;
    private final String currency;
-   @SerializedName("timeperiod")
    private final String timePeriod;
 
-   public Cost(double amount, String currency, String timePeriod) {
+   @ConstructorProperties({
+         "amount", "currency", "timeperiod"
+   })
+   protected Cost(double amount, String currency, String timePeriod) {
       this.amount = amount;
       this.currency = checkNotNull(currency, "currency");
-      this.timePeriod = timePeriod;
+      this.timePeriod = checkNotNull(timePeriod, "timePeriod");
    }
 
    /**
     * @return the numeric cost in #currency / #timePeriod
     */
    public double getAmount() {
-      return amount;
+      return this.amount;
    }
 
    /**
     * @return the currency unit, e.g. "EUR" for Euro
     */
    public String getCurrency() {
-      return currency;
+      return this.currency;
    }
 
    /**
     * @return the time period for which this cost charged, e.g. "month"
     */
    public String getTimePeriod() {
-      return timePeriod;
-   }
-
-   @Override
-   public boolean equals(Object object) {
-      if (this == object) {
-         return true;
-      }
-      if (object instanceof Cost) {
-         Cost other = (Cost) object;
-         return Objects.equal(amount, other.amount)
-               && Objects.equal(currency, other.currency)
-               && Objects.equal(timePeriod, other.timePeriod);
-      } else {
-         return false;
-      }
+      return this.timePeriod;
    }
 
    @Override
@@ -116,8 +128,22 @@
    }
 
    @Override
-   public String toString() {
-      return String.format(
-            "[amount=%f, currency=%s, timePeriod=%s]", amount, currency, timePeriod);
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      Cost that = Cost.class.cast(obj);
+      return Objects.equal(this.amount, that.amount)
+            && Objects.equal(this.currency, that.currency)
+            && Objects.equal(this.timePeriod, that.timePeriod);
    }
-}
+
+   protected ToStringHelper string() {
+      return Objects.toStringHelper("")
+            .add("amount", amount).add("currency", currency).add("timePeriod", timePeriod);
+   }
+
+   @Override
+   public String toString() {
+      return string().toString();
+   }
+}
\ No newline at end of file
diff --git a/labs/glesys/src/main/java/org/jclouds/glesys/domain/Domain.java b/labs/glesys/src/main/java/org/jclouds/glesys/domain/Domain.java
index 3335c27..c98e521 100644
--- a/labs/glesys/src/main/java/org/jclouds/glesys/domain/Domain.java
+++ b/labs/glesys/src/main/java/org/jclouds/glesys/domain/Domain.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -18,10 +18,15 @@
  */
 package org.jclouds.glesys.domain;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.beans.ConstructorProperties;
 import java.util.Date;
 
+import org.jclouds.javax.annotation.Nullable;
+
 import com.google.common.base.Objects;
-import com.google.gson.annotations.SerializedName;
+import com.google.common.base.Objects.ToStringHelper;
 
 /**
  * Domain data for a Glesys account.
@@ -29,80 +34,253 @@
  * @author Adam Lowe
  * @see <a href= "https://customer.glesys.com/api.php?a=doc#domain_list" />
  */
-public class Domain implements Comparable<Domain> {
-   public static Builder builder() {
-      return new Builder();
+public class Domain {
+
+   public static Builder<?> builder() {
+      return new ConcreteBuilder();
    }
 
-   public static class Builder {
-      private String domainName;
-      private Date createTime;
-      private int recordCount;
-      private boolean useGlesysNameServer;
+   public Builder<?> toBuilder() {
+      return new ConcreteBuilder().fromDomain(this);
+   }
 
-      public Builder domainName(String domainName) {
-         this.domainName = domainName;
-         return this;
+   public static abstract class Builder<T extends Builder<T>> {
+      protected abstract T self();
+
+      protected String domainName;
+      protected Date createTime;
+      protected int recordCount;
+      protected boolean useGlesysNameServer;
+      protected String primaryNameServer;
+      protected String responsiblePerson;
+      protected int ttl;
+      protected int refresh;
+      protected int retry;
+      protected int expire;
+      protected int minimum;
+
+      /**
+       * @see Domain#getDomainName()
+       */
+      public T domainName(String domainName) {
+         this.domainName = checkNotNull(domainName, "domainName");
+         return self();
       }
 
-      public Builder createTime(Date createTime) {
+      /**
+       * @see Domain#getCreateTime()
+       */
+      public T createTime(Date createTime) {
          this.createTime = createTime;
-         return this;
+         return self();
       }
 
-      public Builder recordCount(int recordCount) {
+      /**
+       * @see Domain#getRecordCount()
+       */
+      public T recordCount(int recordCount) {
          this.recordCount = recordCount;
-         return this;
+         return self();
       }
 
-      public Builder useGlesysNameServer(boolean useGlesysNameServer) {
+      /**
+       * @see Domain#isUseGlesysNameServer()
+       */
+      public T useGlesysNameServer(boolean useGlesysNameServer) {
          this.useGlesysNameServer = useGlesysNameServer;
-         return this;
+         return self();
+      }
+
+      /**
+       * @see Domain#getPrimaryNameServer()
+       */
+      public T primaryNameServer(String primaryNameServer) {
+         this.primaryNameServer = primaryNameServer;
+         return self();
+      }
+
+      /**
+       * @see Domain#getResponsiblePerson()
+       */
+      public T responsiblePerson(String responsiblePerson) {
+         this.responsiblePerson = responsiblePerson;
+         return self();
+      }
+
+      /**
+       * @see Domain#getTtl()
+       */
+      public T ttl(int ttl) {
+         this.ttl = ttl;
+         return self();
+      }
+
+      /**
+       * @see Domain#getRefresh()
+       */
+      public T refresh(int refresh) {
+         this.refresh = refresh;
+         return self();
+      }
+
+      /**
+       * @see Domain#getRetry()
+       */
+      public T retry(int retry) {
+         this.retry = retry;
+         return self();
+      }
+
+      /**
+       * @see Domain#getExpire()
+       */
+      public T expire(int expire) {
+         this.expire = expire;
+         return self();
+      }
+
+      /**
+       * @see Domain#getMinimum()
+       */
+      public T minimum(int minimum) {
+         this.minimum = minimum;
+         return self();
       }
 
       public Domain build() {
-         return new Domain(domainName, createTime, recordCount, useGlesysNameServer);
+         return new Domain(domainName, createTime, recordCount, new GleSYSBoolean(useGlesysNameServer), primaryNameServer, responsiblePerson, ttl, refresh, retry, expire, minimum);
       }
 
-      public Builder fromDomain(Domain in) {
-         return new Builder().domainName(in.getDomainName()).createTime(in.getCreateTime()).recordCount(in.getRecordCount()).useGlesysNameServer(in.isGlesysNameServer());
+      public T fromDomain(Domain in) {
+         return this.domainName(in.getDomainName())
+               .createTime(in.getCreateTime())
+               .recordCount(in.getRecordCount())
+               .useGlesysNameServer(in.isUseGlesysNameServer())
+               .primaryNameServer(in.getPrimaryNameServer())
+               .responsiblePerson(in.getResponsiblePerson())
+               .ttl(in.getTtl())
+               .refresh(in.getRefresh())
+               .retry(in.getRetry())
+               .expire(in.getExpire());
       }
    }
 
-   @SerializedName("domainname")
-   private final String domainName;
-   @SerializedName("createtime")
-   private final Date createTime;
-   @SerializedName("recordcount")
-   private final int recordCount;
-   @SerializedName("usingglesysnameserver")
-   private final boolean useGlesysNameServer;
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
+   }
 
-   public Domain(String domainName, Date createTime, int recordCount, boolean useGlesysNameServer) {
-      this.domainName = domainName;
+   private final String domainName;
+   private final Date createTime;
+   private final int recordCount;
+   private final boolean useGlesysNameServer;
+   private final String primaryNameServer;
+   private final String responsiblePerson;
+   private final int ttl;
+   private final int refresh;
+   private final int retry;
+   private final int expire;
+   private final int minimum;
+
+   @ConstructorProperties({
+         "domainname", "createtime", "recordcount", "usingglesysnameserver", "primarynameserver", "responsibleperson",
+         "ttl", "refresh", "retry", "expire", "minimum"
+   })
+   protected Domain(String domainName, @Nullable Date createTime, int recordCount, GleSYSBoolean useGlesysNameServer,
+                    @Nullable String primaryNameServer, @Nullable String responsiblePerson,
+                    int ttl, int refresh, int retry, int expire, int minimum) {
+      this.domainName = checkNotNull(domainName, "domainName");
       this.createTime = createTime;
       this.recordCount = recordCount;
-      this.useGlesysNameServer = useGlesysNameServer;
+      this.useGlesysNameServer = checkNotNull(useGlesysNameServer, "useGlesysNameServer").getValue();
+      this.primaryNameServer = primaryNameServer;
+      this.responsiblePerson = responsiblePerson;
+      this.ttl = ttl;
+      this.refresh = refresh;
+      this.retry = retry;
+      this.expire = expire;
+      this.minimum = minimum;
    }
 
-   /** @return the domain name, ex. "jclouds.org" */
+   /**
+    * @return the domain name, ex. "jclouds.org"
+    */
    public String getDomainName() {
-      return domainName;
+      return this.domainName;
    }
 
-   /** @return the date the domain was registered with GleSYS */
+   /**
+    * @return the date the domain was registered with GleSYS
+    */
    public Date getCreateTime() {
-      return createTime;
+      return this.createTime;
    }
 
-   /** @return the number of DNS records for this domain */
+   /**
+    * @return the number of DNS records for this domain
+    */
    public int getRecordCount() {
-      return recordCount;
+      return this.recordCount;
    }
 
-   /** @return true if a GleSYS nameserver holds the records */
-   public boolean isGlesysNameServer() {
-      return useGlesysNameServer;
+   /**
+    * @return true if a GleSYS nameserver holds the records
+    */
+   public boolean isUseGlesysNameServer() {
+      return this.useGlesysNameServer;
+   }
+
+   @Nullable
+   public String getPrimaryNameServer() {
+      return primaryNameServer;
+   }
+
+   /**
+    * The E-mail address of the person responsible for this domain (reformatted with '.' at end).
+    */
+   @Nullable
+   public String getResponsiblePerson() {
+      return responsiblePerson;
+   }
+
+   /**
+    * TTL (time to live). The number of seconds a domain name is cached locally before expiration and return to authoritative nameServers for updates
+    */
+   public int getTtl() {
+      return ttl;
+   }
+
+   /**
+    * The number of seconds between update requests from secondary and slave name servers
+    */
+   public int getRefresh() {
+      return refresh;
+   }
+
+
+   /**
+    * The number of seconds the secondary/slave will wait before retrying when the last attempt failed
+    */
+   public int getRetry() {
+      return retry;
+   }
+
+   /**
+    * The number of seconds a master or slave will wait before considering the data stale if it cannot reach the primary name server
+    */
+   public int getExpire() {
+      return expire;
+   }
+
+   /**
+    * The minimum/default TTL if the domain does not specify ttl
+    *
+    * @see #getTtl()
+    */
+   public int getMinimum() {
+      return minimum;
    }
 
    @Override
@@ -111,25 +289,21 @@
    }
 
    @Override
-   public int compareTo(Domain other) {
-      return domainName.compareTo(other.getDomainName());
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      Domain that = Domain.class.cast(obj);
+      return Objects.equal(this.domainName, that.domainName);
    }
 
-   @Override
-   public boolean equals(Object object) {
-      if (this == object) {
-         return true;
-      }
-      if (object instanceof Domain) {
-         return Objects.equal(domainName, ((Domain) object).domainName);
-      } else {
-         return false;
-      }
+   protected ToStringHelper string() {
+      return Objects.toStringHelper("")
+            .add("domainName", domainName).add("createTime", createTime).add("recordCount", recordCount).add("useGlesysNameServer", useGlesysNameServer);
    }
 
    @Override
    public String toString() {
-      return String.format("[domainname=%s, createtime=%s, count=%d, useglesysnameserver=%b]", domainName, createTime, recordCount, useGlesysNameServer);
+      return string().toString();
    }
 
 }
\ No newline at end of file
diff --git a/labs/glesys/src/main/java/org/jclouds/glesys/domain/DomainRecord.java b/labs/glesys/src/main/java/org/jclouds/glesys/domain/DomainRecord.java
index dd3b92c..7e3a6e4 100644
--- a/labs/glesys/src/main/java/org/jclouds/glesys/domain/DomainRecord.java
+++ b/labs/glesys/src/main/java/org/jclouds/glesys/domain/DomainRecord.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -18,7 +18,14 @@
  */
 package org.jclouds.glesys.domain;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.beans.ConstructorProperties;
+
+import org.jclouds.javax.annotation.Nullable;
+
 import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
 
 /**
  * DNS record data.
@@ -26,55 +33,92 @@
  * @author Adam Lowe
  * @see <a href= "https://customer.glesys.com/api.php?a=doc#domain_list_records" />
  */
-public class DomainRecord implements Comparable<DomainRecord> {
-   public static Builder builder() {
-      return new Builder();
+public class DomainRecord {
+
+   public static Builder<?> builder() {
+      return new ConcreteBuilder();
    }
 
-   public static class Builder {
-      private String id;
-      private String domainname;
-      private String host;
-      private String type;
-      private String data;
-      private int ttl;
+   public Builder<?> toBuilder() {
+      return new ConcreteBuilder().fromDomainRecord(this);
+   }
 
-      public Builder id(String id) {
-         this.id = id;
-         return this;
+   public static abstract class Builder<T extends Builder<T>> {
+      protected abstract T self();
+
+      protected String id;
+      protected String domainname;
+      protected String host;
+      protected String type;
+      protected String data;
+      protected int ttl;
+
+      /**
+       * @see DomainRecord#getId()
+       */
+      public T id(String id) {
+         this.id = checkNotNull(id, "id");
+         return self();
       }
 
-      public Builder domainname(String domainname) {
-         this.domainname = domainname;
-         return this;
+      /**
+       * @see DomainRecord#getDomainname()
+       */
+      public T domainname(String domainname) {
+         this.domainname = checkNotNull(domainname, "domainname");
+         return self();
       }
 
-      public Builder host(String host) {
-         this.host = host;
-         return this;
+      /**
+       * @see DomainRecord#getHost()
+       */
+      public T host(String host) {
+         this.host = checkNotNull(host, "host");
+         return self();
       }
 
-      public Builder type(String type) {
-         this.type = type;
-         return this;
+      /**
+       * @see DomainRecord#getType()
+       */
+      public T type(String type) {
+         this.type = checkNotNull(type, "type");
+         return self();
       }
 
-      public Builder data(String data) {
-         this.data = data;
-         return this;
+      /**
+       * @see DomainRecord#getData()
+       */
+      public T data(String data) {
+         this.data = checkNotNull(data, "data");
+         return self();
       }
 
-      public Builder ttl(int ttl) {
+      /**
+       * @see DomainRecord#getTtl()
+       */
+      public T ttl(int ttl) {
          this.ttl = ttl;
-         return this;
+         return self();
       }
 
       public DomainRecord build() {
          return new DomainRecord(id, domainname, host, type, data, ttl);
       }
 
-      public Builder fromDomainRecord(DomainRecord in) {
-         return new Builder().id(in.getId()).domainname(in.getDomainName()).host(in.getHost()).type(in.getType()).data(in.getData()).ttl(in.getTtl());
+      public T fromDomainRecord(DomainRecord in) {
+         return this.id(in.getId())
+               .domainname(in.getDomainname())
+               .host(in.getHost())
+               .type(in.getType())
+               .data(in.getData())
+               .ttl(in.getTtl());
+      }
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
       }
    }
 
@@ -85,11 +129,14 @@
    private final String data;
    private final int ttl;
 
-   public DomainRecord(String id, String domainname, String host, String type, String data, int ttl) {
+   @ConstructorProperties({
+         "recordid", "domainname", "host", "type", "data", "ttl"
+   })
+   protected DomainRecord(@Nullable String id, String domainname, String host, String type, @Nullable String data, int ttl) {
       this.id = id;
-      this.domainname = domainname;
-      this.host = host;
-      this.type = type;
+      this.domainname = checkNotNull(domainname, "domainname");
+      this.host = checkNotNull(host, "host");
+      this.type = checkNotNull(type, "type");
       this.data = data;
       this.ttl = ttl;
    }
@@ -99,47 +146,43 @@
     * @see org.jclouds.glesys.features.DomainClient
     */
    public String getId() {
-      return id;
+      return this.id;
    }
 
    /**
     * @return the zone content of the record
     */
-   public String getDomainName() {
-      return domainname;
+   public String getDomainname() {
+      return this.domainname;
    }
 
    /**
     * @return the host content of the record
     */
    public String getHost() {
-      return host;
+      return this.host;
    }
 
    /**
     * @return the type of the record, ex. "A"
     */
    public String getType() {
-      return type;
+      return this.type;
    }
 
    /**
     * @return the data content of the record
     */
+   @Nullable
    public String getData() {
-      return data;
+      return this.data;
    }
 
    /**
     * @return the TTL/Time-to-live for the record
     */
    public int getTtl() {
-      return ttl;
-   }
-
-   @Override
-   public int compareTo(DomainRecord other) {
-      return id.compareTo(other.getId());
+      return this.ttl;
    }
 
    @Override
@@ -148,21 +191,22 @@
    }
 
    @Override
-   public boolean equals(Object object) {
-      if (this == object) {
-         return true;
-      }
-      if (object instanceof DomainRecord) {
-         DomainRecord other = (DomainRecord) object;
-         return Objects.equal(id, other.id);
-      } else {
-         return false;
-      }
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      DomainRecord that = DomainRecord.class.cast(obj);
+      return Objects.equal(this.id, that.id);
+   }
+
+   protected ToStringHelper string() {
+      return Objects.toStringHelper("")
+            .add("id", id).add("domainname", domainname).add("host", host).add("type", type).add("data", data)
+            .add("ttl", ttl);
    }
 
    @Override
    public String toString() {
-      return String.format("[id=%s, domainname=%s, host=%s, type=%s, data=%s, ttl=%d]", id, domainname, host, type, data, ttl);
+      return string().toString();
    }
 
 }
\ No newline at end of file
diff --git a/labs/glesys/src/main/java/org/jclouds/glesys/domain/EmailAccount.java b/labs/glesys/src/main/java/org/jclouds/glesys/domain/EmailAccount.java
index 54472a0..13105a9 100644
--- a/labs/glesys/src/main/java/org/jclouds/glesys/domain/EmailAccount.java
+++ b/labs/glesys/src/main/java/org/jclouds/glesys/domain/EmailAccount.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -18,205 +18,253 @@
  */
 package org.jclouds.glesys.domain;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.beans.ConstructorProperties;
 import java.util.Date;
 
+import org.jclouds.javax.annotation.Nullable;
+
 import com.google.common.base.Objects;
-import com.google.gson.annotations.SerializedName;
+import com.google.common.base.Objects.ToStringHelper;
 
 /**
  * Detailed information on an Email Account
- * 
+ *
  * @author Adam Lowe
  * @see <a href="https://customer.glesys.com/api.php?a=doc#email_list" />
  */
-public class EmailAccount implements Comparable<EmailAccount> {
-   public static Builder builder() {
-      return new Builder();
+public class EmailAccount {
+
+   public static Builder<?> builder() {
+      return new ConcreteBuilder();
    }
 
-   public static class Builder {
-      private String account;
-      private String quota;
-      private String usedQuota;
-      private int antispamLevel;
-      private boolean antiVirus;
-      private boolean autoRespond;
-      private String autoRespondMessage;
-      private boolean autoRespondSaveEmail;
-      private Date created;
-      private Date modified;
+   public Builder<?> toBuilder() {
+      return new ConcreteBuilder().fromEmailAccount(this);
+   }
 
-      public Builder account(String account) {
-         this.account = account;
-         return this;
-      }
-      
-      public Builder quota(String quota) {
-         this.quota = quota;
-         return this;
+   public static abstract class Builder<T extends Builder<T>> {
+      protected abstract T self();
+
+      protected String account;
+      protected EmailQuota quota;
+      protected int antispamLevel;
+      protected boolean antiVirus;
+      protected boolean autoRespond;
+      protected String autoRespondMessage;
+      protected boolean autoRespondSaveEmail;
+      protected Date created;
+      protected Date modified;
+
+      /**
+       * @see EmailAccount#getAccount()
+       */
+      public T account(String account) {
+         this.account = checkNotNull(account, "account");
+         return self();
       }
 
-      public Builder usedQuota(String usedQuota) {
-         this.usedQuota = usedQuota;
-         return this;
+      /**
+       * @see EmailAccount#getQuota()
+       */
+      public T quota(EmailQuota quota) {
+         this.quota = checkNotNull(quota, "quota");
+         return self();
       }
-      
-      public Builder antispamLevel(int antispamLevel) {
+
+      /**
+       * @see EmailAccount#getAntispamLevel()
+       */
+      public T antispamLevel(int antispamLevel) {
          this.antispamLevel = antispamLevel;
-         return this;
+         return self();
       }
 
-      public Builder antiVirus(boolean antiVirus) {
+      /**
+       * @see EmailAccount#isAntiVirus()
+       */
+      public T antiVirus(boolean antiVirus) {
          this.antiVirus = antiVirus;
-         return this;
+         return self();
       }
-      
-      public Builder autoRespond(boolean autoRespond) {
+
+      /**
+       * @see EmailAccount#isAutoRespond()
+       */
+      public T autoRespond(boolean autoRespond) {
          this.autoRespond = autoRespond;
-         return this;
-      }
-      
-      public Builder autoRespondMessage(String autoRespondMessage) {
-         this.autoRespondMessage = autoRespondMessage;
-         return this;
+         return self();
       }
 
-      public Builder autoRespondSaveEmail(boolean autoRespondSaveEmail) {
+      /**
+       * @see EmailAccount#getAutoRespondMessage()
+       */
+      public T autoRespondMessage(String autoRespondMessage) {
+         this.autoRespondMessage = checkNotNull(autoRespondMessage, "autoRespondMessage");
+         return self();
+      }
+
+      /**
+       * @see EmailAccount#isAutoRespondSaveEmail()
+       */
+      public T autoRespondSaveEmail(boolean autoRespondSaveEmail) {
          this.autoRespondSaveEmail = autoRespondSaveEmail;
-         return this;
+         return self();
       }
 
-      public Builder created(Date created) {
-         this.created = created;
-         return this;
+      /**
+       * @see EmailAccount#getCreated()
+       */
+      public T created(Date created) {
+         this.created = checkNotNull(created, "created");
+         return self();
       }
 
-      public Builder modified(Date modified) {
-         this.modified = modified;
-         return this;
+      /**
+       * @see EmailAccount#getModified()
+       */
+      public T modified(Date modified) {
+         this.modified = checkNotNull(modified, "modified");
+         return self();
       }
 
       public EmailAccount build() {
-         return new EmailAccount(account, quota, usedQuota, antispamLevel, antiVirus, autoRespond, autoRespondMessage,
-               autoRespondSaveEmail, created, modified);
+         return new EmailAccount(account, quota, antispamLevel, new GleSYSBoolean(antiVirus), new GleSYSBoolean(autoRespond), autoRespondMessage, new GleSYSBoolean(autoRespondSaveEmail), created, modified);
       }
 
-      public Builder fromEmail(EmailAccount in) {
-         return account(in.getAccount()).quota(in.getQuota()).usedQuota(in.getUsedQuota()).antispamLevel(in.getAntispamLevel()).
-               antiVirus(in.getAntiVirus()).autoRespond(in.getAutoRespond()).autoRespondMessage(in.getAutoRespondMessage()).
-               autoRespondSaveEmail(in.getAutoRespondSaveEmail()).created(in.getCreated()).modified(in.getModified());
+      public T fromEmailAccount(EmailAccount in) {
+         return this.account(in.getAccount())
+               .quota(in.getQuota())
+               .antispamLevel(in.getAntispamLevel())
+               .antiVirus(in.isAntiVirus())
+               .autoRespond(in.isAutoRespond())
+               .autoRespondMessage(in.getAutoRespondMessage())
+               .autoRespondSaveEmail(in.isAutoRespondSaveEmail())
+               .created(in.getCreated())
+               .modified(in.getModified());
       }
    }
 
-   @SerializedName("emailaccount")
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
+   }
+
    private final String account;
-   private final String quota;
-   @SerializedName("usedquota")
-   private final String usedQuota;
-   @SerializedName("antispamlevel")
+   private final EmailQuota quota;
    private final int antispamLevel;
-   @SerializedName("antivirus")
    private final boolean antiVirus;
-   @SerializedName("autorespond")
    private final boolean autoRespond;
-   @SerializedName("autorespondmessage")
    private final String autoRespondMessage;
-   @SerializedName("autorespondsaveemail")
    private final boolean autoRespondSaveEmail;
    private final Date created;
    private final Date modified;
 
-   public EmailAccount(String account, String quota, String usedQuota, int antispamLevel, boolean antiVirus, boolean autoRespond, String autoRespondMessage, boolean autoRespondSaveEmail, Date created, Date modified) {
-      this.account = account;
-      this.quota = quota;
-      this.usedQuota = usedQuota;
+   @ConstructorProperties({
+         "emailaccount", "quota", "antispamlevel", "antivirus", "autorespond", "autorespondmessage", "autorespondsaveemail", "created", "modified"
+   })
+   protected EmailAccount(String account, EmailQuota quota, int antispamLevel,
+                          GleSYSBoolean antiVirus, GleSYSBoolean autoRespond, @Nullable String autoRespondMessage,
+                          GleSYSBoolean autoRespondSaveEmail, Date created, @Nullable Date modified) {
+      this.account = checkNotNull(account, "account");
+      this.quota = checkNotNull(quota, "quota");
       this.antispamLevel = antispamLevel;
-      this.antiVirus = antiVirus;
-      this.autoRespond = autoRespond;
+      this.antiVirus = checkNotNull(antiVirus, "antiVirus").getValue();
+      this.autoRespond = checkNotNull(autoRespond, "autoRespond").getValue();
       this.autoRespondMessage = autoRespondMessage;
-      this.autoRespondSaveEmail = autoRespondSaveEmail;
-      this.created = created;
+      this.autoRespondSaveEmail = checkNotNull(autoRespondSaveEmail, "autoRespondSaveEmail").getValue();
+      this.created = checkNotNull(created, "created");
       this.modified = modified;
    }
 
-   /** @return the e-mail address for this e-mail account */
+   /**
+    * @return the e-mail address for this e-mail account
+    */
    public String getAccount() {
-      return account;
+      return this.account;
    }
 
-   /** @return the quota for this e-mail account */
-   public String getQuota() {
-      return quota;
+   /**
+    * @return the quota for this e-mail account
+    */
+   public EmailQuota getQuota() {
+      return this.quota;
    }
 
-   /** @return the amount of quota currently in use */
-   public String getUsedQuota() {
-      return usedQuota;
-   }
-
-   /** @return the antispam level of the e-mail account */
+   /**
+    * @return the antispam level of the e-mail account
+    */
    public int getAntispamLevel() {
-      return antispamLevel;
+      return this.antispamLevel;
    }
 
-   /** @return true if antivirus is enabled for this e-mail account */
-   public boolean getAntiVirus() {
-      return antiVirus;
+   /**
+    * @return true if antivirus is enabled for this e-mail account
+    */
+   public boolean isAntiVirus() {
+      return this.antiVirus;
    }
 
-   /** @return true if auto-respond is enabled for this e-mail account */
-   public boolean getAutoRespond() {
-      return autoRespond;
+   /**
+    * @return true if auto-respond is enabled for this e-mail account
+    */
+   public boolean isAutoRespond() {
+      return this.autoRespond;
    }
-
+   /**
+    * @return the auto-respond message for this e-mail account
+    */
+   @Nullable
    public String getAutoRespondMessage() {
-      return autoRespondMessage;
+      return this.autoRespondMessage;
    }
 
-   /** @return true if saving is enabled for auto-respond e-mails */
-   public boolean getAutoRespondSaveEmail() {
-      return autoRespondSaveEmail;
+   /**
+    * @return true if saving is enabled for auto-respond e-mails
+    */
+   public boolean isAutoRespondSaveEmail() {
+      return this.autoRespondSaveEmail;
    }
 
-   /** @return when this account was created */
+   /**
+    * @return when this account was created
+    */
    public Date getCreated() {
-      return created;
+      return this.created;
    }
 
-   /** @return when this account was last modified */
+   /**
+    * @return when this account was last modified
+    */
+   @Nullable
    public Date getModified() {
-      return modified;
-   }
-   
-   @Override
-   public int compareTo(EmailAccount other) {
-      return account.compareTo(other.getAccount());
-   }
-   
-   @Override
-   public boolean equals(Object object) {
-      if (this == object) {
-         return true;
-      }
-      if (object instanceof EmailAccount) {
-         EmailAccount other = (EmailAccount) object;
-         return Objects.equal(account, other.account);
-      } else {
-         return false;
-      }
+      return this.modified;
    }
 
    @Override
    public int hashCode() {
       return Objects.hashCode(account);
    }
-   
+
    @Override
-   public String toString() {
-      return String.format("account=%s, quota=%s, usedquota=%s, antispamLevel=%d, " +
-            "antiVirus=%b, autoRespond=%b, autoRespondMessage=%s, autoRespondSaveEmail=%b, " +
-            "created=%s, modified=%s", account, quota, usedQuota, antispamLevel, antiVirus, autoRespond, autoRespondMessage,
-            autoRespondSaveEmail, created.toString(), modified.toString());
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      EmailAccount that = EmailAccount.class.cast(obj);
+      return Objects.equal(this.account, that.account);
    }
 
-}
+   protected ToStringHelper string() {
+      return Objects.toStringHelper("")
+            .add("account", account).add("quota", quota).add("antispamLevel", antispamLevel).add("antiVirus", antiVirus).add("autoRespond", autoRespond).add("autoRespondMessage", autoRespondMessage).add("autoRespondSaveEmail", autoRespondSaveEmail).add("created", created).add("modified", modified);
+   }
+
+   @Override
+   public String toString() {
+      return string().toString();
+   }
+
+}
\ No newline at end of file
diff --git a/labs/glesys/src/main/java/org/jclouds/glesys/domain/EmailAlias.java b/labs/glesys/src/main/java/org/jclouds/glesys/domain/EmailAlias.java
new file mode 100644
index 0000000..8f20940
--- /dev/null
+++ b/labs/glesys/src/main/java/org/jclouds/glesys/domain/EmailAlias.java
@@ -0,0 +1,130 @@
+/*
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.glesys.domain;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.beans.ConstructorProperties;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
+
+/**
+ * Detailed information on an Email Account
+ *
+ * @author Adam Lowe
+ * @see <a href="https://customer.glesys.com/api.php?a=doc#email_list" />
+ */
+public class EmailAlias {
+
+   public static Builder<?> builder() {
+      return new ConcreteBuilder();
+   }
+
+   public Builder<?> toBuilder() {
+      return new ConcreteBuilder().fromEmailAlias(this);
+   }
+
+   public static abstract class Builder<T extends Builder<T>> {
+      protected abstract T self();
+
+      protected String alias;
+      protected String forwardTo;
+
+      /**
+       * @see org.jclouds.glesys.domain.EmailAlias#getAlias()
+       */
+      public T alias(String alias) {
+         this.alias = checkNotNull(alias, "alias");
+         return self();
+      }
+
+      /**
+       * @see EmailAlias#getForwardTo()
+       */
+      public T forwardTo(String forwardTo) {
+         this.forwardTo = checkNotNull(forwardTo, "forwardTo");
+         return self();
+      }
+
+      public EmailAlias build() {
+         return new EmailAlias(alias, forwardTo);
+      }
+
+      public T fromEmailAlias(EmailAlias in) {
+         return this.alias(in.getAlias()).forwardTo(in.getForwardTo());
+      }
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
+   }
+
+   private final String alias;
+   private final String forwardTo;
+
+   @ConstructorProperties({
+         "emailalias", "goto"
+   })
+   protected EmailAlias(String alias, String forwardTo) {
+      this.alias = checkNotNull(alias, "alias");
+      this.forwardTo = checkNotNull(forwardTo, "forwardTo");
+   }
+
+   /**
+    * @return the e-mail address being forwarded
+    */
+   public String getAlias() {
+      return this.alias;
+   }
+
+   /**
+    * @return the e-mail address this address forwards to
+    */
+   public String getForwardTo() {
+      return this.forwardTo;
+   }
+
+   @Override
+   public int hashCode() {
+      return Objects.hashCode(alias);
+   }
+
+   @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      EmailAlias that = EmailAlias.class.cast(obj);
+      return Objects.equal(this.alias, that.alias);
+   }
+
+   protected ToStringHelper string() {
+      return Objects.toStringHelper("")
+            .add("alias", alias).add("forwardTo", forwardTo);
+   }
+
+   @Override
+   public String toString() {
+      return string().toString();
+   }
+
+}
diff --git a/labs/glesys/src/main/java/org/jclouds/glesys/domain/EmailOverview.java b/labs/glesys/src/main/java/org/jclouds/glesys/domain/EmailOverview.java
index 785f386..d541977 100644
--- a/labs/glesys/src/main/java/org/jclouds/glesys/domain/EmailOverview.java
+++ b/labs/glesys/src/main/java/org/jclouds/glesys/domain/EmailOverview.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -18,69 +18,99 @@
  */
 package org.jclouds.glesys.domain;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.beans.ConstructorProperties;
 import java.util.Set;
 
 import com.google.common.annotations.Beta;
-import com.google.common.base.Joiner;
 import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
 import com.google.common.collect.ImmutableSet;
 
 /**
  * Structure containing all information about e-mail addresses for a GleSYS account
- * 
+ *
  * @author Adam Lowe
  * @see <a href="https://customer.glesys.com/api.php?a=doc#email_overview" />
  */
 //TODO: find a better name for this class
 @Beta
 public class EmailOverview {
-   public static Builder builder() {
-      return new Builder();
+
+   public static Builder<?> builder() {
+      return new ConcreteBuilder();
    }
 
-   public static class Builder {
-      private EmailOverviewSummary summary;
-      private Set<EmailOverviewDomain> domains;
+   public Builder<?> toBuilder() {
+      return new ConcreteBuilder().fromEmailOverview(this);
+   }
 
-      public Builder summary(EmailOverviewSummary summary) {
-         this.summary = summary;
-         return this;
+   public static abstract class Builder<T extends Builder<T>> {
+      protected abstract T self();
+
+      protected EmailOverviewSummary summary;
+      protected Set<EmailOverviewDomain> domains = ImmutableSet.of();
+
+      /**
+       * @see EmailOverview#getSummary()
+       */
+      public T summary(EmailOverviewSummary summary) {
+         this.summary = checkNotNull(summary, "summary");
+         return self();
       }
 
-      public Builder domains(Set<EmailOverviewDomain> domains) {
-         this.domains = domains;
-         return this;
+      /**
+       * @see EmailOverview#getDomains()
+       */
+      public T domains(Set<EmailOverviewDomain> domains) {
+         this.domains = ImmutableSet.copyOf(checkNotNull(domains, "domains"));
+         return self();
       }
 
-      public Builder domains(EmailOverviewDomain... domains) {
-         return domains(ImmutableSet.copyOf(domains));
+      public T domains(EmailOverviewDomain... in) {
+         return domains(ImmutableSet.copyOf(in));
       }
 
       public EmailOverview build() {
          return new EmailOverview(summary, domains);
       }
-      
-      public Builder fromEmailOverview(EmailOverview in) {
-         return summary(in.getSummary()).domains(in.getDomains());
+
+      public T fromEmailOverview(EmailOverview in) {
+         return this.summary(in.getSummary()).domains(in.getDomains());
       }
    }
 
-   private EmailOverviewSummary summary;
-   private Set<EmailOverviewDomain> domains;
-
-   public EmailOverview(EmailOverviewSummary summary,  Set<EmailOverviewDomain> domains) {
-      this.summary = summary; 
-      this.domains = domains;
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
    }
 
-   /** @return summary information about the account */
+   private final EmailOverviewSummary summary;
+   private final Set<EmailOverviewDomain> domains;
+
+   @ConstructorProperties({
+         "summary", "domains"
+   })
+   protected EmailOverview(EmailOverviewSummary summary, Set<EmailOverviewDomain> domains) {
+      this.summary = checkNotNull(summary, "summary");
+      this.domains = ImmutableSet.copyOf(checkNotNull(domains, "domains"));
+   }
+
+   /**
+    * @return summary information about the account
+    */
    public EmailOverviewSummary getSummary() {
-      return summary;
+      return this.summary;
    }
 
-   /** @return the set of detailed information about the e-mail addresses and aliases for each domain */
+   /**
+    * @return the set of detailed information about the e-mail addresses and aliases for each domain
+    */
    public Set<EmailOverviewDomain> getDomains() {
-      return domains == null ? ImmutableSet.<EmailOverviewDomain>of() : domains;
+      return this.domains;
    }
 
    @Override
@@ -89,23 +119,22 @@
    }
 
    @Override
-   public boolean equals(Object object) {
-      if (object == this) {
-         return true;
-      }
-      if (object instanceof EmailOverview) {
-         EmailOverview other = (EmailOverview) object;
-         return Objects.equal(summary, other.summary)
-               && Objects.equal(domains, other.domains);
-      } else {
-         return false;
-      }
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      EmailOverview that = EmailOverview.class.cast(obj);
+      return Objects.equal(this.summary, that.summary)
+            && Objects.equal(this.domains, that.domains);
+   }
+
+   protected ToStringHelper string() {
+      return Objects.toStringHelper("")
+            .add("summary", summary).add("domains", domains);
    }
 
    @Override
    public String toString() {
-      Joiner commaJoiner = Joiner.on(", ");
-      return String.format("summary=%s, domains=[%s]", summary, commaJoiner.join(getDomains()));
+      return string().toString();
    }
 
-}
+}
\ No newline at end of file
diff --git a/labs/glesys/src/main/java/org/jclouds/glesys/domain/EmailOverviewDomain.java b/labs/glesys/src/main/java/org/jclouds/glesys/domain/EmailOverviewDomain.java
index 3ae18e0..62dcfca 100644
--- a/labs/glesys/src/main/java/org/jclouds/glesys/domain/EmailOverviewDomain.java
+++ b/labs/glesys/src/main/java/org/jclouds/glesys/domain/EmailOverviewDomain.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -18,48 +18,75 @@
  */
 package org.jclouds.glesys.domain;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.beans.ConstructorProperties;
+
 import com.google.common.annotations.Beta;
 import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
 
 /**
  * Detailed information about e-mail settings for a single domain
- * 
+ *
  * @author Adam Lowe
  * @see <a href="https://customer.glesys.com/api.php?a=doc#email_overview" />
  */
-//TODO: find a better name for this class
 @Beta
 public class EmailOverviewDomain {
-   public static Builder builder() {
-      return new Builder();
+
+   public static Builder<?> builder() {
+      return new ConcreteBuilder();
    }
 
-   public static class Builder {
-      private String domain;
-      private int accounts;
-      private int aliases;
+   public Builder<?> toBuilder() {
+      return new ConcreteBuilder().fromEmailOverviewDomain(this);
+   }
 
-      public Builder domain(String domain) {
-         this.domain = domain;
-         return this;
+   public static abstract class Builder<T extends Builder<T>> {
+      protected abstract T self();
+
+      protected String domain;
+      protected int accounts;
+      protected int aliases;
+
+      /**
+       * @see EmailOverviewDomain#getDomain()
+       */
+      public T domain(String domain) {
+         this.domain = checkNotNull(domain, "domain");
+         return self();
       }
 
-      public Builder accounts(int accounts) {
+      /**
+       * @see EmailOverviewDomain#getAccounts()
+       */
+      public T accounts(int accounts) {
          this.accounts = accounts;
-         return this;
+         return self();
       }
-      
-      public Builder aliases(int aliases) {
+
+      /**
+       * @see EmailOverviewDomain#getAliases()
+       */
+      public T aliases(int aliases) {
          this.aliases = aliases;
-         return this;
+         return self();
       }
-      
+
       public EmailOverviewDomain build() {
          return new EmailOverviewDomain(domain, accounts, aliases);
       }
-      
-      public Builder fromEmailOverview(EmailOverviewDomain in) {
-         return domain(domain).accounts(in.getAccounts()).aliases(in.getAliases());
+
+      public T fromEmailOverviewDomain(EmailOverviewDomain in) {
+         return this.domain(in.getDomain()).accounts(in.getAccounts()).aliases(in.getAliases());
+      }
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
       }
    }
 
@@ -67,25 +94,28 @@
    private final int accounts;
    private final int aliases;
 
-   public EmailOverviewDomain(String domain, int accounts, int aliases) {
-      this.domain = domain;
+   @ConstructorProperties({
+         "domainname", "accounts", "aliases"
+   })
+   protected EmailOverviewDomain(String domain, int accounts, int aliases) {
+      this.domain = checkNotNull(domain, "domain");
       this.accounts = accounts;
       this.aliases = aliases;
    }
 
    /** @return the domain name */
    public String getDomain() {
-      return domain;
+      return this.domain;
    }
 
    /** @return the number of e-mail accounts in the domain */
    public int getAccounts() {
-      return accounts;
+      return this.accounts;
    }
 
    /** @return the number of e-mail aliases in the domain */
    public int getAliases() {
-      return aliases;
+      return this.aliases;
    }
 
    @Override
@@ -94,21 +124,20 @@
    }
 
    @Override
-   public boolean equals(Object object) {
-      if (object == this) {
-         return true;
-      }
-      if (object instanceof EmailOverviewDomain) {
-         EmailOverviewDomain other = (EmailOverviewDomain) object;
-         return Objects.equal(domain, other.domain);
-      } else {
-         return false;
-      }
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      EmailOverviewDomain that = EmailOverviewDomain.class.cast(obj);
+      return Objects.equal(this.domain, that.domain);
+   }
+
+   protected ToStringHelper string() {
+      return Objects.toStringHelper("").add("domain", domain).add("accounts", accounts).add("aliases", aliases);
    }
 
    @Override
    public String toString() {
-      return String.format("domain=%s, accounts=%d, aliases=%d", domain, accounts, aliases);
+      return string().toString();
    }
 
-}
+}
\ No newline at end of file
diff --git a/labs/glesys/src/main/java/org/jclouds/glesys/domain/EmailOverviewSummary.java b/labs/glesys/src/main/java/org/jclouds/glesys/domain/EmailOverviewSummary.java
index abc674d..0799722 100644
--- a/labs/glesys/src/main/java/org/jclouds/glesys/domain/EmailOverviewSummary.java
+++ b/labs/glesys/src/main/java/org/jclouds/glesys/domain/EmailOverviewSummary.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -18,90 +18,130 @@
  */
 package org.jclouds.glesys.domain;
 
+import java.beans.ConstructorProperties;
+
 import com.google.common.annotations.Beta;
 import com.google.common.base.Objects;
-import com.google.gson.annotations.SerializedName;
+import com.google.common.base.Objects.ToStringHelper;
 
 /**
  * Summary information of e-mail settings and limits for a GleSYS account
- * 
+ *
  * @author Adam Lowe
  * @see <a href="https://customer.glesys.com/api.php?a=doc#email_overview" />
  */
 //TODO: find a better name for this class
 @Beta
 public class EmailOverviewSummary {
-   public static Builder builder() {
-      return new Builder();
+
+   public static Builder<?> builder() {
+      return new ConcreteBuilder();
    }
 
-   public static class Builder {
-      private int accounts;
-      private int maxAccounts;
-      private int aliases;
-      private int maxAliases;
+   public Builder<?> toBuilder() {
+      return new ConcreteBuilder().fromEmailOverviewSummary(this);
+   }
 
-      public Builder accounts(int accounts) {
+   public static abstract class Builder<T extends Builder<T>> {
+      protected abstract T self();
+
+      protected int accounts;
+      protected int maxAccounts;
+      protected int aliases;
+      protected int maxAliases;
+
+      /**
+       * @see EmailOverviewSummary#getAccounts()
+       */
+      public T accounts(int accounts) {
          this.accounts = accounts;
-         return this;
+         return self();
       }
-      
-      public Builder maxAccounts(int maxAccounts) {
+
+      /**
+       * @see EmailOverviewSummary#getMaxAccounts()
+       */
+      public T maxAccounts(int maxAccounts) {
          this.maxAccounts = maxAccounts;
-         return this;
+         return self();
       }
-      
-      public Builder aliases(int aliases) {
+
+      /**
+       * @see EmailOverviewSummary#getAliases()
+       */
+      public T aliases(int aliases) {
          this.aliases = aliases;
-         return this;
+         return self();
       }
-      
-      public Builder maxAliases(int maxAliases) {
+
+      /**
+       * @see EmailOverviewSummary#getMaxAliases()
+       */
+      public T maxAliases(int maxAliases) {
          this.maxAliases = maxAliases;
-         return this;
+         return self();
       }
-      
+
       public EmailOverviewSummary build() {
          return new EmailOverviewSummary(accounts, maxAccounts, aliases, maxAliases);
       }
-      
-      public Builder fromEmailOverview(EmailOverviewSummary in) {
-         return accounts(in.getAccounts()).maxAccounts(in.getMaxAccounts()).aliases(in.getAliases()).maxAliases(in.getMaxAliases());
+
+      public T fromEmailOverviewSummary(EmailOverviewSummary in) {
+         return this.accounts(in.getAccounts())
+               .maxAccounts(in.getMaxAccounts())
+               .aliases(in.getAliases())
+               .maxAliases(in.getMaxAliases());
+      }
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
       }
    }
 
    private final int accounts;
-   @SerializedName("maxaccounts")
    private final int maxAccounts;
    private final int aliases;
-   @SerializedName("maxaliases")
    private final int maxAliases;
 
-   public EmailOverviewSummary(int accounts, int maxAccounts, int aliases, int maxAliases) {
+   @ConstructorProperties({
+         "accounts", "maxaccounts", "aliases", "maxaliases"
+   })
+   protected EmailOverviewSummary(int accounts, int maxAccounts, int aliases, int maxAliases) {
       this.accounts = accounts;
       this.maxAccounts = maxAccounts;
       this.aliases = aliases;
       this.maxAliases = maxAliases;
    }
 
-   /** @return the number of e-mail accounts */
+   /**
+    * @return the number of e-mail accounts
+    */
    public int getAccounts() {
-      return accounts;
+      return this.accounts;
    }
 
-   /** @return the maximum number of e-mail accounts */
+   /**
+    * @return the maximum number of e-mail accounts
+    */
    public int getMaxAccounts() {
-      return maxAccounts;
+      return this.maxAccounts;
    }
 
-   /** @return the number of e-mail aliases */
+   /**
+    * @return the number of e-mail aliases
+    */
    public int getAliases() {
-      return aliases;
+      return this.aliases;
    }
 
-   /** @return the maximum number of e-mail aliases */
+   /**
+    * @return the maximum number of e-mail aliases
+    */
    public int getMaxAliases() {
-      return maxAliases;
+      return this.maxAliases;
    }
 
    @Override
@@ -110,24 +150,24 @@
    }
 
    @Override
-   public boolean equals(Object object) {
-      if (object == this) {
-         return true;
-      }
-      if (object instanceof EmailOverviewSummary) {
-         EmailOverviewSummary other = (EmailOverviewSummary) object;
-         return Objects.equal(accounts, other.accounts)
-               && Objects.equal(maxAccounts, other.maxAccounts)
-               && Objects.equal(aliases, other.aliases)
-               && Objects.equal(maxAliases, other.maxAliases);
-      } else {
-         return false;
-      }
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      EmailOverviewSummary that = EmailOverviewSummary.class.cast(obj);
+      return Objects.equal(this.accounts, that.accounts)
+            && Objects.equal(this.maxAccounts, that.maxAccounts)
+            && Objects.equal(this.aliases, that.aliases)
+            && Objects.equal(this.maxAliases, that.maxAliases);
+   }
+
+   protected ToStringHelper string() {
+      return Objects.toStringHelper("")
+            .add("accounts", accounts).add("maxAccounts", maxAccounts).add("aliases", aliases).add("maxAliases", maxAliases);
    }
 
    @Override
    public String toString() {
-      return String.format("accounts=%d, maxAccounts=%d, aliases=%d, maxAliases=%d", accounts, maxAccounts, aliases, maxAliases);
+      return string().toString();
    }
 
-}
+}
\ No newline at end of file
diff --git a/labs/glesys/src/main/java/org/jclouds/glesys/domain/EmailQuota.java b/labs/glesys/src/main/java/org/jclouds/glesys/domain/EmailQuota.java
new file mode 100644
index 0000000..e587ff0
--- /dev/null
+++ b/labs/glesys/src/main/java/org/jclouds/glesys/domain/EmailQuota.java
@@ -0,0 +1,130 @@
+/*
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.glesys.domain;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.beans.ConstructorProperties;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
+
+/**
+ * Information on an Email Account Quota size
+ *
+ * @author Adam Lowe
+ * @see <a href="https://customer.glesys.com/api.php?a=doc#email_list" />
+ */
+public class EmailQuota {
+
+   public static Builder<?> builder() {
+      return new ConcreteBuilder();
+   }
+
+   public Builder<?> toBuilder() {
+      return new ConcreteBuilder().fromEmailAccount(this);
+   }
+
+   public static abstract class Builder<T extends Builder<T>> {
+      protected abstract T self();
+
+      protected int max;
+      protected String unit;
+
+      /**
+       * @see EmailQuota#getMax()
+       */
+      public T max(int max) {
+         this.max = max;
+         return self();
+      }
+
+      /**
+       * @see EmailQuota#getUnit()
+       */
+      public T unit(String unit) {
+         this.unit = checkNotNull(unit, "unit");
+         return self();
+      }
+
+      public EmailQuota build() {
+         return new EmailQuota(max, unit);
+      }
+
+      public T fromEmailAccount(EmailQuota in) {
+         return this.max(in.getMax()).unit(in.getUnit());
+      }
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
+   }
+
+   private final int max;
+   private final String unit;
+
+   @ConstructorProperties({
+         "max", "unit"
+   })
+   protected EmailQuota(int max, String unit) {
+      this.max = max;
+      this.unit = unit;
+   }
+
+   /**
+    * @return the maximum size of the mailbox (in units)
+    * @see #getUnit
+    */
+   public int getMax() {
+      return this.max;
+   }
+
+   /**
+    * @return the quota for this e-mail account
+    */
+   public String getUnit() {
+      return this.unit;
+   }
+
+   @Override
+   public int hashCode() {
+      return Objects.hashCode(max, unit);
+   }
+
+   @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      EmailQuota that = EmailQuota.class.cast(obj);
+      return Objects.equal(this.max, that.max) && Objects.equal(this.unit, that.unit);
+   }
+
+   protected ToStringHelper string() {
+      return Objects.toStringHelper("").add("max", max).add("unit", unit);
+   }
+
+   @Override
+   public String toString() {
+      return string().toString();
+   }
+
+}
diff --git a/labs/glesys/src/main/java/org/jclouds/glesys/domain/GleSYSBoolean.java b/labs/glesys/src/main/java/org/jclouds/glesys/domain/GleSYSBoolean.java
new file mode 100644
index 0000000..9dc1a51
--- /dev/null
+++ b/labs/glesys/src/main/java/org/jclouds/glesys/domain/GleSYSBoolean.java
@@ -0,0 +1,19 @@
+package org.jclouds.glesys.domain;
+
+/**
+ * Wrapping booleans for the time being (gson won't allow TypeAdapter&lt;Boolean&gt;)
+ */
+public class GleSYSBoolean {
+   public static final GleSYSBoolean TRUE = new GleSYSBoolean(true);
+   public static final GleSYSBoolean FALSE = new GleSYSBoolean(false);
+
+   private boolean value;
+
+   public GleSYSBoolean(boolean value) {
+      this.value = value;
+   }
+
+   public boolean getValue() {
+      return value;
+   }
+}
diff --git a/labs/glesys/src/main/java/org/jclouds/glesys/domain/Ip.java b/labs/glesys/src/main/java/org/jclouds/glesys/domain/Ip.java
index 2222ae8..0f53453 100644
--- a/labs/glesys/src/main/java/org/jclouds/glesys/domain/Ip.java
+++ b/labs/glesys/src/main/java/org/jclouds/glesys/domain/Ip.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -16,107 +16,145 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
 package org.jclouds.glesys.domain;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.beans.ConstructorProperties;
+
 import com.google.common.base.Objects;
-import com.google.gson.annotations.SerializedName;
+import com.google.common.base.Objects.ToStringHelper;
 
 /**
  * Represents an ip address used by a server.
  *
  * @author Adam Lowe
- * @see ServerCreated
+ * @see Server
  * @see ServerDetails
  */
 public class Ip {
 
-   public static Builder builder() {
-      return new Builder();
+   public static Builder<?> builder() {
+      return new ConcreteBuilder();
    }
 
-   public static class Builder {
+   public Builder<?> toBuilder() {
+      return new ConcreteBuilder().fromIp(this);
+   }
+
+   public static abstract class Builder<T extends Builder<T>> {
+      protected abstract T self();
+
       protected String ip;
       protected int version;
       protected double cost;
+      protected String currency;
 
-      protected Builder version(int version) {
-         this.version = version;
-         return this;
+      /**
+       * @see Ip#getIp()
+       */
+      public T ip(String ip) {
+         this.ip = checkNotNull(ip, "ip");
+         return self();
       }
 
-      public Builder version4() {
+      /**
+       * @see Ip#getVersion()
+       */
+      protected T version(int version) {
+         this.version = version;
+         return self();
+      }
+
+      /**
+       * @see Ip#getVersion()
+       */
+      public T version4() {
          return version(4);
       }
 
-      public Builder version6() {
+      /**
+       * @see Ip#getVersion()
+       */
+      public T version6() {
          return version(6);
       }
-      
-      public Builder ip(String ip) {
-         this.ip = ip;
-         return this;
+
+      /**
+       * @see Ip#getCost()
+       */
+      public T cost(double cost) {
+         this.cost = cost;
+         return self();
       }
 
-      public Builder cost(double cost) {
-         this.cost = cost;
-         return this;
+      /**
+       * @see Ip#getCurrency()
+       */
+      public T currency(String currency) {
+         this.currency = currency;
+         return self();
       }
 
       public Ip build() {
-         return new Ip(ip, version, cost);
+         return new Ip(ip, version, cost, currency);
       }
 
-      public Builder fromIpCreated(Ip from) {
-         return ip(from.getIp()).version(from.getVersion()).cost(from.getCost());
+      public T fromIp(Ip in) {
+         return this.ip(in.getIp()).version(in.getVersion()).cost(in.getCost());
       }
    }
-   
-   @SerializedName("ipaddress")
-   protected final String ip;
-   protected final int version;
-   protected final double cost;
 
-   public Ip(String ip, int version, double cost) {
-      this.ip = ip;
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
+   }
+
+   private final String ip;
+   private final int version;
+   private final double cost;
+   private final String currency;
+
+   @ConstructorProperties({
+         "ipaddress", "version", "cost", "currency"
+   })
+   protected Ip(String ip, int version, double cost, String currency) {
+      this.ip = checkNotNull(ip, "ip");
       this.version = version;
       this.cost = cost;
+      this.currency = checkNotNull(currency, "currency");
    }
 
    /**
     * @return the IP version, ex. 4
     */
-   public int getVersion() {
-      return version;
+   public String getIp() {
+      return this.ip;
    }
 
    /**
     * @return the ip address of the new server
     */
-   public String getIp() {
-      return ip;
+   public int getVersion() {
+      return this.version;
    }
 
    /**
     * @return the cost of the ip address allocated to the new server
+    * @see #getCurrency()
     */
    public double getCost() {
-      return cost;
+      return this.cost;
    }
 
-   @Override
-   public boolean equals(Object object) {
-      if (this == object) {
-         return true;
-      }
-      if (object instanceof Ip) {
-         final Ip other = (Ip) object;
-         return Objects.equal(ip, other.ip)
-               && Objects.equal(version, other.version)
-               && Objects.equal(cost, other.cost);
-      } else {
-         return false;
-      }
+   /**
+    * @return the currency of the cost
+    * @see #getCost() 
+    */
+   public String getCurrency() {
+      return currency;
    }
 
    @Override
@@ -125,8 +163,24 @@
    }
 
    @Override
-   public String toString() {
-      return String.format("[ip=%s, version=%d, cost=%f]",
-            ip, version, cost);
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      Ip that = Ip.class.cast(obj);
+      return Objects.equal(this.ip, that.ip)
+            && Objects.equal(this.version, that.version)
+            && Objects.equal(this.cost, that.cost)
+            && Objects.equal(this.currency, that.currency);
    }
-}
+
+   protected ToStringHelper string() {
+      return Objects.toStringHelper("")
+            .add("ip", ip).add("version", version).add("cost", cost).add("currency", currency);
+   }
+
+   @Override
+   public String toString() {
+      return string().toString();
+   }
+
+}
\ No newline at end of file
diff --git a/labs/glesys/src/main/java/org/jclouds/glesys/domain/IpDetails.java b/labs/glesys/src/main/java/org/jclouds/glesys/domain/IpDetails.java
index c16d18d..466c951 100644
--- a/labs/glesys/src/main/java/org/jclouds/glesys/domain/IpDetails.java
+++ b/labs/glesys/src/main/java/org/jclouds/glesys/domain/IpDetails.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -16,187 +16,311 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
 package org.jclouds.glesys.domain;
 
-import java.util.Arrays;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.beans.ConstructorProperties;
 import java.util.List;
 
 import org.jclouds.javax.annotation.Nullable;
 
-import com.google.gson.annotations.SerializedName;
+import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
+import com.google.common.collect.ImmutableList;
 
 /**
  * Represents detailed information about an IP address.
  */
 public class IpDetails {
 
-   public static Builder builder() {
-      return new Builder();
+   public static Builder<?> builder() {
+      return new ConcreteBuilder();
    }
 
-   public static class Builder {
+   public Builder<?> toBuilder() {
+      return new ConcreteBuilder().fromIpDetails(this);
+   }
+
+   public static abstract class Builder<T extends Builder<T>> {
+      protected abstract T self();
+
       protected String datacenter;
-      protected String ipversion;
+      protected int ipversion;
       protected String ptr;
       protected String platform;
       protected String address;
       protected String netmask;
       protected String broadcast;
       protected String gateway;
-      protected List<String> nameservers;
+      protected List<String> nameServers = ImmutableList.of();
+      protected String serverId;
+      protected Cost cost;
+      protected boolean reserved;
 
-      public Builder datacenter(String datacenter) {
-         this.datacenter = datacenter;
-         return this;
+      /**
+       * @see IpDetails#getDatacenter()
+       */
+      public T datacenter(String datacenter) {
+         this.datacenter = checkNotNull(datacenter, "datacenter");
+         return self();
       }
 
-      public Builder ipversion(String ipversion) {
+      protected T version(int ipversion) {
          this.ipversion = ipversion;
-         return this;
+         return self();
       }
 
-      public Builder ptr(String ptr) {
-         this.ptr = ptr;
-         return this;
+      /*
+      * @see IpDetails#getVersion()
+      */
+      public T version4() {
+         return version(4);
       }
 
-      public Builder platform(String platform) {
-         this.platform = platform;
-         return this;
+      /*
+      * @see IpDetails#getVersion()
+      */
+      public T version6() {
+         return version(6);
+      }
+
+      /**
+       * @see IpDetails#getPtr()
+       */
+      public T ptr(String ptr) {
+         this.ptr = checkNotNull(ptr, "ptr");
+         return self();
+      }
+
+      /**
+       * @see IpDetails#getPlatform()
+       */
+      public T platform(String platform) {
+         this.platform = checkNotNull(platform, "platform");
+         return self();
+      }
+
+      /**
+       * @see IpDetails#getAddress()
+       */
+      public T address(String address) {
+         this.address = address;
+         return self();
+      }
+
+      /**
+       * @see IpDetails#getNetmask()
+       */
+      public T netmask(String netmask) {
+         this.netmask = netmask;
+         return self();
+      }
+
+      /**
+       * @see IpDetails#getBroadcast()
+       */
+      public T broadcast(String broadcast) {
+         this.broadcast = broadcast;
+         return self();
+      }
+
+      /**
+       * @see IpDetails#getGateway()
+       */
+      public T gateway(String gateway) {
+         this.gateway = gateway;
+         return self();
+      }
+
+      /**
+       * @see IpDetails#getNameServers()
+       */
+      public T nameServers(List<String> nameservers) {
+         this.nameServers = ImmutableList.copyOf(checkNotNull(nameservers, "nameServers"));
+         return self();
+      }
+
+      public T nameServers(String... in) {
+         return nameServers(ImmutableList.copyOf(in));
+      }
+
+      /**
+       * @see IpDetails#getServerId()
+       */
+      public T serverId(String serverId) {
+         this.serverId = serverId;
+         return self();
+      }
+
+      /**
+       * @see IpDetails#getCost()
+       */
+      public T cost(Cost cost) {
+         this.cost = cost;
+         return self();
+      }
+
+      /**
+       * @see IpDetails#isReserved()
+       */
+      public T reserved(boolean reserved) {
+         this.reserved = reserved;
+         return self();
       }
 
       public IpDetails build() {
-         return new IpDetails(datacenter, ipversion, ptr, platform,
-                 address, netmask, broadcast, gateway, nameservers);
+         return new IpDetails(datacenter, ipversion, ptr, platform, address, netmask, broadcast, gateway, nameServers,
+               serverId, cost, new GleSYSBoolean(reserved));
       }
 
-      public Builder address(String address) {
-         this.address = address;
-         return this;
+      public T fromIpDetails(IpDetails in) {
+         return this.datacenter(in.getDatacenter())
+               .version(in.getVersion())
+               .ptr(in.getPtr())
+               .platform(in.getPlatform())
+               .address(in.getAddress())
+               .netmask(in.getNetmask())
+               .broadcast(in.getBroadcast())
+               .gateway(in.getGateway())
+               .nameServers(in.getNameServers())
+               .serverId(in.getServerId())
+               .cost(in.getCost())
+               .reserved(in.isReserved());
       }
+   }
 
-      public Builder netmask(String netmask) {
-         this.netmask = netmask;
-         return this;
-      }
-
-      public Builder broadcast(String broadcast) {
-         this.broadcast = broadcast;
-         return this;
-      }
-
-      public Builder gateway(String gateway) {
-         this.gateway = gateway;
-         return this;
-      }
-
-      public Builder nameServers(String... nameServers) {
-         this.nameservers = Arrays.asList(nameServers);
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
          return this;
       }
    }
 
-   protected String datacenter;
-   protected String ipversion;
-   @SerializedName("PTR")
-   protected String ptr;
-   protected String platform;
-   protected String address;
-   protected String netmask;
-   protected String broadcast;
-   protected String gateway;
-   protected List<String> nameservers;
+   private final String datacenter;
+   private final int version;
+   private final String ptr;
+   private final String platform;
+   private final String address;
+   private final String netmask;
+   private final String broadcast;
+   private final String gateway;
+   private final List<String> nameServers;
+   private final String serverId;
+   private final Cost cost;
+   private final boolean reserved;
 
-   public IpDetails(String datacenter, String ipversion, String ptr, String platform,
-                    @Nullable String address, @Nullable String netmask,
-                    @Nullable String broadcast, @Nullable String gateway,
-                    @Nullable List<String> nameservers) {
-      this.datacenter = datacenter;
-      this.ipversion = ipversion;
-      this.ptr = ptr;
-      this.platform = platform;
+   @ConstructorProperties({
+         "datacenter", "ipversion", "ptr", "platform", "ipaddress", "netmask", "broadcast", "gateway", "nameservers",
+         "serverid", "cost", "reserved"
+   })
+   protected IpDetails(String datacenter, int version, String ptr, String platform, String address,
+                       @Nullable String netmask, @Nullable String broadcast, @Nullable String gateway,
+                       List<String> nameServers, @Nullable String serverId, Cost cost, GleSYSBoolean reserved) {
+      this.datacenter = checkNotNull(datacenter, "datacenter");
+      this.version = checkNotNull(version, "version");
+      this.ptr = checkNotNull(ptr, "ptr");
+      this.platform = checkNotNull(platform, "platform");
       this.address = address;
       this.netmask = netmask;
       this.broadcast = broadcast;
       this.gateway = gateway;
-      this.nameservers = nameservers;
+      this.nameServers = ImmutableList.copyOf(nameServers);
+      this.serverId = serverId;
+      this.cost = checkNotNull(cost, "cost");
+      this.reserved = checkNotNull(reserved, "reserved").getValue();
    }
 
    public String getDatacenter() {
-      return datacenter;
+      return this.datacenter;
    }
 
-   public String getIpversion() {
-      return ipversion;
+   /**
+    * @return the IP version, ex. 4
+    */
+   public int getVersion() {
+      return this.version;
    }
 
    public String getPtr() {
-      return ptr;
+      return this.ptr;
    }
 
    public String getPlatform() {
-      return platform;
+      return this.platform;
    }
 
    public String getAddress() {
-      return address;
+      return this.address;
    }
 
+   @Nullable
    public String getNetmask() {
-      return netmask;
+      return this.netmask;
    }
 
+   @Nullable
    public String getBroadcast() {
-      return broadcast;
+      return this.broadcast;
    }
 
+   @Nullable
    public String getGateway() {
-      return gateway;
+      return this.gateway;
    }
 
    public List<String> getNameServers() {
-      return nameservers;
+      return this.nameServers;
    }
 
-   @Override
-   public boolean equals(Object o) {
-      if (this == o) return true;
-      if (o == null || getClass() != o.getClass()) return false;
+   @Nullable
+   public String getServerId() {
+      return serverId;
+   }
 
-      IpDetails ipDetails = (IpDetails) o;
+   public Cost getCost() {
+      return cost;
+   }
 
-      if (address != null ? !address.equals(ipDetails.address) : ipDetails.address != null) return false;
-      if (broadcast != null ? !broadcast.equals(ipDetails.broadcast) : ipDetails.broadcast != null) return false;
-      if (datacenter != null ? !datacenter.equals(ipDetails.datacenter) : ipDetails.datacenter != null) return false;
-      if (gateway != null ? !gateway.equals(ipDetails.gateway) : ipDetails.gateway != null) return false;
-      if (ipversion != null ? !ipversion.equals(ipDetails.ipversion) : ipDetails.ipversion != null) return false;
-      if (netmask != null ? !netmask.equals(ipDetails.netmask) : ipDetails.netmask != null) return false;
-      if (platform != null ? !platform.equals(ipDetails.platform) : ipDetails.platform != null) return false;
-      if (ptr != null ? !ptr.equals(ipDetails.ptr) : ipDetails.ptr != null) return false;
-      if (nameservers != null ? !nameservers.equals(ipDetails.nameservers) : ipDetails.nameservers != null)
-         return false;
-
-      return true;
+   public boolean isReserved() {
+      return reserved;
    }
 
    @Override
    public int hashCode() {
-      int result = datacenter != null ? datacenter.hashCode() : 0;
-      result = 31 * result + (ipversion != null ? ipversion.hashCode() : 0);
-      result = 31 * result + (ptr != null ? ptr.hashCode() : 0);
-      result = 31 * result + (platform != null ? platform.hashCode() : 0);
-      result = 31 * result + (address != null ? address.hashCode() : 0);
-      result = 31 * result + (netmask != null ? netmask.hashCode() : 0);
-      result = 31 * result + (broadcast != null ? broadcast.hashCode() : 0);
-      result = 31 * result + (gateway != null ? gateway.hashCode() : 0);
-      return result;
+      return Objects.hashCode(datacenter, version, ptr, platform, address, netmask, broadcast, gateway, nameServers,
+            serverId, cost, reserved);
+   }
+
+   @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      IpDetails that = IpDetails.class.cast(obj);
+      return Objects.equal(this.datacenter, that.datacenter)
+            && Objects.equal(this.version, that.version)
+            && Objects.equal(this.ptr, that.ptr)
+            && Objects.equal(this.platform, that.platform)
+            && Objects.equal(this.address, that.address)
+            && Objects.equal(this.netmask, that.netmask)
+            && Objects.equal(this.broadcast, that.broadcast)
+            && Objects.equal(this.gateway, that.gateway)
+            && Objects.equal(this.nameServers, that.nameServers)
+            && Objects.equal(this.serverId, that.serverId)
+            && Objects.equal(this.cost, that.cost)
+            && Objects.equal(this.reserved, that.reserved);
+   }
+
+   protected ToStringHelper string() {
+      return Objects.toStringHelper("")
+            .add("datacenter", datacenter).add("ipversion", version).add("ptr", ptr).add("platform", platform)
+            .add("address", address).add("netmask", netmask).add("broadcast", broadcast).add("gateway", gateway)
+            .add("nameServers", nameServers).add("serverId", serverId).add("cost", cost).add("reserved", reserved);
    }
 
    @Override
    public String toString() {
-      return String.format("IpDetails[datacenter=%s, ipversion=%s, platform=%s, PTR=%s, " +
-              "address=%s, netmask=%s, broadcast=%s, gateway=%s",
-              datacenter, ipversion, platform, ptr, address, netmask, broadcast, gateway);
+      return string().toString();
    }
-}
+
+}
\ No newline at end of file
diff --git a/labs/glesys/src/main/java/org/jclouds/glesys/domain/OSTemplate.java b/labs/glesys/src/main/java/org/jclouds/glesys/domain/OSTemplate.java
index e516280..b96b1c3 100644
--- a/labs/glesys/src/main/java/org/jclouds/glesys/domain/OSTemplate.java
+++ b/labs/glesys/src/main/java/org/jclouds/glesys/domain/OSTemplate.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -18,9 +18,12 @@
  */
 package org.jclouds.glesys.domain;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.beans.ConstructorProperties;
+
 import com.google.common.base.Objects;
-import com.google.common.collect.Ordering;
-import com.google.gson.annotations.SerializedName;
+import com.google.common.base.Objects.ToStringHelper;
 
 /**
  * Operating system template
@@ -28,73 +31,106 @@
  * @author Adam Lowe
  * @see <a href= "https://customer.glesys.com/api.php?a=doc#server_templates" />
  */
-public class OSTemplate implements Comparable<OSTemplate>{
+public class OSTemplate {
 
-   public static Builder builder() {
-      return new Builder();
+   public static Builder<?> builder() {
+      return new ConcreteBuilder();
    }
 
-   public static class Builder {
-      private String name;
-      private int minDiskSize;
-      private int minMemSize;
-      private String os;
-      private String platform;
+   public Builder<?> toBuilder() {
+      return new ConcreteBuilder().fromOSTemplate(this);
+   }
 
-      public Builder name(String name) {
-         this.name = name;
-         return this;
+   public static abstract class Builder<T extends Builder<T>> {
+      protected abstract T self();
+
+      protected String name;
+      protected int minDiskSize;
+      protected int minMemSize;
+      protected String os;
+      protected String platform;
+
+      /**
+       * @see OSTemplate#getName()
+       */
+      public T name(String name) {
+         this.name = checkNotNull(name, "name");
+         return self();
       }
 
-      public Builder minDiskSize(int minDiskSize) {
+      /**
+       * @see OSTemplate#getMinDiskSize()
+       */
+      public T minDiskSize(int minDiskSize) {
          this.minDiskSize = minDiskSize;
-         return this;
+         return self();
       }
 
-      public Builder minMemSize(int minMemSize) {
+      /**
+       * @see OSTemplate#getMinMemSize()
+       */
+      public T minMemSize(int minMemSize) {
          this.minMemSize = minMemSize;
-         return this;
+         return self();
       }
 
-      public Builder os(String os) {
-         this.os = os;
-         return this;
+      /**
+       * @see OSTemplate#getOs()
+       */
+      public T os(String os) {
+         this.os = checkNotNull(os, "os");
+         return self();
       }
 
-      public Builder platform(String platform) {
-         this.platform = platform;
-         return this;
+      /**
+       * @see OSTemplate#getPlatform()
+       */
+      public T platform(String platform) {
+         this.platform = checkNotNull(platform, "platform");
+         return self();
       }
 
       public OSTemplate build() {
          return new OSTemplate(name, minDiskSize, minMemSize, os, platform);
       }
 
-      public Builder fromTemplate(OSTemplate in) {
-         return name(in.getName()).minDiskSize(in.getMinDiskSize()).minMemSize(in.getMinMemSize()).os(in.getOs()).platform(in.getPlatform());
+      public T fromOSTemplate(OSTemplate in) {
+         return this.name(in.getName())
+               .minDiskSize(in.getMinDiskSize())
+               .minMemSize(in.getMinMemSize())
+               .os(in.getOs())
+               .platform(in.getPlatform());
       }
+   }
 
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
    }
 
    private final String name;
-   @SerializedName("minimumdisksize")
    private final int minDiskSize;
-   @SerializedName("minimummemorysize")
    private final int minMemSize;
-   @SerializedName("operatingsystem")
    private final String os;
    private final String platform;
 
-   public OSTemplate(String name, int minDiskSize, int minMemSize, String os, String platform) {
-      this.name = name;
+   @ConstructorProperties({
+         "name", "minimumdisksize", "minimummemorysize", "operatingsystem", "platform"
+   })
+   protected OSTemplate(String name, int minDiskSize, int minMemSize, String os, String platform) {
+      this.name = checkNotNull(name, "name");
       this.minDiskSize = minDiskSize;
       this.minMemSize = minMemSize;
-      this.os = os;
-      this.platform = platform;
+      this.os = checkNotNull(os, "os");
+      this.platform = checkNotNull(platform, "platform");
    }
 
+   /**
+    */
    public String getName() {
-      return name;
+      return this.name;
    }
 
    /**
@@ -102,7 +138,7 @@
     * @see org.jclouds.glesys.domain.AllowedArgumentsForCreateServer#getDiskSizesInGB()
     */
    public int getMinDiskSize() {
-      return minDiskSize;
+      return this.minDiskSize;
    }
 
    /**
@@ -110,35 +146,21 @@
     * @see org.jclouds.glesys.domain.AllowedArgumentsForCreateServer#getMemorySizesInMB()
     */
    public int getMinMemSize() {
-      return minMemSize;
+      return this.minMemSize;
    }
 
    /**
     * @return the name of the operating system type ex. "linux"
     */
    public String getOs() {
-      return os;
+      return this.os;
    }
 
    /**
     * @return the name of the platform this template is available in, ex. "Xen"
     */
    public String getPlatform() {
-      return platform;
-   }
-
-   @Override
-   public boolean equals(Object object) {
-      if (this == object) {
-         return true;
-      }
-      if (object instanceof OSTemplate) {
-         final OSTemplate other = (OSTemplate) object;
-         return Objects.equal(name, other.name)
-               && Objects.equal(platform, other.platform);
-      } else {
-         return false;
-      }
+      return this.platform;
    }
 
    @Override
@@ -147,13 +169,22 @@
    }
 
    @Override
-   public String toString() {
-      return String.format("[name=%s, min_disk_size=%d, min_mem_size=%d, os=%s, platform=%s]",
-            name, minDiskSize, minMemSize, os, platform);
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      OSTemplate that = OSTemplate.class.cast(obj);
+      return Objects.equal(this.name, that.name)
+            && Objects.equal(this.platform, that.platform);
+   }
+
+   protected ToStringHelper string() {
+      return Objects.toStringHelper("")
+            .add("name", name).add("minDiskSize", minDiskSize).add("minMemSize", minMemSize).add("os", os).add("platform", platform);
    }
 
    @Override
-   public int compareTo(OSTemplate arg0) {
-      return Ordering.usingToString().compare(this, arg0);
+   public String toString() {
+      return string().toString();
    }
-}
+
+}
\ No newline at end of file
diff --git a/labs/glesys/src/main/java/org/jclouds/glesys/domain/ResourceStatus.java b/labs/glesys/src/main/java/org/jclouds/glesys/domain/ResourceStatus.java
new file mode 100644
index 0000000..edf5a4a
--- /dev/null
+++ b/labs/glesys/src/main/java/org/jclouds/glesys/domain/ResourceStatus.java
@@ -0,0 +1,149 @@
+/*
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.glesys.domain;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.beans.ConstructorProperties;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
+
+/**
+ * Detailed information on usage
+ *
+ * @author Adam Lowe
+ * @see ServerStatus
+ */
+public class ResourceStatus {
+
+   public static Builder<?> builder() {
+      return new ConcreteBuilder();
+   }
+
+   public Builder<?> toBuilder() {
+      return new ConcreteBuilder().fromResourceUsage(this);
+   }
+
+   public static abstract class Builder<T extends Builder<T>> {
+      protected abstract T self();
+
+      protected double usage;
+      protected double max;
+      protected String unit;
+
+      /**
+       * @see ResourceStatus#getUsage()
+       */
+      public T usage(double usage) {
+         this.usage = usage;
+         return self();
+      }
+
+      /**
+       * @see ResourceStatus#getMax()
+       */
+      public T max(double max) {
+         this.max = max;
+         return self();
+      }
+
+      /**
+       * @see ResourceStatus#getUnit()
+       */
+      public T unit(String unit) {
+         this.unit = checkNotNull(unit, "unit");
+         return self();
+      }
+
+      public ResourceStatus build() {
+         return new ResourceStatus(usage, max, unit);
+      }
+
+      public T fromResourceUsage(ResourceStatus in) {
+         return this.usage(in.getUsage()).max(in.getMax()).unit(in.getUnit());
+      }
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
+   }
+
+   private final double usage;
+   private final double max;
+   private final String unit;
+
+   @ConstructorProperties({
+         "usage", "max", "unit"
+   })
+   protected ResourceStatus(double usage, double max, String unit) {
+      this.usage = usage;
+      this.max = max;
+      this.unit = checkNotNull(unit, "unit");
+   }
+
+   /**
+    * @return the usage in #unit
+    */
+   public double getUsage() {
+      return this.usage;
+   }
+
+   /**
+    * @return the max usage in #unit
+    */
+   public double getMax() {
+      return this.max;
+   }
+
+   /**
+    * @return the unit used
+    */
+   public String getUnit() {
+      return this.unit;
+   }
+
+   @Override
+   public int hashCode() {
+      return Objects.hashCode(usage, max, unit);
+   }
+
+   @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      ResourceStatus that = ResourceStatus.class.cast(obj);
+      return Objects.equal(this.usage, that.usage)
+            && Objects.equal(this.max, that.max)
+            && Objects.equal(this.unit, that.unit);
+   }
+
+   protected ToStringHelper string() {
+      return Objects.toStringHelper("").add("usage", usage).add("max", max).add("unit", unit);
+   }
+
+   @Override
+   public String toString() {
+      return string().toString();
+   }
+
+}
\ No newline at end of file
diff --git a/labs/glesys/src/main/java/org/jclouds/glesys/domain/ResourceUsage.java b/labs/glesys/src/main/java/org/jclouds/glesys/domain/ResourceUsage.java
index 6bc3ade..34c8dcd 100644
--- a/labs/glesys/src/main/java/org/jclouds/glesys/domain/ResourceUsage.java
+++ b/labs/glesys/src/main/java/org/jclouds/glesys/domain/ResourceUsage.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -18,103 +18,120 @@
  */
 package org.jclouds.glesys.domain;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.beans.ConstructorProperties;
+import java.util.Set;
+
 import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
+import com.google.common.collect.ImmutableSet;
 
 /**
  * Detailed information on usage
  *
  * @author Adam Lowe
- * @see ServerStatus
+ * @see ResourceUsageInfo
+ * @see ResourceUsageValue
  */
-
 public class ResourceUsage {
-   public static Builder builder() {
-      return new Builder();
+
+   public static Builder<?> builder() {
+      return new ConcreteBuilder();
    }
 
-   public static class Builder {
-      private double usage;
-      private double max;
-      private String unit;
+   public Builder<?> toBuilder() {
+      return new ConcreteBuilder().fromResourceUsages(this);
+   }
 
-      public Builder usage(double usage) {
-         this.usage = usage;
-         return this;
+   public static abstract class Builder<T extends Builder<T>> {
+      protected abstract T self();
+
+      protected ResourceUsageInfo info;
+      protected Set<ResourceUsageValue> values = ImmutableSet.of();
+
+      /**
+       * @see ResourceUsage#getInfo()
+       */
+      public T info(ResourceUsageInfo info) {
+         this.info = checkNotNull(info, "info");
+         return self();
       }
 
-      public Builder max(double max) {
-         this.max = max;
-         return this;
+      /**
+       * @see ResourceUsage#getValues()
+       */
+      public T values(Set<ResourceUsageValue> values) {
+         this.values = ImmutableSet.copyOf(checkNotNull(values, "values"));
+         return self();
       }
 
-      public Builder unit(String unit) {
-         this.unit = unit;
-         return this;
+      /**
+       * @see ResourceUsage#getValues()
+       */
+      public T values(ResourceUsageValue... in) {
+         return values(ImmutableSet.copyOf(in));
       }
 
       public ResourceUsage build() {
-         return new ResourceUsage(usage, max, unit);
+         return new ResourceUsage(info, values);
       }
 
-      public Builder fromCpu(ResourceUsage in) {
-         return usage(in.getUsage()).max(in.getMax()).unit(in.getUnit());
+      public T fromResourceUsages(ResourceUsage in) {
+         return this
+               .info(in.getInfo())
+               .values(in.getValues());
       }
    }
 
-   private final double usage;
-   private final double max;
-   private final String unit;
-
-   public ResourceUsage(double usage, double max, String unit) {
-      this.usage = usage;
-      this.max = max;
-      this.unit = unit;
-   }
-
-   /**
-    * @return the usage in #unit
-    */
-   public double getUsage() {
-      return usage;
-   }
-
-   /**
-    * @return the max usage in #unit
-    */
-   public double getMax() {
-      return max;
-   }
-
-   /**
-    * @return the unit used
-    */
-   public String getUnit() {
-      return unit;
-   }
-
-   @Override
-   public boolean equals(Object object) {
-      if (this == object) {
-         return true;
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
       }
-      if (object instanceof ResourceUsage) {
-         ResourceUsage other = (ResourceUsage) object;
-         return Objects.equal(usage, other.usage)
-               && Objects.equal(max, other.max)
-               && Objects.equal(unit, other.unit);
-      } else {
-         return false;
-      }
+   }
+
+   private final ResourceUsageInfo info;
+   private final Set<ResourceUsageValue> values;
+
+   @ConstructorProperties({
+         "info", "values"
+   })
+   protected ResourceUsage(ResourceUsageInfo info, Set<ResourceUsageValue> values) {
+      this.info = checkNotNull(info, "info");
+      this.values = ImmutableSet.copyOf(checkNotNull(values, "values"));
+   }
+
+   public ResourceUsageInfo getInfo() {
+      return this.info;
+   }
+
+   public Set<ResourceUsageValue> getValues() {
+      return this.values;
    }
 
    @Override
    public int hashCode() {
-      return Objects.hashCode(usage, max, unit);
+      return Objects.hashCode(info, values);
    }
-   
+
+   @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      ResourceUsage that = ResourceUsage.class.cast(obj);
+      return Objects.equal(this.info, that.info)
+            && Objects.equal(this.values, that.values);
+   }
+
+   protected ToStringHelper string() {
+      return Objects.toStringHelper("")
+            .add("info", info).add("values", values);
+   }
+
    @Override
    public String toString() {
-      return String.format("[usage=%f, max=%f, unit=%s]",
-            usage, max, unit);
+      return string().toString();
    }
-}
+
+}
\ No newline at end of file
diff --git a/labs/glesys/src/main/java/org/jclouds/glesys/domain/ResourceUsageInfo.java b/labs/glesys/src/main/java/org/jclouds/glesys/domain/ResourceUsageInfo.java
new file mode 100644
index 0000000..9a0dbc3
--- /dev/null
+++ b/labs/glesys/src/main/java/org/jclouds/glesys/domain/ResourceUsageInfo.java
@@ -0,0 +1,144 @@
+/*
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.glesys.domain;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.beans.ConstructorProperties;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
+
+/**
+ * Detailed information on usage
+ *
+ * @author Adam Lowe
+ * @see ServerStatus
+ */
+public class ResourceUsageInfo {
+
+   public static Builder<?> builder() {
+      return new ConcreteBuilder();
+   }
+
+   public Builder<?> toBuilder() {
+      return new ConcreteBuilder().fromResourceUsageInfo(this);
+   }
+
+   public static abstract class Builder<T extends Builder<T>>  {
+      protected abstract T self();
+
+      protected String resource;
+      protected String resolution;
+      protected String unit;
+
+      /**
+       * @see ResourceUsageInfo#getResource()
+       */
+      public T resource(String resource) {
+         this.resource = checkNotNull(resource, "resource");
+         return self();
+      }
+
+      /**
+       * @see ResourceUsageInfo#getResolution()
+       */
+      public T resolution(String resolution) {
+         this.resolution = checkNotNull(resolution, "resolution");
+         return self();
+      }
+
+      /**
+       * @see ResourceUsageInfo#getUnit()
+       */
+      public T unit(String unit) {
+         this.unit = checkNotNull(unit, "unit");
+         return self();
+      }
+
+      public ResourceUsageInfo build() {
+         return new ResourceUsageInfo(resource, resolution, unit);
+      }
+
+      public T fromResourceUsageInfo(ResourceUsageInfo in) {
+         return this
+               .resource(in.getResource())
+               .resolution(in.getResolution())
+               .unit(in.getUnit());
+      }
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
+   }
+
+   private final String resource;
+   private final String resolution;
+   private final String unit;
+
+   @ConstructorProperties({
+         "type", "resolution", "unit"
+   })
+   protected ResourceUsageInfo(String resource, String resolution, String unit) {
+      this.resource = checkNotNull(resource, "resource");
+      this.resolution = checkNotNull(resolution, "resolution");
+      this.unit = checkNotNull(unit, "unit");
+   }
+
+   public String getResource() {
+      return this.resource;
+   }
+
+   public String getResolution() {
+      return this.resolution;
+   }
+
+   public String getUnit() {
+      return this.unit;
+   }
+
+   @Override
+   public int hashCode() {
+      return Objects.hashCode(resource, resolution, unit);
+   }
+
+   @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      ResourceUsageInfo that = ResourceUsageInfo.class.cast(obj);
+      return Objects.equal(this.resource, that.resource)
+            && Objects.equal(this.resolution, that.resolution)
+            && Objects.equal(this.unit, that.unit);
+   }
+
+   protected ToStringHelper string() {
+      return Objects.toStringHelper("")
+            .add("resource", resource).add("resolution", resolution).add("unit", unit);
+   }
+
+   @Override
+   public String toString() {
+      return string().toString();
+   }
+
+}
\ No newline at end of file
diff --git a/labs/glesys/src/main/java/org/jclouds/glesys/domain/ResourceUsageValue.java b/labs/glesys/src/main/java/org/jclouds/glesys/domain/ResourceUsageValue.java
new file mode 100644
index 0000000..3b42ad7
--- /dev/null
+++ b/labs/glesys/src/main/java/org/jclouds/glesys/domain/ResourceUsageValue.java
@@ -0,0 +1,128 @@
+/*
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.glesys.domain;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.beans.ConstructorProperties;
+import java.util.Date;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
+
+/**
+ * Detailed information on usage
+ *
+ * @author Adam Lowe
+ * @see org.jclouds.glesys.domain.ServerStatus
+ */
+public class ResourceUsageValue {
+
+   public static Builder<?> builder() {
+      return new ConcreteBuilder();
+   }
+
+   public Builder<?> toBuilder() {
+      return new ConcreteBuilder().fromResourceUsage(this);
+   }
+
+   public static abstract class Builder<T extends Builder<T>> {
+      protected abstract T self();
+
+      protected double value;
+      protected Date timestamp;
+
+
+      /**
+       * @see ResourceUsageValue#getValue()
+       */
+      public T value(double value) {
+         this.value = value;
+         return self();
+      }
+
+      /**
+       * @see ResourceUsageValue#getTimestamp()
+       */
+      public T timestamp(Date timestamp) {
+         this.timestamp = checkNotNull(timestamp, "timestamp");
+         return self();
+      }
+
+      public ResourceUsageValue build() {
+         return new ResourceUsageValue(value, timestamp);
+      }
+
+      public T fromResourceUsage(ResourceUsageValue in) {
+         return this
+               .value(in.getValue())
+               .timestamp(in.getTimestamp());
+      }
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
+   }
+
+   private final double value;
+   private final Date timestamp;
+
+   @ConstructorProperties({
+         "value", "timestamp"
+   })
+   protected ResourceUsageValue(double value, Date timestamp) {
+      this.value = value;
+      this.timestamp = checkNotNull(timestamp, "timestamp");
+   }
+
+   public double getValue() {
+      return this.value;
+   }
+
+   public Date getTimestamp() {
+      return this.timestamp;
+   }
+
+   @Override
+   public int hashCode() {
+      return Objects.hashCode(value, timestamp);
+   }
+
+   @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      ResourceUsageValue that = ResourceUsageValue.class.cast(obj);
+      return Objects.equal(this.value, that.value)
+            && Objects.equal(this.timestamp, that.timestamp);
+   }
+
+   protected ToStringHelper string() {
+      return Objects.toStringHelper("").add("value", value).add("timestamp", timestamp);
+   }
+
+   @Override
+   public String toString() {
+      return string().toString();
+   }
+
+}
\ No newline at end of file
diff --git a/labs/glesys/src/main/java/org/jclouds/glesys/domain/Server.java b/labs/glesys/src/main/java/org/jclouds/glesys/domain/Server.java
index c4cfe3c..535275f 100644
--- a/labs/glesys/src/main/java/org/jclouds/glesys/domain/Server.java
+++ b/labs/glesys/src/main/java/org/jclouds/glesys/domain/Server.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -20,9 +20,11 @@
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import java.beans.ConstructorProperties;
+
 import com.google.common.base.CaseFormat;
 import com.google.common.base.Objects;
-import com.google.gson.annotations.SerializedName;
+import com.google.common.base.Objects.ToStringHelper;
 
 /**
  * Listing of a server.
@@ -30,8 +32,10 @@
  * @author Adrian Cole
  * @see <a href= "https://customer.glesys.com/api.php?a=doc#server_list" />
  */
-public class Server implements Comparable<Server> {
-   
+public class Server {
+
+   /**
+    */
    public static enum State {
 
       RUNNING, LOCKED, STOPPED, UNRECOGNIZED;
@@ -53,53 +57,80 @@
          }
       }
    }
-   
-   public static Builder builder() {
-      return new Builder();
+
+   public static Builder<?> builder() {
+      return new ConcreteBuilder();
    }
 
-   public static class Builder {
+   public Builder<?> toBuilder() {
+      return new ConcreteBuilder().fromServer(this);
+   }
+
+   public static abstract class Builder<T extends Builder<T>> {
+      protected abstract T self();
+
       protected String id;
       protected String hostname;
       protected String datacenter;
       protected String platform;
 
-      public Builder id(String id) {
-         this.id = id;
-         return this;
+      /**
+       * @see Server#getId()
+       */
+      public T id(String id) {
+         this.id = checkNotNull(id, "id");
+         return self();
       }
 
-      public Builder hostname(String hostname) {
-         this.hostname = hostname;
-         return this;
+      /**
+       * @see Server#getHostname()
+       */
+      public T hostname(String hostname) {
+         this.hostname = checkNotNull(hostname, "hostname");
+         return self();
       }
 
-      public Builder datacenter(String datacenter) {
-         this.datacenter = datacenter;
-         return this;
+      /**
+       * @see Server#getDatacenter()
+       */
+      public T datacenter(String datacenter) {
+         this.datacenter = checkNotNull(datacenter, "datacenter");
+         return self();
       }
 
-      public Builder platform(String platform) {
-         this.platform = platform;
-         return this;
+      /**
+       * @see Server#getPlatform()
+       */
+      public T platform(String platform) {
+         this.platform = checkNotNull(platform, "platform");
+         return self();
       }
 
       public Server build() {
          return new Server(id, hostname, datacenter, platform);
       }
 
-      public Builder fromServer(Server in) {
-         return datacenter(in.getDatacenter()).platform(in.getPlatform()).hostname(in.getHostname()).id(in.getId());
+      public T fromServer(Server in) {
+         return this.id(in.getId()).hostname(in.getHostname()).datacenter(in.getDatacenter()).platform(in.getPlatform());
       }
    }
 
-   @SerializedName("serverid")
-   protected final String id;
-   protected final String hostname;
-   protected final String datacenter;
-   protected final String platform;
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
+   }
 
-   public Server(String id, String hostname, String datacenter, String platform) {
+   private final String id;
+   private final String hostname;
+   private final String datacenter;
+   private final String platform;
+
+   @ConstructorProperties({
+         "serverid", "hostname", "datacenter", "platform"
+   })
+   protected Server(String id, String hostname, String datacenter, String platform) {
       this.id = checkNotNull(id, "id");
       this.hostname = checkNotNull(hostname, "hostname");
       this.datacenter = checkNotNull(datacenter, "datacenter");
@@ -110,45 +141,28 @@
     * @return the generated id of the server
     */
    public String getId() {
-      return id;
+      return this.id;
    }
 
    /**
     * @return the hostname of the server
     */
    public String getHostname() {
-      return hostname;
+      return this.hostname;
    }
 
    /**
     * @return platform running the server (ex. {@code OpenVZ})
     */
-   public String getPlatform() {
-      return platform;
+   public String getDatacenter() {
+      return this.datacenter;
    }
 
    /**
     * @return the datacenter the server exists in (ex. {@code Falkenberg})
     */
-   public String getDatacenter() {
-      return datacenter;
-   }
-   
-   @Override
-   public int compareTo(Server other) {
-      return id.compareTo(other.getId());
-   }
-
-   @Override
-   public boolean equals(Object object) {
-      if (this == object) {
-         return true;
-      }
-      if (object instanceof Server) {
-         return Objects.equal(id, ((Server) object).id);
-      } else {
-         return false;
-      }
+   public String getPlatform() {
+      return this.platform;
    }
 
    @Override
@@ -157,8 +171,21 @@
    }
 
    @Override
-   public String toString() {
-      return String.format("[id=%s, hostname=%s, datacenter=%s, platform=%s]", id, hostname, datacenter, platform);
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      Server that = Server.class.cast(obj);
+      return Objects.equal(this.id, that.id);
    }
 
-}
+   protected ToStringHelper string() {
+      return Objects.toStringHelper("").add("id", id).add("hostname", hostname).add("datacenter", datacenter)
+            .add("platform", platform);
+   }
+
+   @Override
+   public String toString() {
+      return string().toString();
+   }
+
+}
\ No newline at end of file
diff --git a/labs/glesys/src/main/java/org/jclouds/glesys/domain/ServerDetails.java b/labs/glesys/src/main/java/org/jclouds/glesys/domain/ServerDetails.java
index c18ce42..0e285f1 100644
--- a/labs/glesys/src/main/java/org/jclouds/glesys/domain/ServerDetails.java
+++ b/labs/glesys/src/main/java/org/jclouds/glesys/domain/ServerDetails.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -20,221 +20,239 @@
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import java.beans.ConstructorProperties;
 import java.util.Set;
 
+import org.jclouds.javax.annotation.Nullable;
+
+import com.google.common.base.Objects.ToStringHelper;
 import com.google.common.collect.ImmutableSet;
-import com.google.gson.annotations.SerializedName;
 
 /**
  * Detailed information about a server such as cpuCores, hardware configuration
  * (cpu, memory and disk), ip adresses, cost, transfer, os and more.
- * 
+ *
  * @author Adrian Cole
  * @see <a href= "https://customer.glesys.com/api.php?a=doc#server_details" />
  */
 public class ServerDetails extends Server {
-   public static Builder builder() {
-      return new Builder();
+
+   public static Builder<?> builder() {
+      return new ConcreteBuilder();
    }
 
-   public static class Builder extends Server.Builder {
-      private Server.State state;
-      private String description;
-      private String templateName;
-      private int cpuCores;
-      private int memorySizeMB;
-      private int diskSizeGB;
-      private int transferGB;
-      private Cost cost;
-      private Set<Ip> ips = ImmutableSet.of();
+   public Builder<?> toBuilder() {
+      return new ConcreteBuilder().fromServerDetails(this);
+   }
 
-      public Builder state(Server.State state) {
-         this.state = state;
-         return this;
+   public static abstract class Builder<T extends Builder<T>> extends Server.Builder<T> {
+      protected Server.State state;
+      protected String description;
+      protected String templateName;
+      protected int cpuCores;
+      protected int memorySizeMB;
+      protected int diskSizeGB;
+      protected int transferGB;
+      protected Cost cost;
+      protected Set<Ip> ips = ImmutableSet.of();
+
+      /**
+       * @see ServerDetails#getState()
+       */
+      public T state(Server.State state) {
+         this.state = checkNotNull(state, "state");
+         return self();
       }
 
-      public Builder description(String description) {
-         this.description = description;
-         return this;
+      /**
+       * @see ServerDetails#getDescription()
+       */
+      public T description(String description) {
+         this.description = checkNotNull(description, "description");
+         return self();
       }
 
-      public Builder templateName(String templateName) {
-         this.templateName = templateName;
-         return this;
+      /**
+       * @see ServerDetails#getTemplateName()
+       */
+      public T templateName(String templateName) {
+         this.templateName = checkNotNull(templateName, "templateName");
+         return self();
       }
 
-      public Builder cpuCores(int cpuCores) {
+      /**
+       * @see ServerDetails#getCpuCores()
+       */
+      public T cpuCores(int cpuCores) {
          this.cpuCores = cpuCores;
-         return this;
+         return self();
       }
 
-      public Builder memorySizeMB(int memorySizeMB) {
+      /**
+       * @see ServerDetails#getMemorySizeMB()
+       */
+      public T memorySizeMB(int memorySizeMB) {
          this.memorySizeMB = memorySizeMB;
-         return this;
+         return self();
       }
 
-      public Builder diskSizeGB(int diskSizeGB) {
+      /**
+       * @see ServerDetails#getDiskSizeGB()
+       */
+      public T diskSizeGB(int diskSizeGB) {
          this.diskSizeGB = diskSizeGB;
-         return this;
+         return self();
       }
 
-      public Builder transferGB(int transferGB) {
+      /**
+       * @see ServerDetails#getTransferGB()
+       */
+      public T transferGB(int transferGB) {
          this.transferGB = transferGB;
-         return this;
+         return self();
       }
 
-      public Builder cost(Cost cost) {
-         this.cost = cost;
-         return this;
+      /**
+       * @see ServerDetails#getCost()
+       */
+      public T cost(Cost cost) {
+         this.cost = checkNotNull(cost, "cost");
+         return self();
       }
 
-      public Builder ips(Ip... ips) {
-         return ips(ImmutableSet.copyOf(ips));
+      /**
+       * @see ServerDetails#getIps()
+       */
+      public T ips(Set<Ip> ips) {
+         this.ips = ImmutableSet.copyOf(checkNotNull(ips, "ips"));
+         return self();
       }
 
-      public Builder ips(Iterable<Ip> ips) {
-         this.ips = ImmutableSet.copyOf(ips);
-         return this;
+      public T ips(Ip... in) {
+         return ips(ImmutableSet.copyOf(in));
       }
 
       public ServerDetails build() {
-         return new ServerDetails(id, hostname, datacenter, platform, state, templateName, description, cpuCores,
-               memorySizeMB, diskSizeGB, transferGB, cost, ips);
+         return new ServerDetails(id, hostname, datacenter, platform, state, description, templateName, cpuCores, memorySizeMB, diskSizeGB, transferGB, cost, ips);
       }
 
-      public Builder fromServerDetails(ServerDetails in) {
-         return fromServer(in).templateName(in.getTemplateName()).state(in.getState()).memorySizeMB(in.getMemorySizeMB())
-               .diskSizeGB(in.getDiskSizeGB()).cpuCores(in.getCpuCores()).cost(in.getCost())
-               .transferGB(in.getTransferGB()).description(in.getDescription()).ips(in.getIps());
+      public T fromServerDetails(ServerDetails in) {
+         return super.fromServer(in)
+               .state(in.getState())
+               .description(in.getDescription())
+               .templateName(in.getTemplateName())
+               .cpuCores(in.getCpuCores())
+               .memorySizeMB(in.getMemorySizeMB())
+               .diskSizeGB(in.getDiskSizeGB())
+               .transferGB(in.getTransferGB())
+               .cost(in.getCost())
+               .ips(in.getIps());
       }
+   }
 
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
       @Override
-      public Builder id(String id) {
-         return Builder.class.cast(super.id(id));
-      }
-
-      @Override
-      public Builder hostname(String hostname) {
-         return Builder.class.cast(super.hostname(hostname));
-      }
-
-      @Override
-      public Builder datacenter(String datacenter) {
-         return Builder.class.cast(super.datacenter(datacenter));
-      }
-
-      @Override
-      public Builder platform(String platform) {
-         return Builder.class.cast(super.platform(platform));
-      }
-
-      @Override
-      public Builder fromServer(Server in) {
-         return Builder.class.cast(super.fromServer(in));
+      protected ConcreteBuilder self() {
+         return this;
       }
    }
 
    private final Server.State state;
    private final String description;
-   @SerializedName("templatename")
    private final String templateName;
-   @SerializedName("cpucores")
    private final int cpuCores;
-   @SerializedName("memorysize")
    private final int memorySizeMB;
-   @SerializedName("disksize")
    private final int diskSizeGB;
-   @SerializedName("transfer")
    private final int transferGB;
    private final Cost cost;
-   @SerializedName("iplist")
    private final Set<Ip> ips;
 
-   public ServerDetails(String id, String hostname, String datacenter, String platform, Server.State state,
-         String templateName, String description, int cpuCores, int memorySizeMB, int diskSizeGB, int transferGB,
-         Cost cost, Set<Ip> ips) {
+   @ConstructorProperties({
+         "serverid", "hostname", "datacenter", "platform", "state", "description", "templatename", "cpucores",
+         "memorysize", "disksize", "transfer", "cost", "iplist"
+   })
+   protected ServerDetails(String id, String hostname, String datacenter, String platform, @Nullable Server.State state,
+                           @Nullable String description, String templateName, int cpuCores, int memorySizeMB,
+                           int diskSizeGB, int transferGB, Cost cost, @Nullable Set<Ip> ips) {
       super(id, hostname, datacenter, platform);
       this.state = state;
-      this.templateName = checkNotNull(templateName, "template");
       this.description = description;
+      this.templateName = checkNotNull(templateName, "templateName");
       this.cpuCores = cpuCores;
       this.memorySizeMB = memorySizeMB;
       this.diskSizeGB = diskSizeGB;
       this.transferGB = transferGB;
       this.cost = checkNotNull(cost, "cost");
-      this.ips = ImmutableSet.<Ip> copyOf(ips);
+      this.ips = ips == null ? ImmutableSet.<Ip>of() : ImmutableSet.copyOf(checkNotNull(ips, "ips"));
    }
 
    /**
     * @return the state of the server (e.g. "running")
     */
    public Server.State getState() {
-      return state;
+      return this.state;
    }
 
    /**
     * @return the user-specified description of the server
     */
    public String getDescription() {
-      return description;
-   }
-
-   /**
-    * @return number of cores on the server
-    */
-   public int getCpuCores() {
-      return cpuCores;
-   }
-
-   /**
-    * @return the disk of the server in GB
-    */
-   public int getDiskSizeGB() {
-      return diskSizeGB;
-   }
-
-   /**
-    * @return the memory of the server in MB
-    */
-   public int getMemorySizeMB() {
-      return memorySizeMB;
-   }
-
-   /**
-    * @return the transfer of the server
-    */
-   public int getTransferGB() {
-      return transferGB;
-   }
-
-   /**
-    * @return details of the cost of the server
-    */
-   public Cost getCost() {
-      return cost;
-   }
-
-   /**
-    * @return the ip addresses assigned to the server
-    */
-   public Set<Ip> getIps() {
-      return ips;
+      return this.description;
    }
 
    /**
     * @return the name of the template used to create the server
     */
    public String getTemplateName() {
-      return templateName;
+      return this.templateName;
    }
 
-   @Override
-   public String toString() {
-      return String
-            .format(
-                  "[id=%s, hostname=%s, datacenter=%s, platform=%s, templateName=%s, state=%s, description=%s, cpuCores=%d, memorySizeMB=%d, diskSizeGB=%d, transferGB=%d, cost=%s, ips=%s]",
-                  id, hostname, datacenter, platform, templateName, state, description, cpuCores, memorySizeMB,
-                  diskSizeGB, transferGB, cost, ips);
+   /**
+    * @return number of cores on the server
+    */
+   public int getCpuCores() {
+      return this.cpuCores;
    }
 
-}
+   /**
+    * @return the memory of the server in MB
+    */
+   public int getMemorySizeMB() {
+      return this.memorySizeMB;
+   }
+
+   /**
+    * @return the disk of the server in GB
+    */
+   public int getDiskSizeGB() {
+      return this.diskSizeGB;
+   }
+
+   /**
+    * @return the transfer of the server
+    */
+   public int getTransferGB() {
+      return this.transferGB;
+   }
+
+   /**
+    * @return details of the cost of the server
+    */
+   public Cost getCost() {
+      return this.cost;
+   }
+
+   /**
+    * @return the ip addresses assigned to the server
+    */
+   public Set<Ip> getIps() {
+      return this.ips;
+   }
+
+   protected ToStringHelper string() {
+      return super.string().add("state", state).add("description", description).add("templateName", templateName)
+            .add("cpuCores", cpuCores).add("memorySizeMB", memorySizeMB).add("diskSizeGB", diskSizeGB)
+            .add("transferGB", transferGB).add("cost", cost).add("ips", ips);
+   }
+
+}
\ No newline at end of file
diff --git a/labs/glesys/src/main/java/org/jclouds/glesys/domain/ServerLimit.java b/labs/glesys/src/main/java/org/jclouds/glesys/domain/ServerLimit.java
index 3a3a60b..3f13bd0 100644
--- a/labs/glesys/src/main/java/org/jclouds/glesys/domain/ServerLimit.java
+++ b/labs/glesys/src/main/java/org/jclouds/glesys/domain/ServerLimit.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -18,54 +18,94 @@
  */
 package org.jclouds.glesys.domain;
 
+import java.beans.ConstructorProperties;
+
 import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
 
 /**
  * Detailed information about an OpenVZ server's limits
- * 
+ *
  * @author Adam Lowe
  * @see <a href= "https://customer.glesys.com/api.php?a=doc#server_limits" />
  */
 public class ServerLimit {
-   public static Builder builder() {
-      return new Builder();
+
+   public static Builder<?> builder() {
+      return new ConcreteBuilder();
    }
 
-   public static class Builder {
-      private int held;
-      private int maxHeld;
-      private int barrier;
-      private int limit;
-      private int failCount;
+   public Builder<?> toBuilder() {
+      return new ConcreteBuilder().fromServerLimit(this);
+   }
 
-      public Builder held(int held) {
+   public static abstract class Builder<T extends Builder<T>> {
+      protected abstract T self();
+
+      protected long held;
+      protected long maxHeld;
+      protected long barrier;
+      protected long limit;
+      protected long failCount;
+
+      /**
+       * @see ServerLimit#getHeld()
+       */
+      public T held(long held) {
          this.held = held;
-         return this;
+         return self();
       }
 
-      public Builder maxHeld(int maxHeld) {
+      /**
+       * @see ServerLimit#getMaxHeld()
+       */
+      public T maxHeld(long maxHeld) {
          this.maxHeld = maxHeld;
-         return this;
+         return self();
       }
 
-      public Builder barrier(int barrier) {
+      /**
+       * @see ServerLimit#getBarrier()
+       */
+      public T barrier(long barrier) {
          this.barrier = barrier;
-         return this;
+         return self();
       }
 
-      public Builder limit(int limit) {
+      /**
+       * @see ServerLimit#getLimit()
+       */
+      public T limit(long limit) {
          this.limit = limit;
-         return this;
+         return self();
       }
 
-      public Builder failCount(int failCount) {
+      /**
+       * @see ServerLimit#getFailCount()
+       */
+      public T failCount(long failCount) {
          this.failCount = failCount;
-         return this;
+         return self();
       }
 
       public ServerLimit build() {
          return new ServerLimit(held, maxHeld, barrier, limit, failCount);
       }
+
+      public T fromServerLimit(ServerLimit in) {
+         return this.held(in.getHeld())
+               .maxHeld(in.getMaxHeld())
+               .barrier(in.getBarrier())
+               .limit(in.getLimit())
+               .failCount(in.getFailCount());
+      }
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
    }
 
    private final long held;
@@ -74,7 +114,10 @@
    private final long limit;
    private final long failCount;
 
-   public ServerLimit(long held, long maxHeld, long barrier, long limit, long failCount) {
+   @ConstructorProperties({
+         "held", "maxHeld", "barrier", "limit", "failCount"
+   })
+   protected ServerLimit(long held, long maxHeld, long barrier, long limit, long failCount) {
       this.held = held;
       this.maxHeld = maxHeld;
       this.barrier = barrier;
@@ -83,40 +126,23 @@
    }
 
    public long getHeld() {
-      return held;
+      return this.held;
    }
 
    public long getMaxHeld() {
-      return maxHeld;
+      return this.maxHeld;
    }
 
    public long getBarrier() {
-      return barrier;
+      return this.barrier;
    }
 
    public long getLimit() {
-      return limit;
+      return this.limit;
    }
 
    public long getFailCount() {
-      return failCount;
-   }
-
-   @Override
-   public boolean equals(Object object) {
-      if (this == object) {
-         return true;
-      }
-      if (object instanceof ServerLimit) {
-         final ServerLimit other = (ServerLimit) object;
-         return Objects.equal(held, other.held)
-               && Objects.equal(maxHeld, other.maxHeld)
-               && Objects.equal(barrier, other.barrier)
-               && Objects.equal(limit, other.limit)
-               && Objects.equal(failCount, other.failCount);
-      } else {
-         return false;
-      }
+      return this.failCount;
    }
 
    @Override
@@ -125,7 +151,25 @@
    }
 
    @Override
-   public String toString() {
-      return String.format("[held=%d, maxHeld=%d, barrier=%d, limit=%d, failCount=%d]", held, maxHeld, barrier, limit, failCount);
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      ServerLimit that = ServerLimit.class.cast(obj);
+      return Objects.equal(this.held, that.held)
+            && Objects.equal(this.maxHeld, that.maxHeld)
+            && Objects.equal(this.barrier, that.barrier)
+            && Objects.equal(this.limit, that.limit)
+            && Objects.equal(this.failCount, that.failCount);
    }
-}
+
+   protected ToStringHelper string() {
+      return Objects.toStringHelper("").add("held", held).add("maxHeld", maxHeld).add("barrier", barrier)
+            .add("limit", limit).add("failCount", failCount);
+   }
+
+   @Override
+   public String toString() {
+      return string().toString();
+   }
+
+}
\ No newline at end of file
diff --git a/labs/glesys/src/main/java/org/jclouds/glesys/domain/ServerSpec.java b/labs/glesys/src/main/java/org/jclouds/glesys/domain/ServerSpec.java
index 3ddb603..513b121 100644
--- a/labs/glesys/src/main/java/org/jclouds/glesys/domain/ServerSpec.java
+++ b/labs/glesys/src/main/java/org/jclouds/glesys/domain/ServerSpec.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -18,92 +18,129 @@
  */
 package org.jclouds.glesys.domain;
 
-import static com.google.common.base.Objects.equal;
-import static com.google.common.base.Objects.toStringHelper;
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import java.beans.ConstructorProperties;
+
 import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
 
 /**
- * 
- * 
+ * Class ServerSpec
+ *
  * @author Adrian Cole
  */
 public class ServerSpec {
 
-   public static Builder builder() {
-      return new Builder();
+   public static Builder<?> builder() {
+      return new ConcreteBuilder();
    }
 
-   public Builder toBuilder() {
-      return Builder.fromServerSpecification(this);
+   public Builder<?> toBuilder() {
+      return new ConcreteBuilder().fromServerSpec(this);
    }
 
-   public static class Builder {
-      protected String datacenter;
+   public static abstract class Builder<T extends Builder<T>> {
+      protected abstract T self();
+
       protected String platform;
-      protected String templateName;
-      protected int diskSizeGB;
+      protected String datacenter;
       protected int memorySizeMB;
+      protected int diskSizeGB;
+      protected String templateName;
       protected int cpuCores;
       protected int transferGB;
 
-      public Builder datacenter(String datacenter) {
-         this.datacenter = datacenter;
-         return this;
+      /**
+       * @see ServerSpec#getPlatform()
+       */
+      public T platform(String platform) {
+         this.platform = checkNotNull(platform, "platform");
+         return self();
       }
 
-      public Builder platform(String platform) {
-         this.platform = platform;
-         return this;
+      /**
+       * @see ServerSpec#getDatacenter()
+       */
+      public T datacenter(String datacenter) {
+         this.datacenter = checkNotNull(datacenter, "datacenter");
+         return self();
       }
 
-      public Builder templateName(String templateName) {
-         this.templateName = templateName;
-         return this;
-      }
-
-      public Builder diskSizeGB(int diskSizeGB) {
-         this.diskSizeGB = diskSizeGB;
-         return this;
-      }
-
-      public Builder memorySizeMB(int memorySizeMB) {
+      /**
+       * @see ServerSpec#getMemorySizeMB()
+       */
+      public T memorySizeMB(int memorySizeMB) {
          this.memorySizeMB = memorySizeMB;
-         return this;
+         return self();
       }
 
-      public Builder cpuCores(int cpuCores) {
+      /**
+       * @see ServerSpec#getDiskSizeGB()
+       */
+      public T diskSizeGB(int diskSizeGB) {
+         this.diskSizeGB = diskSizeGB;
+         return self();
+      }
+
+      /**
+       * @see ServerSpec#getTemplateName()
+       */
+      public T templateName(String templateName) {
+         this.templateName = checkNotNull(templateName, "templateName");
+         return self();
+      }
+
+      /**
+       * @see ServerSpec#getCpuCores()
+       */
+      public T cpuCores(int cpuCores) {
          this.cpuCores = cpuCores;
-         return this;
+         return self();
       }
 
-      public Builder transferGB(int transferGB) {
+      /**
+       * @see ServerSpec#getTransferGB()
+       */
+      public T transferGB(int transferGB) {
          this.transferGB = transferGB;
-         return this;
+         return self();
       }
 
       public ServerSpec build() {
          return new ServerSpec(platform, datacenter, memorySizeMB, diskSizeGB, templateName, cpuCores, transferGB);
       }
 
-      public static Builder fromServerSpecification(ServerSpec in) {
-         return new Builder().platform(in.getPlatform()).datacenter(in.getDatacenter())
-               .memorySizeMB(in.getMemorySizeMB()).diskSizeGB(in.getDiskSizeGB()).templateName(in.getTemplateName())
-               .cpuCores(in.getCpuCores()).transferGB(in.getTransferGB());
+      public T fromServerSpec(ServerSpec in) {
+         return this.platform(in.getPlatform())
+               .datacenter(in.getDatacenter())
+               .memorySizeMB(in.getMemorySizeMB())
+               .diskSizeGB(in.getDiskSizeGB())
+               .templateName(in.getTemplateName())
+               .cpuCores(in.getCpuCores())
+               .transferGB(in.getTransferGB());
       }
    }
 
-   protected final String platform;
-   protected final String datacenter;
-   protected final int memorySizeMB;
-   protected final int diskSizeGB;
-   protected final String templateName;
-   protected final int cpuCores;
-   protected final int transferGB;
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
+   }
 
-   protected ServerSpec(String platform, String datacenter, int memorySizeMB, int diskSizeGB, String templateName,
-         int cpuCores, int transferGB) {
+   private final String platform;
+   private final String datacenter;
+   private final int memorySizeMB;
+   private final int diskSizeGB;
+   private final String templateName;
+   private final int cpuCores;
+   private final int transferGB;
+
+   @ConstructorProperties({
+         "platform", "datacenter", "memorySizeMB", "diskSizeGB", "templateName", "cpuCores", "transferGB"
+   })
+   protected ServerSpec(String platform, String datacenter, int memorySizeMB, int diskSizeGB, String templateName, int cpuCores, int transferGB) {
       this.platform = checkNotNull(platform, "platform");
       this.datacenter = checkNotNull(datacenter, "datacenter");
       this.memorySizeMB = memorySizeMB;
@@ -116,66 +153,50 @@
    /**
     * @return the data center to create the new server in
     */
-   public String getDatacenter() {
-      return datacenter;
+   public String getPlatform() {
+      return this.platform;
    }
 
    /**
     * @return the platform to use (i.e. "Xen" or "OpenVZ")
     */
-   public String getPlatform() {
-      return platform;
+   public String getDatacenter() {
+      return this.datacenter;
    }
 
    /**
     * @return the os template to use to create the new server
     */
-   public String getTemplateName() {
-      return templateName;
+   public int getMemorySizeMB() {
+      return this.memorySizeMB;
    }
 
    /**
     * @return the amount of disk space, in GB, to allocate
     */
    public int getDiskSizeGB() {
-      return diskSizeGB;
+      return this.diskSizeGB;
    }
 
    /**
     * @return the memory, in MB, to allocate
     */
-   public int getMemorySizeMB() {
-      return memorySizeMB;
+   public String getTemplateName() {
+      return this.templateName;
    }
 
    /**
     * @return the number of CPU cores to allocate
     */
    public int getCpuCores() {
-      return cpuCores;
+      return this.cpuCores;
    }
 
    /**
     * @return bandwidth of in GB
     */
    public int getTransferGB() {
-      return transferGB;
-   }
-
-   @Override
-   public boolean equals(Object object) {
-      if (this == object) {
-         return true;
-      }
-      if (object instanceof ServerSpec) {
-         final ServerSpec that = ServerSpec.class.cast(object);
-         return equal(platform, that.platform) && equal(datacenter, that.datacenter)
-               && equal(memorySizeMB, that.memorySizeMB) && equal(diskSizeGB, that.diskSizeGB)
-               && equal(templateName, that.templateName) && equal(cpuCores, that.cpuCores)
-               && equal(transferGB, that.transferGB);
-      } else {
-         return false;
-      }
+      return this.transferGB;
    }
 
    @Override
@@ -184,9 +205,27 @@
    }
 
    @Override
-   public String toString() {
-      return toStringHelper("").add("platform", platform).add("datacenter", datacenter)
-            .add("templateName", templateName).add("cpuCores", cpuCores).add("memorySizeMB", memorySizeMB)
-            .add("diskSizeGB", diskSizeGB).add("transferGB", transferGB).toString();
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      ServerSpec that = ServerSpec.class.cast(obj);
+      return Objects.equal(this.platform, that.platform)
+            && Objects.equal(this.datacenter, that.datacenter)
+            && Objects.equal(this.memorySizeMB, that.memorySizeMB)
+            && Objects.equal(this.diskSizeGB, that.diskSizeGB)
+            && Objects.equal(this.templateName, that.templateName)
+            && Objects.equal(this.cpuCores, that.cpuCores)
+            && Objects.equal(this.transferGB, that.transferGB);
    }
-}
+
+   protected ToStringHelper string() {
+      return Objects.toStringHelper("").add("platform", platform).add("datacenter", datacenter)
+            .add("memorySizeMB", memorySizeMB).add("diskSizeGB", diskSizeGB).add("templateName", templateName)
+            .add("cpuCores", cpuCores).add("transferGB", transferGB);
+   }
+
+   @Override
+   public String toString() {
+      return string().toString();
+   }
+}
\ No newline at end of file
diff --git a/labs/glesys/src/main/java/org/jclouds/glesys/domain/ServerStatus.java b/labs/glesys/src/main/java/org/jclouds/glesys/domain/ServerStatus.java
index 4b3dfc7..f8cc218 100644
--- a/labs/glesys/src/main/java/org/jclouds/glesys/domain/ServerStatus.java
+++ b/labs/glesys/src/main/java/org/jclouds/glesys/domain/ServerStatus.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -18,7 +18,14 @@
  */
 package org.jclouds.glesys.domain;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.beans.ConstructorProperties;
+
+import org.jclouds.javax.annotation.Nullable;
+
 import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
 
 /**
  * Detailed information server status including hardware usage (cpu, memory and disk), bandwidth and up-time.
@@ -26,62 +33,93 @@
  * @author Adam Lowe
  * @see <a href= "https://customer.glesys.com/api.php?a=doc#server_status" />
  */
-
 public class ServerStatus {
 
-   public static Builder builder() {
-      return new Builder();
+   public static Builder<?> builder() {
+      return new ConcreteBuilder();
    }
 
-   public static class Builder {
-      private Server.State state;
-      private ResourceUsage cpu;
-      private ResourceUsage memory;
-      private ResourceUsage disk;
-      private ServerUptime uptime;
+   public Builder<?> toBuilder() {
+      return new ConcreteBuilder().fromServerStatus(this);
+   }
 
-      public Builder state(Server.State state) {
-         this.state = state;
-         return this;
+   public static abstract class Builder<T extends Builder<T>> {
+      protected abstract T self();
+
+      protected Server.State state;
+      protected ResourceStatus cpu;
+      protected ResourceStatus memory;
+      protected ResourceStatus disk;
+      protected ServerUptime uptime;
+
+      /**
+       * @see ServerStatus#getState()
+       */
+      public T state(Server.State state) {
+         this.state = checkNotNull(state, "state");
+         return self();
       }
 
-      public Builder cpu(ResourceUsage cpu) {
-         this.cpu = cpu;
-         return this;
+      /**
+       * @see ServerStatus#getCpu()
+       */
+      public T cpu(ResourceStatus cpu) {
+         this.cpu = checkNotNull(cpu, "cpu");
+         return self();
       }
 
-      public Builder memory(ResourceUsage memory) {
-         this.memory = memory;
-         return this;
+      /**
+       * @see ServerStatus#getMemory()
+       */
+      public T memory(ResourceStatus memory) {
+         this.memory = checkNotNull(memory, "memory");
+         return self();
       }
 
-      public Builder disk(ResourceUsage disk) {
-         this.disk = disk;
-         return this;
+      /**
+       * @see ServerStatus#getDisk()
+       */
+      public T disk(ResourceStatus disk) {
+         this.disk = checkNotNull(disk, "disk");
+         return self();
       }
 
-      public Builder uptime(ServerUptime uptime) {
-         this.uptime = uptime;
-         return this;
+      /**
+       * @see ServerStatus#getUptime()
+       */
+      public T uptime(ServerUptime uptime) {
+         this.uptime = checkNotNull(uptime, "uptime");
+         return self();
       }
 
       public ServerStatus build() {
          return new ServerStatus(state, cpu, memory, disk, uptime);
       }
 
-      public Builder fromServerStatus(ServerStatus in) {
-         return state(in.getState()).cpu(in.getCpu()).memory(in.getMemory()).disk(in.getDisk()).uptime(in.getUptime());
+      public T fromServerStatus(ServerStatus in) {
+         return this.state(in.getState()).cpu(in.getCpu()).memory(in.getMemory()).disk(in.getDisk()).uptime(in.getUptime());
+      }
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
       }
    }
 
    private final Server.State state;
-   private final ResourceUsage cpu;
-   private final ResourceUsage memory;
-   private final ResourceUsage disk;
+   private final ResourceStatus cpu;
+   private final ResourceStatus memory;
+   private final ResourceStatus disk;
    private final ServerUptime uptime;
 
-   public ServerStatus(Server.State state, ResourceUsage cpu, ResourceUsage memory, ResourceUsage disk,  ServerUptime uptime) {
-      this.state = state;
+   @ConstructorProperties({
+         "state", "cpu", "memory", "disk", "uptime"
+   })
+   protected ServerStatus(Server.State state, @Nullable ResourceStatus cpu, @Nullable ResourceStatus memory,
+                          @Nullable ResourceStatus disk, @Nullable ServerUptime uptime) {
+      this.state = checkNotNull(state, "state");
       this.cpu = cpu;
       this.memory = memory;
       this.disk = disk;
@@ -91,64 +129,68 @@
    /**
     * @return the state of the server (e.g. "running")
     */
+   @Nullable
    public Server.State getState() {
-      return state;
+      return this.state;
    }
 
    /**
     * @return CPU usage information
     */
-   public ResourceUsage getCpu() {
-      return cpu;
+   @Nullable
+   public ResourceStatus getCpu() {
+      return this.cpu;
    }
 
    /**
     * @return details of memory usage and limits
     */
-   public ResourceUsage getMemory() {
-      return memory;
+   @Nullable
+   public ResourceStatus getMemory() {
+      return this.memory;
    }
 
    /**
     * @return details of disk usage and limits
     */
-   public ResourceUsage getDisk() {
-      return disk;
+   @Nullable
+   public ResourceStatus getDisk() {
+      return this.disk;
    }
 
    /**
     * @return the uptime of the server
     */
+   @Nullable
    public ServerUptime getUptime() {
-      return uptime;
-   }
-
-   @Override
-   public boolean equals(Object object) {
-      if (this == object) {
-         return true;
-      }
-      if (object instanceof ServerStatus) {
-         final ServerStatus other = (ServerStatus) object;
-         return Objects.equal(state, other.state)
-               && Objects.equal(cpu, other.cpu)
-               && Objects.equal(memory, other.memory)
-               && Objects.equal(disk, other.disk)
-               && Objects.equal(uptime, other.uptime);
-      } else {
-         return false;
-      }
+      return this.uptime;
    }
 
    @Override
    public int hashCode() {
       return Objects.hashCode(state, cpu, memory, disk, uptime);
    }
-   
+
    @Override
-   public String toString() {
-      return String.format("[state=%s, cpu=%s, memory=%s, disk=%s, uptime=%s]",
-            state, cpu, memory, disk, uptime);
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      ServerStatus that = ServerStatus.class.cast(obj);
+      return Objects.equal(this.state, that.state)
+            && Objects.equal(this.cpu, that.cpu)
+            && Objects.equal(this.memory, that.memory)
+            && Objects.equal(this.disk, that.disk)
+            && Objects.equal(this.uptime, that.uptime);
    }
 
-}
+   protected ToStringHelper string() {
+      return Objects.toStringHelper("")
+            .add("state", state).add("cpu", cpu).add("memory", memory).add("disk", disk).add("uptime", uptime);
+   }
+
+   @Override
+   public String toString() {
+      return string().toString();
+   }
+
+}
\ No newline at end of file
diff --git a/labs/glesys/src/main/java/org/jclouds/glesys/domain/ServerUptime.java b/labs/glesys/src/main/java/org/jclouds/glesys/domain/ServerUptime.java
index eb44a06..5b78d96 100644
--- a/labs/glesys/src/main/java/org/jclouds/glesys/domain/ServerUptime.java
+++ b/labs/glesys/src/main/java/org/jclouds/glesys/domain/ServerUptime.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -18,7 +18,12 @@
  */
 package org.jclouds.glesys.domain;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.beans.ConstructorProperties;
+
 import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
 
 /**
  * Represents an 'uptime' duration of server in a Glesys cloud
@@ -27,64 +32,76 @@
  * @see ServerStatus
  */
 public class ServerUptime {
-   public static Builder builder() {
-      return new Builder();
+
+   public static Builder<?> builder() {
+      return new ConcreteBuilder();
    }
 
-   public static class Builder {
-      private long current;
-      private String unit;
-      
-      public Builder current(long current) {
+   public Builder<?> toBuilder() {
+      return new ConcreteBuilder().fromServerUptime(this);
+   }
+
+   public static abstract class Builder<T extends Builder<T>> {
+      protected abstract T self();
+
+      protected long current;
+      protected String unit;
+
+      /**
+       * @see ServerUptime#getCurrent()
+       */
+      public T current(long current) {
          this.current = current;
-         return this;
+         return self();
       }
 
-      public Builder unit(String unit) {
-         this.unit = unit;
-         return this;
+      /**
+       * @see ServerUptime#getUnit()
+       */
+      public T unit(String unit) {
+         this.unit = checkNotNull(unit, "unit");
+         return self();
       }
-      
+
       public ServerUptime build() {
          return new ServerUptime(current, unit);
       }
-      
-      public Builder fromServerUptime(ServerUptime from) {
-         return current(from.getCurrent()).unit(from.getUnit());
+
+      public T fromServerUptime(ServerUptime in) {
+         return this.current(in.getCurrent()).unit(in.getUnit());
       }
    }
-   
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
+   }
+
    private final long current;
    private final String unit;
 
-   public ServerUptime(long current, String unit) {
+   @ConstructorProperties({
+         "current", "unit"
+   })
+   protected ServerUptime(long current, String unit) {
       this.current = current;
-      this.unit = unit;
+      this.unit = checkNotNull(unit, "unit");
    }
-   
+
    /**
-    * @return the time the server has been up in #unit
+    * @return the time the server has been up in #getUnit()
     */
    public long getCurrent() {
-      return current;
+      return this.current;
    }
 
    /**
-    * @return the  unit used for #time
+    * @return the unit used for #getCurrent()
     */
    public String getUnit() {
-      return unit;
-   }
-
-
-   @Override
-   public boolean equals(Object object) {
-      if (this == object) {
-         return true;
-      }
-      return object instanceof ServerUptime
-            && Objects.equal(current, ((ServerUptime) object).getCurrent())
-            && Objects.equal(unit, ((ServerUptime) object).getUnit());
+      return this.unit;
    }
 
    @Override
@@ -93,8 +110,20 @@
    }
 
    @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      ServerUptime that = ServerUptime.class.cast(obj);
+      return Objects.equal(this.current, that.current) && Objects.equal(this.unit, that.unit);
+   }
+
+   protected ToStringHelper string() {
+      return Objects.toStringHelper("").add("current", current).add("unit", unit);
+   }
+
+   @Override
    public String toString() {
-      return String.format("[current=%d unit=%s]", current, unit);
+      return string().toString();
    }
 
 }
\ No newline at end of file
diff --git a/labs/glesys/src/main/java/org/jclouds/glesys/features/ArchiveAsyncClient.java b/labs/glesys/src/main/java/org/jclouds/glesys/features/ArchiveAsyncClient.java
index a5af736..13fd47e 100644
--- a/labs/glesys/src/main/java/org/jclouds/glesys/features/ArchiveAsyncClient.java
+++ b/labs/glesys/src/main/java/org/jclouds/glesys/features/ArchiveAsyncClient.java
@@ -29,7 +29,6 @@
 
 import org.jclouds.glesys.domain.Archive;
 import org.jclouds.glesys.domain.ArchiveAllowedArguments;
-import org.jclouds.glesys.domain.ArchiveDetails;
 import org.jclouds.http.filters.BasicAuthentication;
 import org.jclouds.rest.annotations.ExceptionParser;
 import org.jclouds.rest.annotations.RequestFilters;
@@ -61,21 +60,23 @@
    ListenableFuture<Set<Archive>> listArchives();
 
    /**
-    * @see ArchiveClient#getArchiveDetails
+    * @see ArchiveClient#getArchive
     */
    @POST
    @Path("/archive/details/format/json")
    @SelectJson("details")
    @Consumes(MediaType.APPLICATION_JSON)
    @ExceptionParser(ReturnNullOnNotFoundOr404.class)
-   ListenableFuture<ArchiveDetails> getArchiveDetails(@FormParam("username") String username);
+   ListenableFuture<Archive> getArchive(@FormParam("username") String username);
 
    /**
     * @see ArchiveClient#createArchive
     */
    @POST
    @Path("/archive/create/format/json")
-   ListenableFuture<Void> createArchive(@FormParam("username") String username, @FormParam("password") String password,
+   @SelectJson("details")
+   @Consumes(MediaType.APPLICATION_JSON)
+   ListenableFuture<Archive> createArchive(@FormParam("username") String username, @FormParam("password") String password,
                                         @FormParam("size")int size);
 
    /**
@@ -90,13 +91,17 @@
     */
    @POST
    @Path("/archive/resize/format/json")
-   ListenableFuture<Void> resizeArchive(@FormParam("username") String username, @FormParam("size") int size);
+   @SelectJson("details")
+   @Consumes(MediaType.APPLICATION_JSON)
+   ListenableFuture<Archive> resizeArchive(@FormParam("username") String username, @FormParam("size") int size);
    /**
     * @see ArchiveClient#changeArchivePassword
     */
    @POST
    @Path("/archive/changepassword/format/json")
-   ListenableFuture<Void> changeArchivePassword(@FormParam("username") String username, @FormParam("password") String password);
+   @SelectJson("details")
+   @Consumes(MediaType.APPLICATION_JSON)
+   ListenableFuture<Archive> changeArchivePassword(@FormParam("username") String username, @FormParam("password") String password);
 
    /**
     * @see org.jclouds.glesys.features.ArchiveClient#getArchiveAllowedArguments
diff --git a/labs/glesys/src/main/java/org/jclouds/glesys/features/ArchiveClient.java b/labs/glesys/src/main/java/org/jclouds/glesys/features/ArchiveClient.java
index 5b75a21..b3ee9c5 100644
--- a/labs/glesys/src/main/java/org/jclouds/glesys/features/ArchiveClient.java
+++ b/labs/glesys/src/main/java/org/jclouds/glesys/features/ArchiveClient.java
@@ -24,7 +24,6 @@
 import org.jclouds.concurrent.Timeout;
 import org.jclouds.glesys.domain.Archive;
 import org.jclouds.glesys.domain.ArchiveAllowedArguments;
-import org.jclouds.glesys.domain.ArchiveDetails;
 
 /**
  * Provides synchronous access to Archive requests.
@@ -48,7 +47,7 @@
     * @param username the username associated with the archive
     * @return the archive information or null if not found
     */
-   ArchiveDetails getArchiveDetails(String username);
+   Archive getArchive(String username);
 
    /**
     * Create a new backup volume.
@@ -58,7 +57,7 @@
     * @param password the new password
     * @param size     the new size required in GB
     */
-   void createArchive(String username, String password, int size);
+   Archive createArchive(String username, String password, int size);
 
    /**
     * Delete an archive volume. All files on the volume
@@ -75,7 +74,7 @@
     * @param username the username associated with the archive
     * @param size     the new size required, see #getArchiveAllowedArguments for valid values
     */
-   void resizeArchive(String username, int size);
+   Archive resizeArchive(String username, int size);
 
    /**
     * Change the password for an archive user.
@@ -83,7 +82,7 @@
     * @param username the archive username
     * @param password the new password
     */
-   void changeArchivePassword(String username, String password);
+   Archive changeArchivePassword(String username, String password);
 
    /**
     * Lists the allowed arguments for some of the functions in this module such as archive size.
diff --git a/labs/glesys/src/main/java/org/jclouds/glesys/features/DomainAsyncClient.java b/labs/glesys/src/main/java/org/jclouds/glesys/features/DomainAsyncClient.java
index 1bc6b37..23224ac 100644
--- a/labs/glesys/src/main/java/org/jclouds/glesys/features/DomainAsyncClient.java
+++ b/labs/glesys/src/main/java/org/jclouds/glesys/features/DomainAsyncClient.java
@@ -37,6 +37,7 @@
 import org.jclouds.rest.annotations.RequestFilters;
 import org.jclouds.rest.annotations.SelectJson;
 import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404;
+import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404;
 
 import com.google.common.util.concurrent.ListenableFuture;
 
@@ -62,18 +63,32 @@
    ListenableFuture<Set<Domain>> listDomains();
 
    /**
+    * @see org.jclouds.glesys.features.DomainClient#getDomain
+    */
+   @POST
+   @Path("/domain/details/format/json")
+   @SelectJson("domain")
+   @Consumes(MediaType.APPLICATION_JSON)
+   @ExceptionParser(ReturnNullOnNotFoundOr404.class)
+   ListenableFuture<Domain> getDomain(@FormParam("domainname") String name);
+
+   /**
     * @see DomainClient#addDomain
     */
    @POST
    @Path("/domain/add/format/json")
-   ListenableFuture<Void> addDomain(@FormParam("domainname") String name, AddDomainOptions... options);
+   @SelectJson("domain")
+   @Consumes(MediaType.APPLICATION_JSON)
+   ListenableFuture<Domain> addDomain(@FormParam("domainname") String name, AddDomainOptions... options);
 
    /**
     * @see DomainClient#editDomain
     */
    @POST
    @Path("/domain/edit/format/json")
-   ListenableFuture<Void> editDomain(@FormParam("domainname") String domain, DomainOptions... options);
+   @SelectJson("domain")
+   @Consumes(MediaType.APPLICATION_JSON)
+   ListenableFuture<Domain> editDomain(@FormParam("domainname") String domain, DomainOptions... options);
 
 
    /**
@@ -97,7 +112,9 @@
     */
    @POST
    @Path("/domain/addrecord/format/json")
-   ListenableFuture<Void> addRecord(@FormParam("domainname") String domain, @FormParam("host") String host,
+   @SelectJson("record")
+   @Consumes(MediaType.APPLICATION_JSON)
+   ListenableFuture<DomainRecord> addRecord(@FormParam("domainname") String domain, @FormParam("host") String host,
                                     @FormParam("type") String type, @FormParam("data") String data,
                                     AddRecordOptions... options);
 
@@ -106,7 +123,9 @@
     */
    @POST
    @Path("/domain/updaterecord/format/json")
-   ListenableFuture<Void> editRecord(@FormParam("recordid") String record_id, EditRecordOptions... options);
+   @SelectJson("record")
+   @Consumes(MediaType.APPLICATION_JSON)
+   ListenableFuture<DomainRecord> editRecord(@FormParam("recordid") String record_id, EditRecordOptions... options);
 
    /**
     * @see DomainClient#deleteRecord
diff --git a/labs/glesys/src/main/java/org/jclouds/glesys/features/DomainClient.java b/labs/glesys/src/main/java/org/jclouds/glesys/features/DomainClient.java
index 571f6d6..f4a9eb5 100644
--- a/labs/glesys/src/main/java/org/jclouds/glesys/features/DomainClient.java
+++ b/labs/glesys/src/main/java/org/jclouds/glesys/features/DomainClient.java
@@ -41,27 +41,36 @@
 public interface DomainClient {
 
    /**
-    * Get a list of all invoices for this account.
+    * Get a list of all domains for this account.
     *
-    * @return an account's associated invoice objects.
+    * @return an account's associated domain objects.
     */
    Set<Domain> listDomains();
 
    /**
-    * Add a domain to the Glesys dns-system
+    * Get a specific domain.
     *
-    * @param domain  the name of the domain to add.
-    * @param options optional parameters
+    * @return the requested domain object.
     */
-   void addDomain(String domain, AddDomainOptions... options);
+   Domain getDomain(String domain);
 
    /**
     * Add a domain to the Glesys dns-system
     *
     * @param domain  the name of the domain to add.
     * @param options optional parameters
+    * @return information about the added domain
     */
-   void editDomain(String domain, DomainOptions... options);
+   Domain addDomain(String domain, AddDomainOptions... options);
+
+   /**
+    * Edit a domain to the Glesys dns-system
+    *
+    * @param domain  the name of the domain to add.
+    * @param options optional parameters
+    * @return information about the modified domain
+    */
+   Domain editDomain(String domain, DomainOptions... options);
 
    /**
     * Remove a domain to the Glesys dns-system
@@ -80,13 +89,10 @@
    /**
     * Add a DNS Record
     *
-    * @param domain the domain to add the record to
-    * @param host
-    * @param type
-    * @param data
+    * @param domain  the domain to add the record to
     * @param options optional settings for the record
     */
-   void addRecord(String domain, String host, String type, String data, AddRecordOptions... options);
+   DomainRecord addRecord(String domain, String host, String type, String data, AddRecordOptions... options);
 
    /**
     * Modify a specific DNS Record
@@ -95,7 +101,7 @@
     * @param options  the settings to change
     * @see #listRecords to retrieve the necessary ids
     */
-   void editRecord(String recordId, EditRecordOptions... options);
+   DomainRecord editRecord(String recordId, EditRecordOptions... options);
 
    /**
     * Delete a DNS record
diff --git a/labs/glesys/src/main/java/org/jclouds/glesys/features/EmailAsyncClient.java b/labs/glesys/src/main/java/org/jclouds/glesys/features/EmailAsyncClient.java
index b418d48..82f1620 100644
--- a/labs/glesys/src/main/java/org/jclouds/glesys/features/EmailAsyncClient.java
+++ b/labs/glesys/src/main/java/org/jclouds/glesys/features/EmailAsyncClient.java
@@ -27,6 +27,7 @@
 import javax.ws.rs.core.MediaType;
 
 import org.jclouds.glesys.domain.EmailAccount;
+import org.jclouds.glesys.domain.EmailAlias;
 import org.jclouds.glesys.domain.EmailOverview;
 import org.jclouds.glesys.options.CreateAccountOptions;
 import org.jclouds.glesys.options.EditAccountOptions;
@@ -55,7 +56,7 @@
     */
    @POST
    @Path("/email/overview/format/json")
-   @SelectJson("response")
+   @SelectJson("overview")
    @Consumes(MediaType.APPLICATION_JSON)
    @ExceptionParser(ReturnNullOnNotFoundOr404.class)
    ListenableFuture<EmailOverview> getEmailOverview();
@@ -68,41 +69,59 @@
    @SelectJson("emailaccounts")
    @Consumes(MediaType.APPLICATION_JSON)
    @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
-   ListenableFuture<Set<EmailAccount>> listAccounts(@FormParam("domain") String domain);
+   ListenableFuture<Set<EmailAccount>> listAccounts(@FormParam("domainname") String domain);
+
+   /**
+    * @see org.jclouds.glesys.features.EmailClient#listAccounts
+    */
+   @POST
+   @Path("/email/list/format/json")
+   @SelectJson("emailaliases")
+   @Consumes(MediaType.APPLICATION_JSON)
+   @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
+   ListenableFuture<Set<EmailAlias>> listAliases(@FormParam("domainname") String domain);
 
    /**
     * @see org.jclouds.glesys.features.EmailClient#createAccount
     */
    @POST
+   @Consumes(MediaType.APPLICATION_JSON)
+   @SelectJson("emailaccount")
    @Path("/email/createaccount/format/json")
-   ListenableFuture<Void> createAccount(@FormParam("emailaccount") String accountAddress, @FormParam("password") String password, CreateAccountOptions... options);
+   ListenableFuture<EmailAccount> createAccount(@FormParam("emailaccount") String accountAddress, @FormParam("password") String password, CreateAccountOptions... options);
 
    /**
     * @see org.jclouds.glesys.features.EmailClient#createAlias
     */
    @POST
+   @Consumes(MediaType.APPLICATION_JSON)
+   @SelectJson("alias")
    @Path("/email/createalias/format/json")
-   ListenableFuture<Void> createAlias(@FormParam("emailalias") String aliasAddress, @FormParam("goto") String toEmailAddress);
+   ListenableFuture<EmailAlias> createAlias(@FormParam("emailalias") String aliasAddress, @FormParam("goto") String toEmailAddress);
 
    /**
     * @see org.jclouds.glesys.features.EmailClient#editAccount
     */
    @POST
+   @Consumes(MediaType.APPLICATION_JSON)
+   @SelectJson("emailaccount")
    @Path("/email/editaccount/format/json")
-   ListenableFuture<Void> editAccount(@FormParam("emailaccount") String accountAddress, EditAccountOptions... options);
+   ListenableFuture<EmailAccount> editAccount(@FormParam("emailaccount") String accountAddress, EditAccountOptions... options);
 
    /**
     * @see org.jclouds.glesys.features.EmailClient#editAlias
     */
    @POST
+   @Consumes(MediaType.APPLICATION_JSON)
+   @SelectJson("alias")
    @Path("/email/editalias/format/json")
-   ListenableFuture<Void> editAlias(@FormParam("emailalias") String aliasAddress, @FormParam("goto") String toEmailAddress);
+   ListenableFuture<EmailAlias> editAlias(@FormParam("emailalias") String aliasAddress, @FormParam("goto") String toEmailAddress);
 
    /**
     * @see org.jclouds.glesys.features.EmailClient#delete
     */
    @POST
    @Path("/email/delete/format/json")
-   ListenableFuture<Void> delete(@FormParam("email") String accountAddress);
+   ListenableFuture<Boolean> delete(@FormParam("email") String accountAddress);
 
 }
diff --git a/labs/glesys/src/main/java/org/jclouds/glesys/features/EmailClient.java b/labs/glesys/src/main/java/org/jclouds/glesys/features/EmailClient.java
index 4346ada..1dc5460 100644
--- a/labs/glesys/src/main/java/org/jclouds/glesys/features/EmailClient.java
+++ b/labs/glesys/src/main/java/org/jclouds/glesys/features/EmailClient.java
@@ -23,6 +23,7 @@
 
 import org.jclouds.concurrent.Timeout;
 import org.jclouds.glesys.domain.EmailAccount;
+import org.jclouds.glesys.domain.EmailAlias;
 import org.jclouds.glesys.domain.EmailOverview;
 import org.jclouds.glesys.options.CreateAccountOptions;
 import org.jclouds.glesys.options.EditAccountOptions;
@@ -53,6 +54,13 @@
    Set<EmailAccount> listAccounts(String domain);
 
    /**
+    * Get the set of details about e-mail aliases
+    *
+    * @return the relevant set of details
+    */
+   Set<EmailAlias> listAliases(String domain);
+
+   /**
     * Create a new e-mail account
     *
     * @param accountAddress the e-mail address to use (the domain should already exist)
@@ -60,7 +68,7 @@
     * @param options        optional parameters
     * @see DomainClient#addDomain
     */
-   void createAccount(String accountAddress, String password, CreateAccountOptions... options);
+   EmailAccount createAccount(String accountAddress, String password, CreateAccountOptions... options);
 
    /**
     * Create an e-mail alias for an e-mail account
@@ -69,7 +77,7 @@
     * @param toEmailAddress the existing e-mail account address the alias should forward to
     * @see DomainClient#addDomain
     */
-   void createAlias(String aliasAddress, String toEmailAddress);
+   EmailAlias createAlias(String aliasAddress, String toEmailAddress);
 
    /**
     * Adjust an e-mail account's settings
@@ -77,7 +85,7 @@
     * @param accountAddress the existing e-mail account address
     * @param options        optional parameters
     */
-   void editAccount(String accountAddress, EditAccountOptions... options);
+   EmailAccount editAccount(String accountAddress, EditAccountOptions... options);
 
    /**
     * Adjust (re-target) an e-mail alias
@@ -85,13 +93,13 @@
     * @param aliasAddress   the existing alias e-mail address
     * @param toEmailAddress the existing e-mail account address the alias should forward to
     */
-   void editAlias(String aliasAddress, String toEmailAddress);
+   EmailAlias editAlias(String aliasAddress, String toEmailAddress);
 
    /**
     * Delete an e-mail account or alias
     *
     * @param accountAddress the existing alias e-mail account or alias address
     */
-   void delete(String accountAddress);
+   boolean delete(String accountAddress);
 
 }
\ No newline at end of file
diff --git a/labs/glesys/src/main/java/org/jclouds/glesys/features/IpAsyncClient.java b/labs/glesys/src/main/java/org/jclouds/glesys/features/IpAsyncClient.java
index d61f84a..383eda5 100644
--- a/labs/glesys/src/main/java/org/jclouds/glesys/features/IpAsyncClient.java
+++ b/labs/glesys/src/main/java/org/jclouds/glesys/features/IpAsyncClient.java
@@ -29,8 +29,10 @@
 import javax.ws.rs.core.MediaType;
 
 import org.jclouds.glesys.domain.IpDetails;
+import org.jclouds.glesys.options.ListIpOptions;
 import org.jclouds.http.filters.BasicAuthentication;
 import org.jclouds.rest.annotations.ExceptionParser;
+import org.jclouds.rest.annotations.FormParams;
 import org.jclouds.rest.annotations.RequestFilters;
 import org.jclouds.rest.annotations.SelectJson;
 import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404;
@@ -42,70 +44,111 @@
  * Provides asynchronous access to IP Addresses via their REST API.
  * <p/>
  *
- * @author Adrian Cole, Mattias Holmqvist
- *
- * @see ServerClient
+ * @author Adrian Cole, Mattias Holmqvist, Adam Lowe
+ * @see IpClient
  * @see <a href="https://customer.glesys.com/api.php" />
  */
 @RequestFilters(BasicAuthentication.class)
 public interface IpAsyncClient {
-
-
-   /**
-    * @see IpClient#take
-    */
-   @POST
-   @Path("/ip/take/format/json")
-   ListenableFuture<Void> take(@FormParam("ipaddress") String ipAddress);
-
-
-   /**
-    * @see IpClient#release
-    */
-   @POST
-   @Path("/ip/release/format/json")
-   ListenableFuture<Void> release(@FormParam("ipaddress") String ipAddress);
-
-   /**
-    * @see IpClient#add
-    */
-   @POST
-   @Path("/ip/add/format/json")
-   ListenableFuture<Void> addIpToServer(@FormParam("ipaddress") String ipAddress,
-                                        @FormParam("serverid") String serverId);
-
-
-   /**
-    * @see IpClient#remove
-    *
-    * TODO: add optional release_ip parameter
-    */
-   @POST
-   @Path("/ip/remove/format/json")
-   ListenableFuture<Void> removeIpFromServer(@FormParam("ipaddress") String ipAddress,
-                                 @FormParam("serverid") String serverId);
-
-
    /**
     * @see IpClient#listFree
     */
    @GET
    @Path("/ip/listfree/ipversion/{ipversion}/datacenter/{datacenter}/platform/{platform}/format/json")
    @Consumes(MediaType.APPLICATION_JSON)
-   @SelectJson("iplist")
+   @SelectJson("ipaddresses")
    @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
-   ListenableFuture<Set<String>> listFree(@PathParam("ipversion") String ipversion,
+   ListenableFuture<Set<String>> listFree(@PathParam("ipversion") int ipversion,
                                           @PathParam("datacenter") String datacenter,
                                           @PathParam("platform") String platform);
 
    /**
-    * @see IpClient#getIpDetails
+    * @see IpClient#take
+    */
+   @POST
+   @Path("/ip/take/format/json")
+   @SelectJson("details")
+   @Consumes(MediaType.APPLICATION_JSON)
+   ListenableFuture<IpDetails> take(@FormParam("ipaddress") String ipAddress);
+
+   /**
+    * @see IpClient#release
+    */
+   @POST
+   @Path("/ip/release/format/json")
+   @SelectJson("details")
+   @Consumes(MediaType.APPLICATION_JSON)
+   ListenableFuture<IpDetails> release(@FormParam("ipaddress") String ipAddress);
+
+   /**
+    * @see IpClient#listIps
+    */
+   @GET
+   @Path("/ip/listown/format/json")
+   @Consumes(MediaType.APPLICATION_JSON)
+   @SelectJson("iplist")
+   @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class)
+   ListenableFuture<Set<IpDetails>> listIps(ListIpOptions... options);
+
+   /**
+    * @see IpClient#getIp
     */
    @GET
    @Path("/ip/details/ipaddress/{ipaddress}/format/json")
-   @Consumes(MediaType.APPLICATION_JSON)
    @SelectJson("details")
+   @Consumes(MediaType.APPLICATION_JSON)
    @ExceptionParser(ReturnNullOnNotFoundOr404.class)
-   ListenableFuture<IpDetails> getIpDetails(@PathParam("ipaddress") String ipAddress);
+   ListenableFuture<IpDetails> getIp(@PathParam("ipaddress") String ipAddress);
+
+   /**
+    * @see IpClient#addIpToServer
+    */
+   @POST
+   @Path("/ip/add/format/json")
+   @SelectJson("details")
+   @Consumes(MediaType.APPLICATION_JSON)
+   ListenableFuture<IpDetails> addIpToServer(@FormParam("ipaddress") String ipAddress,
+                                             @FormParam("serverid") String serverId);
+
+   /**
+    * @see IpClient#removeIpFromServer
+    */
+   @POST
+   @Path("/ip/remove/format/json")
+   @SelectJson("details")
+   @Consumes(MediaType.APPLICATION_JSON)
+   ListenableFuture<IpDetails> removeIpFromServer(@FormParam("ipaddress") String ipAddress,
+                                                  @FormParam("serverid") String serverId);
+
+   /**
+    * @see IpClient#removeIpFromServer
+    */
+   @POST
+   @FormParams(keys = "release", values = "true")
+   @Path("/ip/remove/format/json")
+   @SelectJson("details")
+   @Consumes(MediaType.APPLICATION_JSON)
+   ListenableFuture<IpDetails> removeIpFromServerAndRelease(@FormParam("ipaddress") String ipAddress,
+                                                            @FormParam("serverid") String serverId);
+
+   /**
+    * @see IpClient#setPtr
+    */
+   @POST
+   @Path("/ip/setptr/format/json")
+   @SelectJson("details")
+   @Consumes(MediaType.APPLICATION_JSON)
+   ListenableFuture<IpDetails> setPtr(@FormParam("ipaddress") String ipAddress,
+                                      @FormParam("data") String ptr);
+
+   /**
+    * @see IpClient#resetPtr
+    */
+   @POST
+   @Path("/ip/resetptr/format/json")
+   @SelectJson("details")
+   @Consumes(MediaType.APPLICATION_JSON)
+   ListenableFuture<IpDetails> resetPtr(@FormParam("ipaddress") String ipAddress);
+
 
 }
diff --git a/labs/glesys/src/main/java/org/jclouds/glesys/features/IpClient.java b/labs/glesys/src/main/java/org/jclouds/glesys/features/IpClient.java
index 4335c87..7e0c984 100644
--- a/labs/glesys/src/main/java/org/jclouds/glesys/features/IpClient.java
+++ b/labs/glesys/src/main/java/org/jclouds/glesys/features/IpClient.java
@@ -23,72 +23,100 @@
 
 import org.jclouds.concurrent.Timeout;
 import org.jclouds.glesys.domain.IpDetails;
+import org.jclouds.glesys.options.ListIpOptions;
 
 /**
  * Provides synchronous access to IP Addresses.
  * <p/>
  *
- * @author Adrian Cole, Mattias Holmqvist
+ * @author Adrian Cole, Mattias Holmqvist, Adam Lowe
  * @see IpAsyncClient
  * @see <a href="https://customer.glesys.com/api.php" />
  */
 @Timeout(duration = 30, timeUnit = TimeUnit.SECONDS)
 public interface IpClient {
+   /**
+    * Get a set of all IP addresses that are available and not used on any account or server.
+    *
+    * @param ipVersion  4 or 6, for IPV4 or IPV6, respectively
+    * @param datacenter the datacenter
+    * @param platform   the platform
+    * @return a set of free IP addresses
+    */
+   Set<String> listFree(int ipVersion, String datacenter, String platform);
 
-    /**
-     * Take a free IP address and add it to this account. You can list free IP addresses with the function listFree().
-     * Once your free IP on this account you can add it to a server with the add() function.
-     *
-     * @param ipAddress
-     */
-    void take(String ipAddress);
+   /**
+    * Take a free IP address and add it to this account. You can list free IP addresses with the function listFree().
+    * Once your free IP on this account you can add it to a server with the add() function.
+    *
+    * @param ipAddress the IP address to be add to this account (reserve)
+    */
+   IpDetails take(String ipAddress);
 
-    /**
-     * Return an unused IP address to the pool of free ips. If the IP address is allocated to a server,
-     * it must first be removed by calling remove(ipAddress) before it can be released.
-     *
-     * @param ipAddress the IP address to be released
-     */
-    void release(String ipAddress);
+   /**
+    * Return an unused IP address to the pool of free ips. If the IP address is allocated to a server,
+    * it must first be removed by calling remove(ipAddress) before it can be released.
+    *
+    * @param ipAddress the IP address to be released
+    */
+   IpDetails release(String ipAddress);
 
-    /**
-     * Add an IP address to an server. The IP has to be free, but reserved to this account. You are able to list such addresses
-     * with listOwn() and reserve an address for this account by using take(). To find free ips you can use ip/listfree
-     * ip to an Xen-server you have to configure the server yourself, unless the ip was added during the c
-     * server (server/create). You can get detailed information such as gateway and netmask using the ip
-     *
-     * @param ipAddress the IP address to remove
-     * @param serverId  the server to add the IP address to
-     */
-    void addIpToServer(String ipAddress, String serverId);
+   /**
+    * Get IP addresses associated with your account (reserved, assigned to servers, etc)
+    *
+    * @param options options to filter the results (by IPV4/6, serverId, etc)
+    * @return the set of IP addresses
+    */
+   Set<IpDetails> listIps(ListIpOptions... options);
 
-    /**
-     * Remove an IP address from a server. This does not release it back to GleSYS pool of free ips. The address will be
-     * kept on the account so that you can use it for other servers or the same server at a later time. To completely remove
-     * the IP address from this account, use the function release().
-     *
-     * @param ipAddress the IP address to remove
-     * @param serverId  the server to remove the IP address from
-     */
-    void removeIpFromServer(String ipAddress, String serverId);
+   /**
+    * Get details about the given IP address such as gateway and netmask. Different details are available
+    * on different platforms.
+    *
+    * @param ipAddress the ip address
+    * @return details about the given IP address
+    */
+   IpDetails getIp(String ipAddress);
 
-    /**
-     * Get a set of all IP addresses that are available and not used on any account or server.
-     *
-     * @param ipversion  "4" or "6", for IPV4 or IPV6, respectively
-     * @param datacenter the datacenter
-     * @param platform   the platform
-     * @return a set of free IP addresses
-     */
-    Set<String> listFree(String ipversion, String datacenter, String platform);
+   /**
+    * Add an IP address to an server. The IP has to be free, but reserved to this account. You are able to list such addresses
+    * with listOwn() and reserve an address for this account by using take(). To find free ips you can use ip/listfree
+    * ip to an Xen-server you have to configure the server yourself, unless the ip was added during the c
+    * server (server/create). You can get detailed information such as gateway and netmask using the ip
+    *
+    * @param ipAddress the IP address to remove
+    * @param serverId  the server to add the IP address to
+    */
+   IpDetails addIpToServer(String ipAddress, String serverId);
 
-    /**
-     * Get details about the given IP address such as gateway and netmask. Different details are available
-     * on different platforms.
-     *
-     * @param ipAddress the ip address
-     * @return details about the given IP address
-     */
-    IpDetails getIpDetails(String ipAddress);
+   /**
+    * Remove an IP address from a server. This does not release it back to GleSYS pool of free ips. The address will be
+    * kept on the account so that you can use it for other servers or the same server at a later time. To completely remove
+    * the IP address from this account, use removeIpFromServerAndRelease to do so
+    *
+    * @param ipAddress the IP address to remove
+    * @param serverId  the server to remove the IP address from
+    * @see #removeIpFromServerAndRelease
+    */
+   IpDetails removeIpFromServer(String ipAddress, String serverId);
+
+   /**
+    * Remove an IP address from a server and release it back to GleSYS pool of free ips.
+    *
+    * @param ipAddress the IP address to remove
+    * @param serverId  the server to remove the IP address from
+    * @see #removeIpFromServer
+    */
+   IpDetails removeIpFromServerAndRelease(String ipAddress, String serverId);
+
+   /**
+    * Sets PTR data for an IP. Use ip/listown or ip/details to get current PTR data
+    */
+   IpDetails setPtr(String ipAddress, String ptr);
+
+   /**
+    * Resets PTR data for an IP back to the default value
+    */
+   IpDetails resetPtr(String ipAddress);
 
 }
\ No newline at end of file
diff --git a/labs/glesys/src/main/java/org/jclouds/glesys/features/ServerAsyncClient.java b/labs/glesys/src/main/java/org/jclouds/glesys/features/ServerAsyncClient.java
index bb6942c..0bd400f 100644
--- a/labs/glesys/src/main/java/org/jclouds/glesys/features/ServerAsyncClient.java
+++ b/labs/glesys/src/main/java/org/jclouds/glesys/features/ServerAsyncClient.java
@@ -32,6 +32,7 @@
 import org.jclouds.glesys.domain.AllowedArgumentsForCreateServer;
 import org.jclouds.glesys.domain.Console;
 import org.jclouds.glesys.domain.OSTemplate;
+import org.jclouds.glesys.domain.ResourceUsage;
 import org.jclouds.glesys.domain.Server;
 import org.jclouds.glesys.domain.ServerDetails;
 import org.jclouds.glesys.domain.ServerLimit;
@@ -225,15 +226,19 @@
     * @see ServerClient#resetPassword
     */
    @POST
-   @Path("/server/destroy/format/json")
-   ListenableFuture<Void> resetPassword(@FormParam("serverid") String id, @FormParam("newpassword") String password);
+   @Path("/server/resetpassword/format/json")
+   @SelectJson("server")
+   @Consumes(MediaType.APPLICATION_JSON)
+   ListenableFuture<ServerDetails> resetPassword(@FormParam("serverid") String id, @FormParam("rootpassword") String password);
 
    /**
-    * @see ServerClient#resourceUsage
+    * @see ServerClient#getResourceUsage
     */
    @POST
    @Path("/server/resourceusage/format/json")
-   void resourceUsage(@FormParam("serverid") String id, @FormParam("resource") String resource,
+   @SelectJson("usage")
+   @Consumes(MediaType.APPLICATION_JSON)
+   ListenableFuture<ResourceUsage> getResourceUsage(@FormParam("serverid") String id, @FormParam("resource") String resource,
          @FormParam("resolution") String resolution);
 
 }
diff --git a/labs/glesys/src/main/java/org/jclouds/glesys/features/ServerClient.java b/labs/glesys/src/main/java/org/jclouds/glesys/features/ServerClient.java
index aa21075..ddb6dc0 100644
--- a/labs/glesys/src/main/java/org/jclouds/glesys/features/ServerClient.java
+++ b/labs/glesys/src/main/java/org/jclouds/glesys/features/ServerClient.java
@@ -26,6 +26,7 @@
 import org.jclouds.glesys.domain.AllowedArgumentsForCreateServer;
 import org.jclouds.glesys.domain.Console;
 import org.jclouds.glesys.domain.OSTemplate;
+import org.jclouds.glesys.domain.ResourceUsage;
 import org.jclouds.glesys.domain.Server;
 import org.jclouds.glesys.domain.ServerDetails;
 import org.jclouds.glesys.domain.ServerLimit;
@@ -42,7 +43,7 @@
 /**
  * Provides synchronous access to Server.
  * <p/>
- * 
+ *
  * @author Adrian Cole
  * @author Adam Lowe
  * @see ServerAsyncClient
@@ -53,7 +54,7 @@
 
    /**
     * Get a list of all servers on this account.
-    * 
+    *
     * @return an account's associated server objects.
     */
    Set<Server> listServers();
@@ -62,9 +63,8 @@
     * Get detailed information about a server such as hostname, hardware
     * configuration (cpu, memory and disk), ip addresses, cost, transfer, os and
     * more.
-    * 
-    * @param id
-    *           id of the server
+    *
+    * @param id id of the server
     * @return server or null if not found
     */
    ServerDetails getServerDetails(String id);
@@ -72,11 +72,9 @@
    /**
     * Get detailed information about a server status including up-time and
     * hardware usage (cpu, disk, memory and bandwidth)
-    * 
-    * @param id
-    *           id of the server
-    * @param options
-    *           optional parameters
+    *
+    * @param id      id of the server
+    * @param options optional parameters
     * @return the status of the server or null if not found
     */
    ServerStatus getServerStatus(String id, ServerStatusOptions... options);
@@ -84,87 +82,76 @@
    /**
     * Get detailed information about a server's limits (for OpenVZ only).
     * <p/>
-    * 
-    * @param id
-    *           id of the server
+    *
+    * @param id id of the server
     * @return the requested information about the server or null if not found
     */
    Map<String, ServerLimit> getServerLimits(String id);
 
    /**
     * Get information about how to connect to a server via VNC
-    * 
-    * @param id
-    *           id of the server
+    *
+    * @param id id of the server
     * @return the requested information about the server or null if not found
     */
    Console getConsole(String id);
 
    /**
     * Get information about the OS templates available
-    * 
+    *
     * @return the set of information about each template
     */
    Set<OSTemplate> listTemplates();
 
    /**
     * Get information about valid arguments to #createServer for each platform
-    * 
+    *
     * @return a map of argument lists, keyed on platform
     */
    Map<String, AllowedArgumentsForCreateServer> getAllowedArgumentsForCreateServerByPlatform();
 
    /**
     * Reset the fail count for a server limit (for OpenVZ only).
-    * 
-    * @param id
-    *           id of the server
-    * @param type
-    *           the type of limit to reset
+    *
+    * @param id   id of the server
+    * @param type the type of limit to reset
     */
    Map<String, ServerLimit> resetServerLimit(String id, String type);
 
    /**
     * Reboot a server
-    * 
-    * @param id
-    *           id of the server
+    *
+    * @param id id of the server
     */
    ServerDetails rebootServer(String id);
 
    /**
     * Start a server
-    * 
-    * @param id
-    *           id of the server
+    *
+    * @param id id of the server
     */
    ServerDetails startServer(String id);
 
    /**
     * Stop a server
-    * 
-    * @param id
-    *           id of the server
+    *
+    * @param id id of the server
     */
-   void stopServer(String id);
+   ServerDetails stopServer(String id);
 
    /**
     * hard stop a server
-    * 
-    * @param id
-    *           id of the server
+    *
+    * @param id id of the server
     */
-   void hardStopServer(String id);
+   ServerDetails hardStopServer(String id);
 
    /**
     * Create a new server
-    * 
-    * @param hostname
-    *           the host name of the new server
-    * @param rootPassword
-    *           the root password to use
-    * @param options
-    *           optional settings ex. description
+    *
+    * @param hostname     the host name of the new server
+    * @param rootPassword the root password to use
+    * @param options      optional settings ex. description
     */
    @Timeout(duration = 180, timeUnit = TimeUnit.SECONDS)
    ServerDetails createServerWithHostnameAndRootPassword(ServerSpec serverSpec, String hostname, String rootPassword,
@@ -172,58 +159,47 @@
 
    /**
     * Edit the configuration of a server
-    * 
-    * @param serverid
-    *           the serverId of the server to edit
-    * @param options
-    *           the settings to change
+    *
+    * @param serverid the serverId of the server to edit
+    * @param options  the settings to change
     */
    ServerDetails editServer(String serverid, EditServerOptions... options);
 
    /**
     * Clone a server
-    * 
-    * @param serverid
-    *           the serverId of the server to clone
-    * @param hostname
-    *           the new host name of the cloned server
-    * @param options
-    *           the settings to change
+    *
+    * @param serverid the serverId of the server to clone
+    * @param hostname the new host name of the cloned server
+    * @param options  the settings to change
     */
    @Timeout(duration = 180, timeUnit = TimeUnit.SECONDS)
    ServerDetails cloneServer(String serverid, String hostname, CloneServerOptions... options);
 
    /**
     * Destroy a server
-    * 
-    * @param id
-    *           the id of the server
-    * @param keepIp
-    *           if DestroyServerOptions.keepIp(true) the servers ip will be
-    *           retained for use in your GleSYS account
+    *
+    * @param id     the id of the server
+    * @param keepIp if DestroyServerOptions.keepIp(true) the servers ip will be retained for use in your GleSYS account
     */
    ServerDetails destroyServer(String id, DestroyServerOptions keepIp);
 
    /**
     * Reset the root password of a server
-    * 
-    * @param id
-    *           the id of the server
-    * @param password
-    *           the new password to use
+    *
+    * @param id       the id of the server
+    * @param password the new password to use
     */
-   void resetPassword(String id, String password);
+   ServerDetails resetPassword(String id, String password);
 
    /**
     * Return resource usage over time for server
-    * 
-    * @param id
-    *           the id of the server
-    * @param resource
-    *           the name of the resource to retrieve usage information for
+    *
+    * @param id       the id of the server
+    * @param resource the name of the resource to retrieve usage information for (e.g. "cpuusage")
+    * @param resolution the time-period to extract data for (one of "minute", "hour" or "day)
     */
    @Beta
    // TODO: better name
-   void resourceUsage(String id, String resource, String resolution);
+   ResourceUsage getResourceUsage(String id, String resource, String resolution);
 
 }
\ No newline at end of file
diff --git a/labs/glesys/src/main/java/org/jclouds/glesys/functions/internal/GleSYSTypeAdapters.java b/labs/glesys/src/main/java/org/jclouds/glesys/functions/internal/GleSYSTypeAdapters.java
index 070c9f1..5ba16de 100644
--- a/labs/glesys/src/main/java/org/jclouds/glesys/functions/internal/GleSYSTypeAdapters.java
+++ b/labs/glesys/src/main/java/org/jclouds/glesys/functions/internal/GleSYSTypeAdapters.java
@@ -20,10 +20,13 @@
 
 import java.io.IOException;
 
+import org.jclouds.glesys.domain.GleSYSBoolean;
 import org.jclouds.glesys.domain.Server;
 
+import com.google.common.base.Objects;
 import com.google.gson.TypeAdapter;
 import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonToken;
 import com.google.gson.stream.JsonWriter;
 
 /**
@@ -43,4 +46,24 @@
       }
    }
 
+   public static class GleSYSBooleanAdapter extends TypeAdapter<GleSYSBoolean> {
+
+      @Override
+      public void write(JsonWriter writer, GleSYSBoolean value) throws IOException {
+         writer.value(value.getValue() ? "yes" : "no");
+      }
+
+      @Override
+      public GleSYSBoolean read(JsonReader in) throws IOException {
+         if (in.peek() == JsonToken.BOOLEAN) {
+            return new GleSYSBoolean(in.nextBoolean());
+         } else if (in.peek() == JsonToken.NULL) {
+            return GleSYSBoolean.FALSE;
+         } else {
+            return new GleSYSBoolean(Objects.equal(in.nextString(), "yes"));
+         }
+      }
+
+   }
+
 }
\ No newline at end of file
diff --git a/labs/glesys/src/main/java/org/jclouds/glesys/functions/internal/GlesysDateAdapter.java b/labs/glesys/src/main/java/org/jclouds/glesys/functions/internal/GlesysDateAdapter.java
deleted file mode 100644
index 281b3e8..0000000
--- a/labs/glesys/src/main/java/org/jclouds/glesys/functions/internal/GlesysDateAdapter.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/**
- * Licensed to jclouds, Inc. (jclouds) under one or more
- * contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  jclouds licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.jclouds.glesys.functions.internal;
-
-import java.io.IOException;
-import java.text.DateFormat;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-
-import javax.inject.Singleton;
-
-import org.jclouds.date.DateService;
-import org.jclouds.json.config.GsonModule;
-
-import com.google.common.base.Throwables;
-import com.google.gson.stream.JsonReader;
-import com.google.gson.stream.JsonWriter;
-import com.google.inject.Inject;
-
-/**
- * Parser for Glesys Date formats
- * 
- * @deprecated this should be replaced by standard ISO8601 parser in the next week or so
- * 
- * @author Adam Lowe
- */
-@Singleton
-public class GlesysDateAdapter extends GsonModule.DateAdapter {
-   private final DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
-   private final DateService standardDateService;
-
-   @Inject
-   public GlesysDateAdapter(DateService service) {
-      this.standardDateService = service;
-   }
-
-   public void write(JsonWriter writer, Date value) throws IOException {
-      try {
-         writer.value(standardDateService.iso8601SecondsDateFormat(value));
-      } catch (Exception ex) {
-         synchronized (dateFormat) {
-            writer.value(dateFormat.format(value));
-         }
-      }
-   }
-
-   public Date read(JsonReader reader) throws IOException {
-      String toParse = reader.nextString();
-      try {
-         return standardDateService.iso8601SecondsDateParse(toParse);
-      } catch (Exception ex) {
-         try {
-            synchronized (dateFormat) {
-               return dateFormat.parse(toParse);
-            }
-         } catch (ParseException e) {
-            throw Throwables.propagate(e);
-         }
-      }
-   }
-}
diff --git a/labs/glesys/src/main/java/org/jclouds/glesys/options/AddDomainOptions.java b/labs/glesys/src/main/java/org/jclouds/glesys/options/AddDomainOptions.java
index 473d68b..44c1562 100644
--- a/labs/glesys/src/main/java/org/jclouds/glesys/options/AddDomainOptions.java
+++ b/labs/glesys/src/main/java/org/jclouds/glesys/options/AddDomainOptions.java
@@ -85,7 +85,7 @@
     * Ensure only NS and SOA records will be created by default, when this option is not used a number of default records will be created on the domain.
     */
    public DomainOptions minimalRecords() {
-      formParameters.put("createrecords",  "0");
+      formParameters.put("createrecords", Boolean.FALSE.toString());
       return this;
    }
 
diff --git a/labs/glesys/src/main/java/org/jclouds/glesys/options/CreateAccountOptions.java b/labs/glesys/src/main/java/org/jclouds/glesys/options/CreateAccountOptions.java
index 4a915e0..fa16f63 100644
--- a/labs/glesys/src/main/java/org/jclouds/glesys/options/CreateAccountOptions.java
+++ b/labs/glesys/src/main/java/org/jclouds/glesys/options/CreateAccountOptions.java
@@ -70,19 +70,19 @@
 
    /** Enable or disable virus checking */
    public CreateAccountOptions antiVirus(boolean antiVirus) {
-      formParameters.put("antivirus", Integer.toString(antiVirus ? 1 : 0));
+      formParameters.put("antivirus", Boolean.toString(antiVirus));
       return this;
    }
 
    /** Enable or disable auto-respond */
    public CreateAccountOptions autorespond(boolean autorespond) {
-      formParameters.put("autorespond", Integer.toString(autorespond ? 1 : 0));
+      formParameters.put("autorespond", Boolean.toString(autorespond));
       return this;
    }
 
    /** Enable or disable saving of auto-respond e-mails */
    public CreateAccountOptions autorespondSaveEmail(boolean autorespondSaveEmail) {
-      formParameters.put("autorespondsaveemail", Integer.toString(autorespondSaveEmail ? 1 : 0));
+      formParameters.put("autorespondsaveemail", Boolean.toString(autorespondSaveEmail));
       return this;
    }
 
diff --git a/labs/glesys/src/main/java/org/jclouds/glesys/options/DestroyServerOptions.java b/labs/glesys/src/main/java/org/jclouds/glesys/options/DestroyServerOptions.java
index 61ede83..1f8f562 100644
--- a/labs/glesys/src/main/java/org/jclouds/glesys/options/DestroyServerOptions.java
+++ b/labs/glesys/src/main/java/org/jclouds/glesys/options/DestroyServerOptions.java
@@ -47,7 +47,7 @@
     * @param keepIp if true, keep the ip address
     */
    public DestroyServerOptions keepIp(boolean keepIp) {
-      formParameters.put("keepip", Integer.toString(keepIp ? 1 : 0));
+      formParameters.put("keepip", Boolean.toString(keepIp));
       return this;
    }
 }
\ No newline at end of file
diff --git a/labs/glesys/src/main/java/org/jclouds/glesys/options/ListIpOptions.java b/labs/glesys/src/main/java/org/jclouds/glesys/options/ListIpOptions.java
new file mode 100644
index 0000000..09350e2
--- /dev/null
+++ b/labs/glesys/src/main/java/org/jclouds/glesys/options/ListIpOptions.java
@@ -0,0 +1,106 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.glesys.options;
+
+import org.jclouds.http.options.BaseHttpRequestOptions;
+
+/**
+ * @author Adam Lowe
+ */
+public class ListIpOptions extends BaseHttpRequestOptions {
+
+   public static class Builder {
+      /**
+       * @see org.jclouds.glesys.options.ListIpOptions#used
+       */
+      public static ListIpOptions used(boolean used) {
+         return new ListIpOptions().used(used);
+      }
+
+      /**
+       * @see org.jclouds.glesys.options.ListIpOptions#serverId
+       */
+      public static ListIpOptions serverId(String serverId) {
+         return new ListIpOptions().serverId(serverId);
+      }
+
+      /**
+       * @see org.jclouds.glesys.options.ListIpOptions#ipVersion
+       */
+      public static ListIpOptions ipVersion(int ipVersion) {
+         return new ListIpOptions().ipVersion(ipVersion);
+      }
+
+      /**
+       * @see org.jclouds.glesys.options.ListIpOptions#datacenter
+       */
+      public static ListIpOptions datacenter(String datacenter) {
+         return new ListIpOptions().datacenter(datacenter);
+      }
+
+      /**
+       * @see org.jclouds.glesys.options.ListIpOptions#platform
+       */
+      public static ListIpOptions platform(String platform) {
+         return new ListIpOptions().platform(platform);
+      }
+
+   }
+
+   /**
+    * Retrieve only IPs that are in use
+    */
+   public ListIpOptions used(boolean used) {
+      formParameters.put("used", Boolean.toString(used));
+      return this;
+   }
+
+   /**
+    * Retrieve only IP assigned to the specified server
+    */
+   public ListIpOptions serverId(String serverId) {
+      formParameters.put("serverid", serverId);
+      return this;
+   }
+
+   /**
+    * Retrieve only IPs of the requested version
+    */
+   public ListIpOptions ipVersion(int ipVersion) {
+      formParameters.put("ipversion", Integer.toString(ipVersion));
+      return this;
+   }
+
+   /**
+    * Retrieve only IPs served in the specified datacenter
+    */
+   public ListIpOptions datacenter(String datacenter) {
+      formParameters.put("datacenter", datacenter);
+      return this;
+   }
+
+   /**
+    * Retrieve only IPs served on the specified platform
+    */
+   public ListIpOptions platform(String platform) {
+      formParameters.put("platform", platform);
+      return this;
+   }
+
+}
\ No newline at end of file
diff --git a/labs/glesys/src/test/java/org/jclouds/glesys/GleSYSAsyncClientTest.java b/labs/glesys/src/test/java/org/jclouds/glesys/GleSYSAsyncClientTest.java
index 55872e6..eaab64a 100644
--- a/labs/glesys/src/test/java/org/jclouds/glesys/GleSYSAsyncClientTest.java
+++ b/labs/glesys/src/test/java/org/jclouds/glesys/GleSYSAsyncClientTest.java
@@ -21,8 +21,9 @@
 import java.io.IOException;
 import java.util.concurrent.ExecutionException;
 
-import org.jclouds.glesys.internal.BaseGleSYSAsyncClientTest;
 import org.jclouds.http.HttpRequest;
+import org.jclouds.providers.ProviderMetadata;
+import org.jclouds.rest.internal.BaseAsyncClientTest;
 import org.jclouds.rest.internal.RestAnnotationProcessor;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
@@ -37,11 +38,15 @@
 // NOTE:without testName, this will not call @Before* and fail w/NPE during
 // surefire
 @Test(groups = "unit", testName = "GleSYSAsyncClientTest")
-public class GleSYSAsyncClientTest extends BaseGleSYSAsyncClientTest<GleSYSAsyncClient> {
-
+public class GleSYSAsyncClientTest extends BaseAsyncClientTest<GleSYSAsyncClient> {
    private GleSYSAsyncClient asyncClient;
    private GleSYSClient syncClient;
 
+   @Override
+   public ProviderMetadata createProviderMetadata() {
+      return new GleSYSProviderMetadata();
+   }
+   
    public void testSync() throws SecurityException, NoSuchMethodException, InterruptedException, ExecutionException {
       assert syncClient.getServerClient() != null;
       assert syncClient.getIpClient() != null;
diff --git a/labs/glesys/src/test/java/org/jclouds/glesys/compute/functions/ServerDetailsToNodeMetadataTest.java b/labs/glesys/src/test/java/org/jclouds/glesys/compute/functions/ServerDetailsToNodeMetadataTest.java
index 516283c..1e77c01 100644
--- a/labs/glesys/src/test/java/org/jclouds/glesys/compute/functions/ServerDetailsToNodeMetadataTest.java
+++ b/labs/glesys/src/test/java/org/jclouds/glesys/compute/functions/ServerDetailsToNodeMetadataTest.java
@@ -20,10 +20,12 @@
 
 import static org.jclouds.io.Payloads.newUrlEncodedFormPayload;
 import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
 
 import java.net.URI;
 
 import org.jclouds.compute.domain.HardwareBuilder;
+import org.jclouds.compute.domain.NodeMetadata;
 import org.jclouds.compute.domain.NodeMetadataBuilder;
 import org.jclouds.compute.domain.OperatingSystem;
 import org.jclouds.compute.domain.OsFamily;
@@ -73,22 +75,25 @@
 
       ).getInstance(ServerDetailsToNodeMetadata.class);
 
+      NodeMetadata actual = toTest.apply(ServerClientExpectTest.expectedServerDetails());
+      assertNotNull(actual);
+
       assertEquals(
-            toTest.apply(ServerClientExpectTest.expectedServerDetails()),
+            actual.toString(),
             new NodeMetadataBuilder()
-                  .ids("xm3276891")
-                  .name("glesys-s-6dd")
-                  .hostname("glesys-s-6dd")
+                  .ids("vz1840356")
+                  .name("test-email-jclouds")
+                  .hostname("test-email-jclouds")
                   .group("glesys-s")
-                  .imageId("Ubuntu 11.04 x64")
+                  .imageId("Ubuntu 10.04 LTS 32-bit")
                   .operatingSystem(
-                        OperatingSystem.builder().name("Ubuntu 11.04 x64").family(OsFamily.UBUNTU).version("11.04")
-                              .is64Bit(true).description("Ubuntu 11.04 x64").build())
-                  .publicAddresses(ImmutableSet.of("109.74.10.45"))
+                        OperatingSystem.builder().name("Ubuntu 10.04 LTS 32-bit").family(OsFamily.UBUNTU).version("10.04")
+                              .is64Bit(false).description("Ubuntu 10.04 LTS 32-bit").build())
+                  .publicAddresses(ImmutableSet.of("31.192.231.254"))
                   .hardware(
-                        new HardwareBuilder().ids("xm3276891").ram(512)
+                        new HardwareBuilder().ids("vz1840356").ram(512)
                               .processors(ImmutableList.of(new Processor(1, 1.0)))
-                              .volumes(ImmutableList.<Volume> of(new VolumeImpl(5f, true, true))).hypervisor("Xen")
-                              .build()).status(Status.RUNNING).build());
+                              .volumes(ImmutableList.<Volume> of(new VolumeImpl(5f, true, true))).hypervisor("OpenVZ")
+                              .build()).status(Status.RUNNING).build().toString());
    }
 }
diff --git a/labs/glesys/src/test/java/org/jclouds/glesys/features/ArchiveClientExpectTest.java b/labs/glesys/src/test/java/org/jclouds/glesys/features/ArchiveClientExpectTest.java
index b7d165f..a7d2c74 100644
--- a/labs/glesys/src/test/java/org/jclouds/glesys/features/ArchiveClientExpectTest.java
+++ b/labs/glesys/src/test/java/org/jclouds/glesys/features/ArchiveClientExpectTest.java
@@ -26,15 +26,13 @@
 import java.net.URI;
 import java.util.List;
 
-import org.jclouds.glesys.GleSYSClient;
 import org.jclouds.glesys.domain.Archive;
 import org.jclouds.glesys.domain.ArchiveAllowedArguments;
-import org.jclouds.glesys.domain.ArchiveDetails;
+import org.jclouds.glesys.internal.BaseGleSYSClientExpectTest;
 import org.jclouds.http.HttpRequest;
 import org.jclouds.http.HttpResponse;
 import org.jclouds.http.HttpResponseException;
 import org.jclouds.rest.ResourceNotFoundException;
-import org.jclouds.rest.internal.BaseRestClientExpectTest;
 import org.testng.annotations.Test;
 
 import com.google.common.collect.ImmutableList;
@@ -46,10 +44,7 @@
  * @author Adam Lowe
  */
 @Test(groups = "unit", testName = "ArchiveClientExpectTest")
-public class ArchiveClientExpectTest extends BaseRestClientExpectTest<GleSYSClient> {
-   public ArchiveClientExpectTest() {
-      provider = "glesys";
-   }
+public class ArchiveClientExpectTest extends BaseGleSYSClientExpectTest {
 
    public void testListArchivesWhenReponseIs2xx() throws Exception {
       ArchiveClient client = requestSendsResponse(
@@ -61,8 +56,8 @@
             HttpResponse.builder().statusCode(200).payload(payloadFromResource("/archive_list.json")).build())
             .getArchiveClient();
 
-      List<Archive> expected = ImmutableList.<Archive>of(
-            Archive.builder().username("xxxxx_test1").freeSize("20 GB").totalSize("20 GB").locked(false).build());
+      List<Archive> expected = ImmutableList.of(
+            Archive.builder().username("xxxxx_test1").freeSize("20 GB").totalSize("30 GB").locked(false).build());
 
       assertEquals(client.listArchives(), expected);
    }
@@ -88,9 +83,12 @@
                         ImmutableMultimap.<String, String>builder().put("username", "xxxxxx_test1").build())).build(),
             HttpResponse.builder().statusCode(200).payload(payloadFromResource("/archive_details.json")).build())
             .getArchiveClient();
-      ArchiveDetails expected = ArchiveDetails.builder().username("xxxxxx_test1").freeSize("30 GB").totalSize("30 GB").locked(false).build();
 
-      assertEquals(client.getArchiveDetails("xxxxxx_test1"), expected);
+      assertEquals(client.getArchive("xxxxxx_test1"), detailsInArchiveDetails());
+   }
+
+   private Archive detailsInArchiveDetails() {
+      return Archive.builder().username("xxxxxx_test1").freeSize("30 GB").totalSize("30 GB").locked(false).build();
    }
 
    public void testArchiveDetailsWhenResponseIs4xxReturnsNull() {
@@ -103,20 +101,21 @@
                         ImmutableMultimap.<String, String>builder().put("username", "xxxxxx_test1").build())).build(),
             HttpResponse.builder().statusCode(404).build())
             .getArchiveClient();
-      assertNull(client.getArchiveDetails("xxxxxx_test1"));
+      assertNull(client.getArchive("xxxxxx_test1"));
    }
 
    public void testCreateArchiveWhenResponseIs2xx() throws Exception {
       ArchiveClient client = requestSendsResponse(
             HttpRequest.builder().method("POST").endpoint(URI.create("https://api.glesys.com/archive/create/format/json"))
-                  .headers(ImmutableMultimap.<String, String>builder().put(
-                        "Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
+                  .headers(ImmutableMultimap.<String, String>builder()
+                        .put("Accept", "application/json")
+                        .put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
                   .payload(newUrlEncodedFormPayload(ImmutableMultimap.<String, String>builder()
                         .put("username", "xxxxxx_test1")
                         .put("size", "5")
                         .put("password", "somepass").build())).build(),
-            HttpResponse.builder().statusCode(200).build()).getArchiveClient();
-      client.createArchive("xxxxxx_test1", "somepass", 5);
+            HttpResponse.builder().statusCode(200).payload(payloadFromResource("/archive_details.json")).build()).getArchiveClient();
+      assertEquals(client.createArchive("xxxxxx_test1", "somepass", 5), detailsInArchiveDetails());
    }
 
    public void testDeleteArchiveWhenResponseIs2xx() throws Exception {
@@ -146,22 +145,24 @@
    public void testResizeArchiveWhenResponseIs2xx() throws Exception {
       ArchiveClient client = requestSendsResponse(
             HttpRequest.builder().method("POST").endpoint(URI.create("https://api.glesys.com/archive/resize/format/json"))
-                  .headers(ImmutableMultimap.<String, String>builder().put(
-                        "Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
+                  .headers(ImmutableMultimap.<String, String>builder()
+                        .put("Accept", "application/json")
+                        .put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
                   .payload(newUrlEncodedFormPayload(ImmutableMultimap.<String, String>builder()
                         .put("username", "username1")
                         .put("size", "5").build())).build(),
-            HttpResponse.builder().statusCode(200).build()).getArchiveClient();
+            HttpResponse.builder().statusCode(200).payload(payloadFromResource("/archive_details.json")).build()).getArchiveClient();
 
-      client.resizeArchive("username1", 5);
+      assertEquals(client.resizeArchive("username1", 5), detailsInArchiveDetails());
    }
 
    @Test(expectedExceptions = {ResourceNotFoundException.class})
    public void testResizeArchiveWhenResponseIs4xxThrows() throws Exception {
       ArchiveClient client = requestSendsResponse(
             HttpRequest.builder().method("POST").endpoint(URI.create("https://api.glesys.com/archive/resize/format/json"))
-                  .headers(ImmutableMultimap.<String, String>builder().put(
-                        "Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
+                  .headers(ImmutableMultimap.<String, String>builder()
+                        .put("Accept", "application/json")
+                        .put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
                   .payload(newUrlEncodedFormPayload(ImmutableMultimap.<String, String>builder()
                         .put("username", "username1")
                         .put("size", "5").build())).build(),
@@ -174,14 +175,15 @@
       ArchiveClient client = requestSendsResponse(
             HttpRequest.builder().method("POST")
                   .endpoint(URI.create("https://api.glesys.com/archive/changepassword/format/json"))
-                  .headers(ImmutableMultimap.<String, String>builder().put(
-                        "Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
+                  .headers(ImmutableMultimap.<String, String>builder()
+                        .put("Accept", "application/json")
+                        .put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
                   .payload(newUrlEncodedFormPayload(ImmutableMultimap.<String, String>builder()
                         .put("username", "username")
                         .put("password", "newpass").build())).build(),
-            HttpResponse.builder().statusCode(200).build()).getArchiveClient();
-
-      client.changeArchivePassword("username", "newpass");
+            HttpResponse.builder().statusCode(200).payload(payloadFromResource("/archive_details.json")).build()).getArchiveClient();
+      
+      assertEquals(client.changeArchivePassword("username", "newpass"), detailsInArchiveDetails());
    }
 
    @Test(expectedExceptions = {ResourceNotFoundException.class})
@@ -189,8 +191,9 @@
       ArchiveClient client = requestSendsResponse(
             HttpRequest.builder().method("POST")
                   .endpoint(URI.create("https://api.glesys.com/archive/changepassword/format/json"))
-                  .headers(ImmutableMultimap.<String, String>builder().put(
-                        "Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
+                  .headers(ImmutableMultimap.<String, String>builder()
+                        .put("Accept", "application/json")
+                        .put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
                   .payload(newUrlEncodedFormPayload(ImmutableMultimap.<String, String>builder()
                         .put("username", "username")
                         .put("password", "newpass").build())).build(),
diff --git a/labs/glesys/src/test/java/org/jclouds/glesys/features/ArchiveClientLiveTest.java b/labs/glesys/src/test/java/org/jclouds/glesys/features/ArchiveClientLiveTest.java
index e71ca77..e0d843f 100644
--- a/labs/glesys/src/test/java/org/jclouds/glesys/features/ArchiveClientLiveTest.java
+++ b/labs/glesys/src/test/java/org/jclouds/glesys/features/ArchiveClientLiveTest.java
@@ -24,8 +24,8 @@
 
 import java.util.concurrent.TimeUnit;
 
+import org.jclouds.glesys.domain.Archive;
 import org.jclouds.glesys.domain.ArchiveAllowedArguments;
-import org.jclouds.glesys.domain.ArchiveDetails;
 import org.jclouds.glesys.internal.BaseGleSYSClientLiveTest;
 import org.jclouds.predicates.RetryablePredicate;
 import org.testng.annotations.AfterClass;
@@ -47,7 +47,7 @@
       super.setupContext();
       
       client = gleContext.getApi().getArchiveClient();
-      archiveUser = gleContext.getIdentity().toLowerCase() + "_jcloudstest9";
+      archiveUser = gleContext.getIdentity().toLowerCase() + "_test9";
       archiveCounter = new RetryablePredicate<Integer>(
             new Predicate<Integer>() {
                public boolean apply(Integer value){
@@ -97,10 +97,8 @@
 
    @Test(dependsOnMethods = "testCreateArchive")
    public void testArchiveDetails() throws Exception {
-      ArchiveDetails details = client.getArchiveDetails(archiveUser);
+      Archive details = client.getArchive(archiveUser);
       assertEquals(details.getUsername(), archiveUser);
-      assertNotNull(details.getFreeSize());
-      assertNotNull(details.getTotalSize());
    }
 
    @Test(dependsOnMethods = "testCreateArchive")
@@ -116,7 +114,7 @@
       assertTrue(new RetryablePredicate<String>(
             new Predicate<String>() {
                public boolean apply(String value){
-                  return client.getArchiveDetails(archiveUser) != null && value.equals(client.getArchiveDetails(archiveUser).getTotalSize());
+                  return client.getArchive(archiveUser) != null && value.equals(client.getArchive(archiveUser).getTotalSize());
                }
             }, 30, 1, TimeUnit.SECONDS).apply("20 GB"));
    }
diff --git a/labs/glesys/src/test/java/org/jclouds/glesys/features/DomainClientExpectTest.java b/labs/glesys/src/test/java/org/jclouds/glesys/features/DomainClientExpectTest.java
index f03e3ad..b348ba7 100644
--- a/labs/glesys/src/test/java/org/jclouds/glesys/features/DomainClientExpectTest.java
+++ b/labs/glesys/src/test/java/org/jclouds/glesys/features/DomainClientExpectTest.java
@@ -20,17 +20,22 @@
 
 import static org.jclouds.io.Payloads.newUrlEncodedFormPayload;
 import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNull;
 import static org.testng.Assert.assertTrue;
 
 import java.net.URI;
 import java.util.Set;
 
+import javax.ws.rs.core.MediaType;
+
 import org.jclouds.date.DateService;
 import org.jclouds.date.internal.SimpleDateFormatDateService;
 import org.jclouds.glesys.GleSYSClient;
 import org.jclouds.glesys.domain.Domain;
 import org.jclouds.glesys.domain.DomainRecord;
+import org.jclouds.glesys.internal.BaseGleSYSClientExpectTest;
 import org.jclouds.glesys.options.AddDomainOptions;
+import org.jclouds.glesys.options.EditRecordOptions;
 import org.jclouds.http.HttpRequest;
 import org.jclouds.http.HttpResponse;
 import org.jclouds.rest.ResourceNotFoundException;
@@ -38,7 +43,7 @@
 import org.testng.annotations.Test;
 
 import com.google.common.collect.ImmutableMultimap;
-import com.google.common.collect.ImmutableSortedSet;
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
 
 /**
@@ -47,13 +52,8 @@
  * @author Adam Lowe
  */
 @Test(groups = "unit", testName = "DomainClientExpectTest")
-public class DomainClientExpectTest extends BaseRestClientExpectTest<GleSYSClient> {
-   private DateService dateService = new SimpleDateFormatDateService();
+public class DomainClientExpectTest extends BaseGleSYSClientExpectTest {
    
-   public DomainClientExpectTest() {
-      provider = "glesys";
-   }
-
    public void testListDomainsWhenResponseIs2xx() throws Exception {
       DomainClient client = requestSendsResponse(
             HttpRequest.builder().method("POST").endpoint(URI.create("https://api.glesys.com/domain/list/format/json"))
@@ -93,7 +93,7 @@
                         .put("domainname", "testglesys.jclouds.org").build())).build(),
             HttpResponse.builder().statusCode(200).payload(payloadFromResource("/domain_list_records.json")).build()).getDomainClient();
 
-      Set<DomainRecord> expected = ImmutableSortedSet.of(
+      Set<DomainRecord> expected = ImmutableSet.of(
             DomainRecord.builder().id("224538").domainname("testglesys.jclouds.org").host("@").type("NS").data("ns1.namesystem.se.").ttl(3600).build(),
             DomainRecord.builder().id("224539").domainname("testglesys.jclouds.org").host("@").type("NS").data("ns2.namesystem.se.").ttl(3600).build(),
             DomainRecord.builder().id("224540").domainname("testglesys.jclouds.org").host("@").type("NS").data("ns3.namesystem.se.").ttl(3600).build(),
@@ -104,13 +104,13 @@
             DomainRecord.builder().id("224545").domainname("testglesys.jclouds.org").host("@").type("MX").data("20 mx02.glesys.se.").ttl(3600).build(),
             DomainRecord.builder().id("224546").domainname("testglesys.jclouds.org").host("@").type("TXT").data("v=spf1 include:spf.glesys.se -all").ttl(3600).build()
       );
-      
+
       Set<DomainRecord> actual = client.listRecords("testglesys.jclouds.org");
-      
+
       assertEquals(actual, expected);
 
-      for(DomainRecord result : actual) {
-         for(DomainRecord expect : expected) {
+      for (DomainRecord result : actual) {
+         for (DomainRecord expect : expected) {
             if (result.equals(expect)) {
                assertEquals(result.toString(), expect.toString(), "Deep comparison using toString() failed!");
             }
@@ -130,24 +130,150 @@
       assertTrue(client.listDomains().isEmpty());
    }
 
+   public void testAddDomainRecordsWhenResponseIs2xx() throws Exception {
+      DomainClient client = requestSendsResponse(
+            HttpRequest.builder().method("POST").endpoint(URI.create("https://api.glesys.com/domain/addrecord/format/json"))
+                  .headers(ImmutableMultimap.<String, String>builder()
+                        .put("Accept", "application/json")
+                        .put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
+                  .payload(payloadFromStringWithContentType("domainname=jclouds.org&type=A&host=jclouds.org&data=", MediaType.APPLICATION_FORM_URLENCODED))
+                  .build(),
+            HttpResponse.builder().statusCode(200)
+                  .payload(payloadFromResourceWithContentType("/domain_record.json", MediaType.APPLICATION_JSON)).build())
+            .getDomainClient();
+
+      assertEquals(client.addRecord("jclouds.org", "jclouds.org", "A", ""), recordInDomainRecord());
+   }
+
+   protected DomainRecord recordInDomainRecord() {
+      return DomainRecord.builder().id("256151").domainname("cl13016-domain.jclouds.org").host("test").type("A").data("127.0.0.1").ttl(3600).build();
+   }
+
+   @Test(expectedExceptions = ResourceNotFoundException.class)
+   public void testAddDomainRecordsWhenResponseIs4xx() throws Exception {
+      DomainClient client = requestSendsResponse(
+            HttpRequest.builder().method("POST").endpoint(URI.create("https://api.glesys.com/domain/addrecord/format/json"))
+                  .headers(ImmutableMultimap.<String, String>builder()
+                        .put("Accept", "application/json")
+                        .put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
+                  .payload(payloadFromStringWithContentType("domainname=jclouds.org&type=A&host=jclouds.org&data=", MediaType.APPLICATION_FORM_URLENCODED))
+                  .build(),
+            HttpResponse.builder().statusCode(404).build()).getDomainClient();
+
+      client.addRecord("jclouds.org", "jclouds.org", "A", "");
+   }
+
+   public void testEditDomainRecordsWhenResponseIs2xx() throws Exception {
+      DomainClient client = requestSendsResponse(
+            HttpRequest.builder().method("POST").endpoint(URI.create("https://api.glesys.com/domain/updaterecord/format/json"))
+                  .headers(ImmutableMultimap.<String, String>builder()
+                        .put("Accept", "application/json")
+                        .put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
+                  .payload(payloadFromStringWithContentType("recordid=256151&host=somehost&ttl=1800", MediaType.APPLICATION_FORM_URLENCODED))
+                  .build(),
+            HttpResponse.builder().statusCode(200)
+                  .payload(payloadFromResourceWithContentType("/domain_record.json", MediaType.APPLICATION_JSON)).build())
+            .getDomainClient();
+
+      assertEquals(client.editRecord("256151", EditRecordOptions.Builder.host("somehost"), EditRecordOptions.Builder.ttl(1800)), recordInDomainRecord());
+   }
+
+   @Test(expectedExceptions = ResourceNotFoundException.class)
+   public void testEditDomainRecordsWhenResponseIs4xx() throws Exception {
+      DomainClient client = requestSendsResponse(
+            HttpRequest.builder().method("POST").endpoint(URI.create("https://api.glesys.com/domain/updaterecord/format/json"))
+                  .headers(ImmutableMultimap.<String, String>builder()
+                        .put("Accept", "application/json")
+                        .put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
+                  .payload(payloadFromStringWithContentType("recordid=256151&host=somehost&ttl=1800", MediaType.APPLICATION_FORM_URLENCODED))
+                  .build(),
+            HttpResponse.builder().statusCode(404).build()).getDomainClient();
+
+      client.editRecord("256151", EditRecordOptions.Builder.host("somehost"), EditRecordOptions.Builder.ttl(1800));
+   }
+
+   public void testDeleteDomainRecordsWhenResponseIs2xx() throws Exception {
+      DomainClient client = requestSendsResponse(
+            HttpRequest.builder().method("POST").endpoint(URI.create("https://api.glesys.com/domain/deleterecord/format/json"))
+                  .headers(ImmutableMultimap.<String, String>builder()
+                        .put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
+                  .payload(payloadFromStringWithContentType("recordid=256151", MediaType.APPLICATION_FORM_URLENCODED))
+                  .build(),
+            HttpResponse.builder().statusCode(200)
+                  .payload(payloadFromResourceWithContentType("/domain_record.json", MediaType.APPLICATION_JSON)).build())
+            .getDomainClient();
+
+      client.deleteRecord("256151");
+   }
+
+   @Test(expectedExceptions = ResourceNotFoundException.class)
+   public void testDeleteDomainRecordsWhenResponseIs4xx() throws Exception {
+      DomainClient client = requestSendsResponse(
+            HttpRequest.builder().method("POST").endpoint(URI.create("https://api.glesys.com/domain/deleterecord/format/json"))
+                  .headers(ImmutableMultimap.<String, String>builder()
+                        .put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
+                  .payload(payloadFromStringWithContentType("recordid=256151", MediaType.APPLICATION_FORM_URLENCODED))
+                  .build(),
+            HttpResponse.builder().statusCode(404).build()).getDomainClient();
+
+      client.deleteRecord("256151");
+   }
+
+   public void testGetDomainWhenResponseIs2xx() throws Exception {
+      DomainClient client = requestSendsResponse(
+            HttpRequest.builder().method("POST").endpoint(URI.create("https://api.glesys.com/domain/details/format/json"))
+                  .headers(ImmutableMultimap.<String, String>builder()
+                        .put("Accept", "application/json")
+                        .put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
+                  .payload(newUrlEncodedFormPayload(ImmutableMultimap.<String, String>builder()
+                        .put("domainname", "cl66666_x").build())).build(),
+            HttpResponse.builder().statusCode(200)
+                  .payload(payloadFromResourceWithContentType("/domain_details.json", MediaType.APPLICATION_JSON)).build())
+            .getDomainClient();
+
+      assertEquals(client.getDomain("cl66666_x"), domainInDomainDetails());
+   }
+
+
+   public void testGetDomainWhenResponseIs4xx() throws Exception {
+      DomainClient client = requestSendsResponse(
+            HttpRequest.builder().method("POST").endpoint(URI.create("https://api.glesys.com/domain/details/format/json"))
+                  .headers(ImmutableMultimap.<String, String>builder()
+                        .put("Accept", "application/json")
+                        .put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
+                  .payload(newUrlEncodedFormPayload(ImmutableMultimap.<String, String>builder()
+                        .put("domainname", "cl66666_x").build())).build(),
+            HttpResponse.builder().statusCode(404).build())
+            .getDomainClient();
+
+      assertNull(client.getDomain("cl66666_x"));
+   }
+
+   protected Domain domainInDomainDetails() {
+      return Domain.builder().domainName("cl13016-domain.jclouds.org").createTime(dateService.iso8601SecondsDateParse("2012-06-24T11:52:49+02:00")).recordCount(9).build();
+   }
+
    public void testAddDomainWhenResponseIs2xx() throws Exception {
       DomainClient client = requestSendsResponse(
             HttpRequest.builder().method("POST").endpoint(URI.create("https://api.glesys.com/domain/add/format/json"))
-                  .headers(ImmutableMultimap.<String, String>builder().put(
-                        "Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
+                  .headers(ImmutableMultimap.<String, String>builder()
+                        .put("Accept", "application/json")
+                        .put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
                   .payload(newUrlEncodedFormPayload(ImmutableMultimap.<String, String>builder()
                         .put("domainname", "cl66666_x").build())).build(),
-            HttpResponse.builder().statusCode(200).build()).getDomainClient();
+            HttpResponse.builder().statusCode(200)
+                  .payload(payloadFromResourceWithContentType("/domain_details.json", MediaType.APPLICATION_JSON)).build())
+            .getDomainClient();
 
-      client.addDomain("cl66666_x");
+      assertEquals(client.addDomain("cl66666_x"), domainInDomainDetails());
    }
 
-
    public void testAddDomainWithOptsWhenResponseIs2xx() throws Exception {
       DomainClient client = requestSendsResponse(
             HttpRequest.builder().method("POST").endpoint(URI.create("https://api.glesys.com/domain/add/format/json"))
-                  .headers(ImmutableMultimap.<String, String>builder().put(
-                        "Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
+                  .headers(ImmutableMultimap.<String, String>builder()
+                        .put("Accept", "application/json")
+                        .put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
                   .payload(newUrlEncodedFormPayload(ImmutableMultimap.<String, String>builder()
                         .put("domainname", "cl66666_x")
                         .put("primarynameserver", "ns1.somewhere.x")
@@ -158,31 +284,37 @@
                         .put("retry", "1")
                         .put("ttl", "1")
                         .build())).build(),
-            HttpResponse.builder().statusCode(200).build()).getDomainClient();
+            HttpResponse.builder().statusCode(200)
+                  .payload(payloadFromResourceWithContentType("/domain_details.json", MediaType.APPLICATION_JSON)).build())
+            .getDomainClient();
       AddDomainOptions options = (AddDomainOptions) AddDomainOptions.Builder.primaryNameServer("ns1.somewhere.x")
             .expire(1).minimum(1).refresh(1).responsiblePerson("Tester").retry(1).ttl(1);
 
-      client.addDomain("cl66666_x", options);
+      assertEquals(client.addDomain("cl66666_x", options), domainInDomainDetails());
    }
 
    public void testEditDomainWhenResponseIs2xx() throws Exception {
       DomainClient client = requestSendsResponse(
             HttpRequest.builder().method("POST").endpoint(URI.create("https://api.glesys.com/domain/edit/format/json"))
-                  .headers(ImmutableMultimap.<String, String>builder().put(
-                        "Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
+                  .headers(ImmutableMultimap.<String, String>builder()
+                        .put("Accept", "application/json")
+                        .put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
                   .payload(newUrlEncodedFormPayload(ImmutableMultimap.<String, String>builder()
                         .put("domainname", "x").build())).build(),
-            HttpResponse.builder().statusCode(200).build()).getDomainClient();
+            HttpResponse.builder().statusCode(200)
+                  .payload(payloadFromResourceWithContentType("/domain_details.json", MediaType.APPLICATION_JSON)).build())
+            .getDomainClient();
 
-      client.editDomain("x");
+      assertEquals(client.editDomain("x"), domainInDomainDetails());
    }
 
    @Test(expectedExceptions = {ResourceNotFoundException.class})
    public void testEditDomainWhenResponseIs4xxThrows() throws Exception {
       DomainClient client = requestSendsResponse(
             HttpRequest.builder().method("POST").endpoint(URI.create("https://api.glesys.com/domain/edit/format/json"))
-                  .headers(ImmutableMultimap.<String, String>builder().put(
-                        "Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
+                  .headers(ImmutableMultimap.<String, String>builder()
+                        .put("Accept", "application/json")
+                        .put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
                   .payload(newUrlEncodedFormPayload(ImmutableMultimap.<String, String>builder()
                         .put("domainname", "x").build())).build(),
             HttpResponse.builder().statusCode(404).build()).getDomainClient();
diff --git a/labs/glesys/src/test/java/org/jclouds/glesys/features/DomainClientLiveTest.java b/labs/glesys/src/test/java/org/jclouds/glesys/features/DomainClientLiveTest.java
index 78ec757..7592289 100644
--- a/labs/glesys/src/test/java/org/jclouds/glesys/features/DomainClientLiveTest.java
+++ b/labs/glesys/src/test/java/org/jclouds/glesys/features/DomainClientLiveTest.java
@@ -44,12 +44,12 @@
  */
 @Test(groups = "live", testName = "DomainClientLiveTest", singleThreaded = true)
 public class DomainClientLiveTest extends BaseGleSYSClientLiveTest {
-   public final String testDomain = "glesystest.jclouds.org";
+   public String testDomain;
 
    @BeforeGroups(groups = {"live"})
    public void setupContext() {
       super.setupContext();
-
+      testDomain =  identity.toLowerCase() + "-domain.jclouds.org";
       client = gleContext.getApi().getDomainClient();
       domainCounter = new RetryablePredicate<Integer>(
             new Predicate<Integer>() {
@@ -86,9 +86,18 @@
    private RetryablePredicate<Integer> recordCounter;
 
    @Test
+   public void testGetDomain() throws Exception {
+      Domain domain = client.getDomain(testDomain);
+      assertNotNull(domain);
+      assertEquals(domain.getDomainName(), testDomain);
+      assertNotNull(domain.getCreateTime());
+   }
+   
+   @Test
    public void testEditDomain() throws Exception {
-      client.editDomain(testDomain, DomainOptions.Builder.responsiblePerson("tester.jclouds.org"));
-      assertTrue(client.listDomains().contains(Domain.builder().domainName(testDomain).build()));
+      client.editDomain(testDomain, DomainOptions.Builder.responsiblePerson("another-tester.jclouds.org."));
+      Domain domain = client.getDomain(testDomain);
+      assertEquals(domain.getResponsiblePerson(), "another-tester.jclouds.org.");
    }
 
    @Test
diff --git a/labs/glesys/src/test/java/org/jclouds/glesys/features/EmailClientExpectTest.java b/labs/glesys/src/test/java/org/jclouds/glesys/features/EmailClientExpectTest.java
index c27bf7d..0bbba36 100644
--- a/labs/glesys/src/test/java/org/jclouds/glesys/features/EmailClientExpectTest.java
+++ b/labs/glesys/src/test/java/org/jclouds/glesys/features/EmailClientExpectTest.java
@@ -24,24 +24,27 @@
 import static org.testng.Assert.assertTrue;
 
 import java.net.URI;
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
 import java.util.Set;
 
-import org.jclouds.glesys.GleSYSClient;
+import javax.ws.rs.core.MediaType;
+
 import org.jclouds.glesys.domain.EmailAccount;
+import org.jclouds.glesys.domain.EmailAlias;
 import org.jclouds.glesys.domain.EmailOverview;
 import org.jclouds.glesys.domain.EmailOverviewDomain;
 import org.jclouds.glesys.domain.EmailOverviewSummary;
+import org.jclouds.glesys.domain.EmailQuota;
+import org.jclouds.glesys.internal.BaseGleSYSClientExpectTest;
+import org.jclouds.glesys.options.EditAccountOptions;
 import org.jclouds.http.HttpRequest;
 import org.jclouds.http.HttpResponse;
 import org.jclouds.rest.AuthorizationException;
 import org.jclouds.rest.ResourceNotFoundException;
-import org.jclouds.rest.internal.BaseRestClientExpectTest;
 import org.testng.annotations.Test;
 
 import com.google.common.collect.ImmutableMultimap;
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
 
 /**
  * Tests annotation parsing of {@code EmailClient}
@@ -49,10 +52,7 @@
  * @author Adam Lowe
  */
 @Test(groups = "unit", testName = "EmailAsyncClientTest")
-public class EmailClientExpectTest extends BaseRestClientExpectTest<GleSYSClient> {
-   public EmailClientExpectTest() {
-      provider = "glesys";
-   }
+public class EmailClientExpectTest extends BaseGleSYSClientExpectTest {
 
    public void testListWhenResponseIs2xx() throws Exception {
       EmailClient client = requestSendsResponse(
@@ -61,18 +61,22 @@
                         .put("Accept", "application/json")
                         .put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
                   .payload(newUrlEncodedFormPayload(
-                        ImmutableMultimap.<String, String>builder().put("domain", "test").build())).build(),
+                        ImmutableMultimap.<String, String>builder().put("domainname", "cl13016.test.jclouds.org").build())).build(),
             HttpResponse.builder().statusCode(200).payload(payloadFromResource("/email_list.json")).build()).getEmailClient();
 
-      DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
-      EmailAccount.Builder builder = EmailAccount.builder().quota("200 MB").usedQuota("0 MB").antispamLevel(3).antiVirus(true).autoRespond(false).autoRespondSaveEmail(true).autoRespondMessage("false");
+      EmailAccount.Builder builder = EmailAccount.builder().quota(EmailQuota.builder().max(200).unit("MB").build()).antispamLevel(3).antiVirus(true).autoRespond(false).autoRespondSaveEmail(true);
       Set<EmailAccount> expected =
             ImmutableSet.of(
-                  builder.account("test@adamlowe.net").created(dateFormat.parse("2011-12-22T12:13:14")).modified(dateFormat.parse("2011-12-22T12:13:35")).build(),
-                  builder.account("test2@adamlowe.net").created(dateFormat.parse("2011-12-22T12:14:29")).modified(dateFormat.parse("2011-12-22T12:14:31")).build()
+                  builder.account("test1@cl13016.test.jclouds.org").antispamLevel(3)
+                        .created(dateService.iso8601SecondsDateParse("2012-06-24T11:53:45+02:00")).build(),
+                  builder.account("test@cl13016.test.jclouds.org").antispamLevel(3)
+                        .created(dateService.iso8601SecondsDateParse("2012-06-21T11:26:09+02:00"))
+                        .modified(dateService.iso8601SecondsDateParse("2012-06-24T11:53:48+02:00")).build()
             );
 
-      assertEquals(client.listAccounts("test"), expected);
+      Set<EmailAccount> actual = client.listAccounts("cl13016.test.jclouds.org");
+      assertEquals(actual, expected);
+      assertEquals(Iterables.get(actual, 0).toString(), Iterables.get(expected, 0).toString());
    }
 
    public void testListWhenResponseIs404IsEmpty() throws Exception {
@@ -82,12 +86,40 @@
                         .put("Accept", "application/json")
                         .put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
                   .payload(newUrlEncodedFormPayload(
-                        ImmutableMultimap.<String, String>builder().put("domain", "test").build())).build(),
+                        ImmutableMultimap.<String, String>builder().put("domainname", "test").build())).build(),
             HttpResponse.builder().statusCode(404).build()).getEmailClient();
 
       assertTrue(client.listAccounts("test").isEmpty());
    }
 
+   public void testListAliasesWhenResponseIs2xx() throws Exception {
+      EmailClient client = requestSendsResponse(
+            HttpRequest.builder().method("POST").endpoint(URI.create("https://api.glesys.com/email/list/format/json"))
+                  .headers(ImmutableMultimap.<String, String>builder()
+                        .put("Accept", "application/json")
+                        .put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
+                  .payload(newUrlEncodedFormPayload(
+                        ImmutableMultimap.<String, String>builder().put("domainname", "cl13016.test.jclouds.org").build())).build(),
+            HttpResponse.builder().statusCode(200).payload(payloadFromResource("/email_list.json")).build()).getEmailClient();
+
+      EmailAlias expected = EmailAlias.builder().alias("test2@cl13016.test.jclouds.org").forwardTo("test2@cl13016.test.jclouds.org").build();
+      EmailAlias actual = Iterables.getOnlyElement(client.listAliases("cl13016.test.jclouds.org"));
+      assertEquals(actual, expected);
+   }
+
+   public void testListAliasesWhenResponseIs404IsEmpty() throws Exception {
+      EmailClient client = requestSendsResponse(
+            HttpRequest.builder().method("POST").endpoint(URI.create("https://api.glesys.com/email/list/format/json"))
+                  .headers(ImmutableMultimap.<String, String>builder()
+                        .put("Accept", "application/json")
+                        .put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
+                  .payload(newUrlEncodedFormPayload(
+                        ImmutableMultimap.<String, String>builder().put("domainname", "test").build())).build(),
+            HttpResponse.builder().statusCode(404).build()).getEmailClient();
+
+      assertTrue(client.listAliases("test").isEmpty());
+   }
+
    public void testOverviewWhenResponseIs2xx() throws Exception {
       EmailClient client = requestSendsResponse(
             HttpRequest.builder().method("POST").endpoint(URI.create("https://api.glesys.com/email/overview/format/json"))
@@ -97,8 +129,8 @@
                   .build(),
             HttpResponse.builder().statusCode(200).payload(payloadFromResource("/email_overview.json")).build()).getEmailClient();
 
-      EmailOverviewSummary summary = EmailOverviewSummary.builder().accounts(2).maxAccounts(50).aliases(0).maxAliases(1000).build();
-      EmailOverviewDomain domain = EmailOverviewDomain.builder().domain("adamlowe.net").accounts(2).aliases(0).build();
+      EmailOverviewSummary summary = EmailOverviewSummary.builder().accounts(2).maxAccounts(50).aliases(1).maxAliases(1000).build();
+      EmailOverviewDomain domain = EmailOverviewDomain.builder().domain("cl13016.test.jclouds.org").accounts(2).aliases(0).build();
       EmailOverview expected = EmailOverview.builder().summary(summary).domains(domain).build();
 
       assertEquals(client.getEmailOverview(), expected);
@@ -120,15 +152,61 @@
       EmailClient client = requestSendsResponse(
             HttpRequest.builder().method("POST").endpoint(URI.create("https://api.glesys.com/email/createaccount/format/json"))
                   .headers(ImmutableMultimap.<String, String>builder()
+                        .put("Accept", "application/json")
                         .put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
                   .payload(newUrlEncodedFormPayload(ImmutableMultimap.<String, String>builder()
                         .put("emailaccount", "test@jclouds.org")
                         .put("password", "newpass")
                         .build()))
                   .build(),
-            HttpResponse.builder().statusCode(200).build()).getEmailClient();
+            HttpResponse.builder().statusCode(200).payload(payloadFromResourceWithContentType("/email_details.json", MediaType.APPLICATION_JSON)).build())
+            .getEmailClient();
 
-      client.createAccount("test@jclouds.org", "newpass");
+      assertEquals(client.createAccount("test@jclouds.org", "newpass").toString(), getEmailAccountInDetails().toString());
+   }
+
+   public void testEditAccountWhenResponseIs2xx() throws Exception {
+      EmailClient client = requestSendsResponse(
+            HttpRequest.builder().method("POST").endpoint(URI.create("https://api.glesys.com/email/editaccount/format/json"))
+                  .headers(ImmutableMultimap.<String, String>builder()
+                        .put("Accept", "application/json")
+                        .put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
+                  .payload(newUrlEncodedFormPayload(ImmutableMultimap.<String, String>builder()
+                        .put("emailaccount", "test@jclouds.org")
+                        .put("password", "anotherpass")
+                        .build()))
+                  .build(),
+            HttpResponse.builder().statusCode(200).payload(payloadFromResourceWithContentType("/email_details.json", MediaType.APPLICATION_JSON)).build())
+            .getEmailClient();
+
+      assertEquals(client.editAccount("test@jclouds.org", EditAccountOptions.Builder.password("anotherpass")).toString(), getEmailAccountInDetails().toString());
+   }
+
+   @Test(expectedExceptions = ResourceNotFoundException.class)
+   public void testEditAccountWhenResponseIs4xx() throws Exception {
+      EmailClient client = requestSendsResponse(
+            HttpRequest.builder().method("POST").endpoint(URI.create("https://api.glesys.com/email/editaccount/format/json"))
+                  .headers(ImmutableMultimap.<String, String>builder()
+                        .put("Accept", "application/json")
+                        .put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
+                  .payload(newUrlEncodedFormPayload(ImmutableMultimap.<String, String>builder()
+                        .put("emailaccount", "test@jclouds.org")
+                        .put("password", "anotherpass")
+                        .build()))
+                  .build(),
+            HttpResponse.builder().statusCode(404).build())
+            .getEmailClient();
+
+      assertEquals(client.editAccount("test@jclouds.org", EditAccountOptions.Builder.password("anotherpass")).toString(), getEmailAccountInDetails().toString());
+   }
+
+   private EmailAccount getEmailAccountInDetails() {
+      return EmailAccount.builder().account("test@CL13016.jclouds.org")
+            .antispamLevel(3)
+            .antiVirus(true)
+            .autoRespondSaveEmail(true)
+            .created(dateService.iso8601SecondsDateParse("2012-06-20T12:01:01+02:00"))
+            .quota(EmailQuota.builder().max(200).unit("MB").build()).build();
    }
 
    @Test(expectedExceptions = {ResourceNotFoundException.class})
@@ -136,6 +214,7 @@
       EmailClient client = requestSendsResponse(
             HttpRequest.builder().method("POST").endpoint(URI.create("https://api.glesys.com/email/createaccount/format/json"))
                   .headers(ImmutableMultimap.<String, String>builder()
+                        .put("Accept", "application/json")
                         .put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
                   .payload(newUrlEncodedFormPayload(ImmutableMultimap.<String, String>builder()
                         .put("emailaccount", "test@jclouds.org")
@@ -151,6 +230,7 @@
       EmailClient client = requestSendsResponse(
             HttpRequest.builder().method("POST").endpoint(URI.create("https://api.glesys.com/email/createalias/format/json"))
                   .headers(ImmutableMultimap.<String, String>builder()
+                        .put("Accept", "application/json")
                         .put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
                   .payload(newUrlEncodedFormPayload(ImmutableMultimap.<String, String>builder()
                         .put("emailalias", "test2@jclouds.org")
@@ -167,6 +247,7 @@
       EmailClient client = requestSendsResponse(
             HttpRequest.builder().method("POST").endpoint(URI.create("https://api.glesys.com/email/createalias/format/json"))
                   .headers(ImmutableMultimap.<String, String>builder()
+                        .put("Accept", "application/json")
                         .put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
                   .payload(newUrlEncodedFormPayload(ImmutableMultimap.<String, String>builder()
                         .put("emailalias", "test2@jclouds.org")
@@ -182,6 +263,7 @@
       EmailClient client = requestSendsResponse(
             HttpRequest.builder().method("POST").endpoint(URI.create("https://api.glesys.com/email/editalias/format/json"))
                   .headers(ImmutableMultimap.<String, String>builder()
+                        .put("Accept", "application/json")
                         .put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
                   .payload(newUrlEncodedFormPayload(ImmutableMultimap.<String, String>builder()
                         .put("emailalias", "test2@jclouds.org")
@@ -198,6 +280,7 @@
       EmailClient client = requestSendsResponse(
             HttpRequest.builder().method("POST").endpoint(URI.create("https://api.glesys.com/email/editalias/format/json"))
                   .headers(ImmutableMultimap.<String, String>builder()
+                        .put("Accept", "application/json")
                         .put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
                   .payload(newUrlEncodedFormPayload(ImmutableMultimap.<String, String>builder()
                         .put("emailalias", "test2@jclouds.org")
@@ -208,4 +291,33 @@
 
       client.editAlias("test2@jclouds.org", "test@jclouds.org");
    }
+
+   public void testDeleteWhenResponseIs2xx() throws Exception {
+      EmailClient client = requestSendsResponse(
+            HttpRequest.builder().method("POST").endpoint(URI.create("https://api.glesys.com/email/delete/format/json"))
+                  .headers(ImmutableMultimap.<String, String>builder()
+                        .put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
+                  .payload(newUrlEncodedFormPayload(ImmutableMultimap.<String, String>builder()
+                        .put("email", "test2@jclouds.org")
+                        .build()))
+                  .build(),
+            HttpResponse.builder().statusCode(200).build()).getEmailClient();
+
+      client.delete("test2@jclouds.org");
+   }
+
+   @Test(expectedExceptions = {ResourceNotFoundException.class})
+   public void testDeleteWhenResponseIs4xxThrows() throws Exception {
+      EmailClient client = requestSendsResponse(
+            HttpRequest.builder().method("POST").endpoint(URI.create("https://api.glesys.com/email/delete/format/json"))
+                  .headers(ImmutableMultimap.<String, String>builder()
+                        .put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
+                  .payload(newUrlEncodedFormPayload(ImmutableMultimap.<String, String>builder()
+                        .put("email", "test2@jclouds.org")
+                        .build()))
+                  .build(),
+            HttpResponse.builder().statusCode(404).build()).getEmailClient();
+
+      client.delete("test2@jclouds.org");
+   }
 }
diff --git a/labs/glesys/src/test/java/org/jclouds/glesys/features/EmailClientLiveTest.java b/labs/glesys/src/test/java/org/jclouds/glesys/features/EmailClientLiveTest.java
index 6db9894..58bd3b4 100644
--- a/labs/glesys/src/test/java/org/jclouds/glesys/features/EmailClientLiveTest.java
+++ b/labs/glesys/src/test/java/org/jclouds/glesys/features/EmailClientLiveTest.java
@@ -18,6 +18,7 @@
  */
 package org.jclouds.glesys.features;
 
+import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertFalse;
 import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertTrue;
@@ -26,11 +27,11 @@
 import java.util.concurrent.TimeUnit;
 
 import org.jclouds.glesys.domain.EmailAccount;
+import org.jclouds.glesys.domain.EmailAlias;
 import org.jclouds.glesys.domain.EmailOverview;
 import org.jclouds.glesys.domain.EmailOverviewDomain;
-import org.jclouds.glesys.internal.BaseGleSYSClientLiveTest;
+import org.jclouds.glesys.internal.BaseGleSYSClientWithAServerLiveTest;
 import org.jclouds.glesys.options.CreateAccountOptions;
-import org.jclouds.glesys.options.DestroyServerOptions;
 import org.jclouds.glesys.options.EditAccountOptions;
 import org.jclouds.predicates.RetryablePredicate;
 import org.testng.annotations.AfterGroups;
@@ -38,6 +39,7 @@
 import org.testng.annotations.Test;
 
 import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
 
 /**
  * Tests behavior of {@code EmailClient}
@@ -45,15 +47,13 @@
  * @author Adam Lowe
  */
 @Test(groups = "live", testName = "EmailClientLiveTest", singleThreaded = true)
-public class EmailClientLiveTest extends BaseGleSYSClientLiveTest {
+public class EmailClientLiveTest extends BaseGleSYSClientWithAServerLiveTest {
 
    @BeforeGroups(groups = {"live"})
-   public void setupContext() {
-      super.setupContext();
+   public void setupDomains() {
+      testDomain = identity + ".test.jclouds.org";
       client = gleContext.getApi().getEmailClient();
 
-      serverId = createServer("test-email-jclouds").getServerId();
-
       createDomain(testDomain);
 
       emailAccountCounter = new RetryablePredicate<Integer>(
@@ -61,24 +61,26 @@
                public boolean apply(Integer value) {
                   return client.listAccounts(testDomain).size() == value;
                }
-            }, 90, 5, TimeUnit.SECONDS);
+            }, 180, 5, TimeUnit.SECONDS);
 
       assertTrue(emailAccountCounter.apply(0));
+      
+      try {
+         client.delete("test2@" + testDomain);
+      } catch(Exception e) {
+      }
    }
 
    @AfterGroups(groups = {"live"})
-   public void tearDownContext() {
+   public void tearDownDomains() {
       client.delete("test@" + testDomain);
       client.delete("test1@" + testDomain);
       assertTrue(emailAccountCounter.apply(0));
       gleContext.getApi().getDomainClient().deleteDomain(testDomain);
-      gleContext.getApi().getServerClient().destroyServer(serverId, DestroyServerOptions.Builder.discardIp());
-      super.tearDownContext();
    }
 
    private EmailClient client;
-   private String serverId;
-   private final String testDomain = "email-test.jclouds.org";
+   private String testDomain;
    private RetryablePredicate<Integer> emailAccountCounter;
 
    @Test
@@ -95,14 +97,24 @@
 
    @Test(dependsOnMethods = "testCreateEmail")
    public void testAliases() {
-      client.createAlias("test2@" + testDomain, "test@" + testDomain);
+      assertTrue(client.listAliases(testDomain).isEmpty());
+
+      EmailAlias alias = client.createAlias("test2@" + testDomain, "test@" + testDomain);
+      assertEquals(alias.getAlias(), "test2@" + testDomain);
+      assertEquals(alias.getForwardTo(), "test@" + testDomain);
+
+      EmailAlias aliasFromList = Iterables.getOnlyElement(client.listAliases(testDomain));
+      assertEquals(aliasFromList, alias);
+      
       EmailOverview overview = client.getEmailOverview();
       assertTrue(overview.getSummary().getAliases() == 1);
 
-      // TODO verify the result of editing the alias
-      client.editAlias("test2@" + testDomain, "test1@" + testDomain);
+      alias = client.editAlias("test2@" + testDomain, "test1@" + testDomain);
       overview = client.getEmailOverview();
       assertTrue(overview.getSummary().getAliases() == 1);
+      
+      aliasFromList = Iterables.getOnlyElement(client.listAliases(testDomain));
+      assertEquals(aliasFromList, alias);
 
       client.delete("test2@" + testDomain);
       overview = client.getEmailOverview();
@@ -113,8 +125,8 @@
    public void testOverview() throws Exception {
       EmailOverview overview = client.getEmailOverview();
       assertNotNull(overview.getSummary());
-      assertTrue(overview.getSummary().getAccounts() >= 1);
-      assertTrue(overview.getSummary().getAliases() == 0);
+      assertTrue(overview.getSummary().getAccounts() > 0);
+      assertTrue(overview.getSummary().getAliases() > -1);
       assertTrue(overview.getSummary().getMaxAccounts() > 0);
       assertTrue(overview.getSummary().getMaxAliases() > 0);
       assertNotNull(overview.getDomains());
@@ -135,7 +147,7 @@
       Set<EmailAccount> accounts = client.listAccounts(testDomain);
       for (EmailAccount account : accounts) {
          if (account.getAccount().equals("test@" + testDomain)) {
-            assertTrue(account.getAntiVirus());
+            assertTrue(account.isAntiVirus());
          }
       }
 
@@ -144,7 +156,7 @@
       accounts = client.listAccounts(testDomain);
       for (EmailAccount account : accounts) {
          if (account.getAccount().equals("test@" + testDomain)) {
-            assertFalse(account.getAntiVirus());
+            assertFalse(account.isAntiVirus());
          }
       }
    }
diff --git a/labs/glesys/src/test/java/org/jclouds/glesys/features/IpAsyncClientTest.java b/labs/glesys/src/test/java/org/jclouds/glesys/features/IpAsyncClientTest.java
deleted file mode 100644
index c43079c..0000000
--- a/labs/glesys/src/test/java/org/jclouds/glesys/features/IpAsyncClientTest.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/**
- * Licensed to jclouds, Inc. (jclouds) under one or more
- * contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  jclouds licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.jclouds.glesys.features;
-
-import java.io.IOException;
-import java.lang.reflect.Method;
-
-import org.jclouds.glesys.internal.BaseGleSYSAsyncClientTest;
-import org.jclouds.http.HttpRequest;
-import org.jclouds.http.functions.ParseFirstJsonValueNamed;
-import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404;
-import org.jclouds.rest.internal.RestAnnotationProcessor;
-import org.testng.annotations.Test;
-
-import com.google.inject.TypeLiteral;
-
-/**
- * Tests annotation parsing of {@code IpAsyncClient}
- * 
- * @author Adrian Cole
- */
-@Test(groups = "unit", testName = "IpAsyncClientTest")
-public class IpAsyncClientTest extends BaseGleSYSAsyncClientTest<IpAsyncClient> {
-
-   public void testGetIpDetails() throws SecurityException, NoSuchMethodException, IOException {
-      Method method = IpAsyncClient.class.getMethod("getIpDetails", String.class);
-      HttpRequest request = processor.createRequest(method, "31.192.227.37");
-
-      assertRequestLineEquals(request,
-               "GET https://api.glesys.com/ip/details/ipaddress/31.192.227.37/format/json HTTP/1.1");
-      assertNonPayloadHeadersEqual(request, "Accept: application/json\n");
-      assertPayloadEquals(request, null, "application/xml", false);
-
-      assertResponseParserClassEquals(method, request, ParseFirstJsonValueNamed.class);
-      assertSaxResponseParserClassEquals(method, null);
-      assertExceptionParserClassEquals(method, ReturnNullOnNotFoundOr404.class);
-
-      checkFilters(request);
-   }
-
-   @Override
-   protected TypeLiteral<RestAnnotationProcessor<IpAsyncClient>> createTypeLiteral() {
-      return new TypeLiteral<RestAnnotationProcessor<IpAsyncClient>>() {
-      };
-   }
-}
diff --git a/labs/glesys/src/test/java/org/jclouds/glesys/features/IpClientExpectTest.java b/labs/glesys/src/test/java/org/jclouds/glesys/features/IpClientExpectTest.java
index 6acb032..d5761b9 100644
--- a/labs/glesys/src/test/java/org/jclouds/glesys/features/IpClientExpectTest.java
+++ b/labs/glesys/src/test/java/org/jclouds/glesys/features/IpClientExpectTest.java
@@ -18,234 +18,345 @@
  */
 package org.jclouds.glesys.features;
 
-import static java.util.Arrays.asList;
 import static java.util.Collections.emptySet;
 import static org.jclouds.io.Payloads.newUrlEncodedFormPayload;
 import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.fail;
-import static org.testng.collections.Sets.newHashSet;
+import static org.testng.Assert.assertTrue;
 
 import java.net.URI;
-import java.util.Set;
 
-import org.jclouds.glesys.GleSYSClient;
+import javax.ws.rs.core.MediaType;
+
+import org.jclouds.glesys.domain.Cost;
 import org.jclouds.glesys.domain.IpDetails;
+import org.jclouds.glesys.internal.BaseGleSYSClientExpectTest;
+import org.jclouds.glesys.parse.ParseIpAddressFromResponseTest;
 import org.jclouds.http.HttpRequest;
 import org.jclouds.http.HttpResponse;
 import org.jclouds.http.HttpResponseException;
-import org.jclouds.rest.internal.BaseRestClientExpectTest;
+import org.jclouds.rest.AuthorizationException;
+import org.jclouds.rest.ResourceNotFoundException;
 import org.testng.annotations.Test;
 
 import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.ImmutableSet;
 
 /**
  * Allows us to test a client via its side effects.
  *
- * @author Adrian Cole
+ * @author Adrian Cole, Adam Lowe
  */
 @Test(groups = "unit", testName = "IpClientExpectTest")
-public class IpClientExpectTest extends BaseRestClientExpectTest<GleSYSClient> {
-   public IpClientExpectTest() {
-      provider = "glesys";
+public class IpClientExpectTest extends BaseGleSYSClientExpectTest {
+
+   public void testListIpsWhenResponseIs2xx() {
+      IpClient client = requestSendsResponse(
+            HttpRequest.builder().method("GET").endpoint(
+                  URI.create("https://api.glesys.com/ip/listown/format/json"))
+                  .headers(ImmutableMultimap.<String, String>builder().put("Accept", "application/json")
+                        .put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build()).build(),
+            HttpResponse.builder().statusCode(200).payload(payloadFromResource("/ip_list_own.json")).build())
+            .getIpClient();
+
+      IpDetails.Builder builder = IpDetails.builder().datacenter("Falkenberg").version4().reserved(true)
+            .platform("OpenVZ")
+            .nameServers("79.99.4.100", "79.99.4.101")
+            .cost(Cost.builder().amount(2.0).currency("EUR").timePeriod("month").build());
+
+      assertEquals(client.listIps().toString(), ImmutableSet.of(
+            builder.ptr("31-192-230-68-static.serverhotell.net.").address("31.192.230.68").serverId(null).build(),
+            builder.ptr("31-192-231-148-static.serverhotell.net.").address("31.192.231.148").serverId("vz1609110").build()).toString());
+   }
+
+   public void testListIpsWhenResponseIs4xxReturnsEmpty() {
+      IpClient client = requestSendsResponse(
+            HttpRequest.builder().method("GET").endpoint(
+                  URI.create("https://api.glesys.com/ip/listown/format/json")).headers(
+                  ImmutableMultimap.<String, String>builder().put("Accept", "application/json").put(
+                        "Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build()).build(),
+            HttpResponse.builder().statusCode(404).build()).getIpClient();
+
+      assertTrue(client.listIps().isEmpty());
    }
 
    public void testGetIpDetailsWhenResponseIs2xx() {
-
       IpClient client = requestSendsResponse(
-              HttpRequest.builder().method("GET").endpoint(
-                      URI.create("https://api.glesys.com/ip/details/ipaddress/31.192.227.37/format/json")).headers(
-                      ImmutableMultimap.<String, String>builder().put("Accept", "application/json").put(
-                              "Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build()).build(),
-              HttpResponse.builder().statusCode(200).payload(payloadFromResource("/ip_get_details.json")).build())
-              .getIpClient();
+            HttpRequest.builder().method("GET").endpoint(
+                  URI.create("https://api.glesys.com/ip/details/ipaddress/31.192.227.113/format/json"))
+                  .headers(ImmutableMultimap.<String, String>builder().put("Accept", "application/json")
+                        .put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build()).build(),
+            HttpResponse.builder().statusCode(200).payload(payloadFromResource("/ip_get_details.json")).build())
+            .getIpClient();
 
-      assertEquals(client.getIpDetails("31.192.227.37"), IpDetails.builder().datacenter("Falkenberg").ipversion("4")
-              .platform("OpenVZ").ptr("31-192-227-37-static.serverhotell.net.").build());
+      assertEquals(client.getIp("31.192.227.113"), getIpInIpDetails());
+   }
 
+   protected IpDetails getIpInIpDetails() {
+      return IpDetails.builder().datacenter("Falkenberg").version4()
+            .platform("OpenVZ").ptr("31-192-227-113-static.serverhotell.net.")
+            .nameServers("79.99.4.100", "79.99.4.101")
+            .address("31.192.227.113")
+            .cost(Cost.builder().amount(2.0).currency("EUR").timePeriod("month").build()).build();
    }
 
    public void testGetIpDetailsWhenResponseIs4xxReturnsNull() {
 
       IpClient client = requestSendsResponse(
-              HttpRequest.builder().method("GET").endpoint(
-                      URI.create("https://api.glesys.com/ip/details/ipaddress/31.192.227.37/format/json")).headers(
-                      ImmutableMultimap.<String, String>builder().put("Accept", "application/json").put(
-                              "Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build()).build(),
-              HttpResponse.builder().statusCode(404).build()).getIpClient();
+            HttpRequest.builder().method("GET").endpoint(
+                  URI.create("https://api.glesys.com/ip/details/ipaddress/31.192.227.37/format/json")).headers(
+                  ImmutableMultimap.<String, String>builder().put("Accept", "application/json").put(
+                        "Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build()).build(),
+            HttpResponse.builder().statusCode(404).build()).getIpClient();
 
-      assertEquals(client.getIpDetails("31.192.227.37"), null);
-
+      assertEquals(client.getIp("31.192.227.37"), null);
    }
 
    public void testTakeWhenResponseIs2xx() {
       IpClient client = requestSendsResponse(
-              HttpRequest.builder().method("POST").endpoint(
-                      URI.create("https://api.glesys.com/ip/take/format/json"))
-                      .headers(ImmutableMultimap.<String, String>builder()
-                              .put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
-                      .payload(newUrlEncodedFormPayload(
-                              ImmutableMultimap.<String, String>builder().put("ipaddress", "46.21.105.186").build()
-                      )).build(),
-              HttpResponse.builder().statusCode(200).payload(payloadFromResource("/ip_take.json")).build())
-              .getIpClient();
+            HttpRequest.builder().method("POST").endpoint(
+                  URI.create("https://api.glesys.com/ip/take/format/json"))
+                  .headers(ImmutableMultimap.<String, String>builder().put("Accept", "application/json")
+                        .put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
+                  .payload(newUrlEncodedFormPayload(
+                        ImmutableMultimap.<String, String>builder().put("ipaddress", "46.21.105.186").build()
+                  )).build(),
+            HttpResponse.builder().statusCode(200).payload(payloadFromResource("/ip_take.json")).build())
+            .getIpClient();
 
       client.take("46.21.105.186");
    }
 
+   @Test(expectedExceptions = HttpResponseException.class)
    public void testTakeWhenResponseIs4xxThrowsResponseException() {
       IpClient client = requestSendsResponse(
-              HttpRequest.builder().method("POST").endpoint(
-                      URI.create("https://api.glesys.com/ip/take/format/json"))
-                      .headers(ImmutableMultimap.<String, String>builder()
-                              .put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
-                      .payload(newUrlEncodedFormPayload(
-                              ImmutableMultimap.<String, String>builder().put("ipaddress", "46.21.105.186").build()
-                      )).build(),
-              HttpResponse.builder().statusCode(400).build())
-              .getIpClient();
-
-      try {
-         client.take("46.21.105.186");
-         fail();
-      } catch (HttpResponseException e) {
-         // Expected
-      }
+            HttpRequest.builder().method("POST").endpoint(
+                  URI.create("https://api.glesys.com/ip/take/format/json"))
+                  .headers(ImmutableMultimap.<String, String>builder().put("Accept", "application/json")
+                        .put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
+                  .payload(newUrlEncodedFormPayload(
+                        ImmutableMultimap.<String, String>builder().put("ipaddress", "46.21.105.186").build()
+                  )).build(),
+            HttpResponse.builder().statusCode(400).build()).getIpClient();
+      client.take("46.21.105.186");
    }
 
    public void testReleaseWhenResponseIs2xx() {
       IpClient client = requestSendsResponse(
-              HttpRequest.builder().method("POST").endpoint(
-                      URI.create("https://api.glesys.com/ip/release/format/json"))
-                      .headers(ImmutableMultimap.<String, String>builder()
-                              .put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
-                      .payload(newUrlEncodedFormPayload(
-                              ImmutableMultimap.<String, String>builder().put("ipaddress", "46.21.105.186").build()
-                      )).build(),
-              HttpResponse.builder().statusCode(200).payload(payloadFromResource("/ip_release.json")).build())
-              .getIpClient();
+            HttpRequest.builder().method("POST").endpoint(
+                  URI.create("https://api.glesys.com/ip/release/format/json"))
+                  .headers(ImmutableMultimap.<String, String>builder().put("Accept", "application/json")
+                        .put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
+                  .payload(newUrlEncodedFormPayload(
+                        ImmutableMultimap.<String, String>builder().put("ipaddress", "46.21.105.186").build()
+                  )).build(),
+            HttpResponse.builder().statusCode(200).payload(payloadFromResource("/ip_release.json")).build())
+            .getIpClient();
 
       client.release("46.21.105.186");
    }
 
+   @Test(expectedExceptions = ResourceNotFoundException.class)
    public void testReleaseWhenResponseIs4xxThrowsResponseException() {
       IpClient client = requestSendsResponse(
-              HttpRequest.builder().method("POST").endpoint(
-                      URI.create("https://api.glesys.com/ip/release/format/json"))
-                      .headers(ImmutableMultimap.<String, String>builder()
-                              .put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
-                      .payload(newUrlEncodedFormPayload(
-                              ImmutableMultimap.<String, String>builder().put("ipaddress", "46.21.105.186").build()
-                      )).build(),
-              HttpResponse.builder().statusCode(400).build())
-              .getIpClient();
+            HttpRequest.builder().method("POST").endpoint(
+                  URI.create("https://api.glesys.com/ip/release/format/json"))
+                  .headers(ImmutableMultimap.<String, String>builder().put("Accept", "application/json")
+                        .put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
+                  .payload(newUrlEncodedFormPayload(
+                        ImmutableMultimap.<String, String>builder().put("ipaddress", "46.21.105.186").build()
+                  )).build(),
+            HttpResponse.builder().statusCode(404).build())
+            .getIpClient();
 
-      try {
-         client.release("46.21.105.186");
-         fail();
-      } catch (HttpResponseException e) {
-         // Expected
-      }
+      client.release("46.21.105.186");
    }
 
    public void testListFreeWhenResponseIs2xx() {
       IpClient client = requestSendsResponse(
-              HttpRequest.builder().method("GET").endpoint(
-                      URI.create("https://api.glesys.com/ip/listfree/ipversion/4/datacenter/Falkenberg/platform/OpenVZ/format/json"))
-                      .headers(ImmutableMultimap.<String, String>builder().put("Accept", "application/json").put(
-                              "Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build()).build(),
-              HttpResponse.builder().statusCode(200).payload(payloadFromResource("/ip_list_free.json")).build())
-              .getIpClient();
+            HttpRequest.builder().method("GET").endpoint(
+                  URI.create("https://api.glesys.com/ip/listfree/ipversion/4/datacenter/Falkenberg/platform/OpenVZ/format/json"))
+                  .headers(ImmutableMultimap.<String, String>builder().put("Accept", "application/json").put(
+                        "Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build()).build(),
+            HttpResponse.builder().statusCode(200).payload(payloadFromResource("/ip_list_free.json")).build())
+            .getIpClient();
 
-      Set<Object> expectedIps = newHashSet();
-      expectedIps.addAll(asList("31.192.226.131", "31.192.226.133"));
-      assertEquals(client.listFree("4", "Falkenberg", "OpenVZ"), expectedIps);
+      assertEquals(client.listFree(4, "Falkenberg", "OpenVZ"), ParseIpAddressFromResponseTest.EXPECTED_IPS);
    }
 
    public void testListFreeWhenResponseIs404ReturnsEmptySet() {
       IpClient client = requestSendsResponse(
-              HttpRequest.builder().method("GET").endpoint(
-                      URI.create("https://api.glesys.com/ip/listfree/ipversion/4/datacenter/Falkenberg/platform/OpenVZ/format/json"))
-                      .headers(ImmutableMultimap.<String, String>builder().put("Accept", "application/json").put(
-                              "Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build()).build(),
-              HttpResponse.builder().statusCode(404).build())
-              .getIpClient();
+            HttpRequest.builder().method("GET").endpoint(
+                  URI.create("https://api.glesys.com/ip/listfree/ipversion/6/datacenter/Falkenberg/platform/OpenVZ/format/json"))
+                  .headers(ImmutableMultimap.<String, String>builder().put("Accept", "application/json").put(
+                        "Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build()).build(),
+            HttpResponse.builder().statusCode(404).build())
+            .getIpClient();
 
-      assertEquals(client.listFree("4", "Falkenberg", "OpenVZ"), emptySet());
+      assertEquals(client.listFree(6, "Falkenberg", "OpenVZ"), emptySet());
    }
 
    public void testAddWhenResponseIs2xx() {
       IpClient client = requestSendsResponse(
-              HttpRequest.builder().method("POST").endpoint(
-                      URI.create("https://api.glesys.com/ip/add/format/json"))
-                      .headers(ImmutableMultimap.<String, String>builder().put(
-                              "Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
-                      .payload(newUrlEncodedFormPayload(
-                              ImmutableMultimap.<String, String>builder()
-                                      .put("ipaddress", "31.192.227.37")
-                                      .put("serverid", "vz1946889").build())).build(),
-              HttpResponse.builder().statusCode(200).build())
-              .getIpClient();
+            HttpRequest.builder().method("POST").endpoint(
+                  URI.create("https://api.glesys.com/ip/add/format/json"))
+                  .headers(ImmutableMultimap.<String, String>builder().put("Accept", "application/json")
+                        .put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
+                  .payload(newUrlEncodedFormPayload(
+                        ImmutableMultimap.<String, String>builder()
+                              .put("ipaddress", "31.192.227.37")
+                              .put("serverid", "vz1946889").build())).build(),
+            HttpResponse.builder().statusCode(200).build())
+            .getIpClient();
 
       client.addIpToServer("31.192.227.37", "vz1946889");
    }
 
+   @Test(expectedExceptions = AuthorizationException.class)
    public void testAddWhenResponseIs4xxThrowsHttpException() {
       IpClient client = requestSendsResponse(
-              HttpRequest.builder().method("POST").endpoint(
-                      URI.create("https://api.glesys.com/ip/add/format/json"))
-                      .headers(ImmutableMultimap.<String, String>builder().put(
-                              "Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
-                      .payload(newUrlEncodedFormPayload(
-                              ImmutableMultimap.<String, String>builder()
-                                      .put("ipaddress", "31.192.227.37")
-                                      .put("serverid", "vz1946889")
-                                      .build())).build(),
-              HttpResponse.builder().statusCode(400).build())
-              .getIpClient();
-
-      try {
-         client.addIpToServer("31.192.227.37", "vz1946889");
-         fail();
-      } catch (HttpResponseException e) {
-         // Expected
-      }
+            HttpRequest.builder().method("POST").endpoint(
+                  URI.create("https://api.glesys.com/ip/add/format/json"))
+                  .headers(ImmutableMultimap.<String, String>builder().put("Accept", "application/json")
+                        .put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
+                  .payload(newUrlEncodedFormPayload(
+                        ImmutableMultimap.<String, String>builder()
+                              .put("ipaddress", "31.192.227.37")
+                              .put("serverid", "vz1946889")
+                              .build())).build(),
+            HttpResponse.builder().statusCode(401).build())
+            .getIpClient();
+      client.addIpToServer("31.192.227.37", "vz1946889");
    }
 
    public void testRemoveWhenResponseIs2xx() {
       IpClient client = requestSendsResponse(
-              HttpRequest.builder().method("POST").endpoint(
-                      URI.create("https://api.glesys.com/ip/remove/format/json"))
-                      .headers(ImmutableMultimap.<String, String>builder().put(
-                              "Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
-                      .payload(newUrlEncodedFormPayload(
-                              ImmutableMultimap.<String, String>builder()
-                                      .put("ipaddress", "31.192.227.37")
-                                      .put("serverid", "vz1946889").build())).build(),
-              HttpResponse.builder().statusCode(200).build())
-              .getIpClient();
+            HttpRequest.builder().method("POST").endpoint(
+                  URI.create("https://api.glesys.com/ip/remove/format/json"))
+                  .headers(ImmutableMultimap.<String, String>builder().put("Accept", "application/json")
+                        .put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
+                  .payload(newUrlEncodedFormPayload(
+                        ImmutableMultimap.<String, String>builder()
+                              .put("ipaddress", "31.192.227.37")
+                              .put("serverid", "vz1946889").build())).build(),
+            HttpResponse.builder().statusCode(200).build())
+            .getIpClient();
 
       client.removeIpFromServer("31.192.227.37", "vz1946889");
    }
 
+   @Test(expectedExceptions = HttpResponseException.class)
    public void testRemoveWhenResponseIs4xxThrowsHttpException() {
       IpClient client = requestSendsResponse(
-              HttpRequest.builder().method("POST").endpoint(
-                      URI.create("https://api.glesys.com/ip/remove/format/json"))
-                      .headers(ImmutableMultimap.<String, String>builder().put(
-                              "Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
-                      .payload(newUrlEncodedFormPayload(
-                              ImmutableMultimap.<String, String>builder()
-                                      .put("ipaddress", "31.192.227.37")
-                                      .put("serverid", "vz1946889").build())).build(),
-              HttpResponse.builder().statusCode(400).build())
-              .getIpClient();
+            HttpRequest.builder().method("POST").endpoint(
+                  URI.create("https://api.glesys.com/ip/remove/format/json"))
+                  .headers(ImmutableMultimap.<String, String>builder().put("Accept", "application/json")
+                        .put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
+                  .payload(newUrlEncodedFormPayload(
+                        ImmutableMultimap.<String, String>builder()
+                              .put("ipaddress", "31.192.227.37")
+                              .put("serverid", "vz1946889").build())).build(),
+            HttpResponse.builder().statusCode(400).build())
+            .getIpClient();
 
-      try {
-         client.removeIpFromServer("31.192.227.37", "vz1946889");
-         fail();
-      } catch (HttpResponseException e) {
-         // Expected
-      }
+      client.removeIpFromServer("31.192.227.37", "vz1946889");
    }
 
+   public void testRemoveAndReleaseWhenResponseIs2xx() {
+      IpClient client = requestSendsResponse(
+            HttpRequest.builder().method("POST").endpoint(
+                  URI.create("https://api.glesys.com/ip/remove/format/json"))
+                  .headers(ImmutableMultimap.<String, String>builder().put("Accept", "application/json")
+                        .put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
+                  .payload(newUrlEncodedFormPayload(
+                        ImmutableMultimap.<String, String>builder()
+                              .put("release", "true")
+                              .put("ipaddress", "31.192.227.37")
+                              .put("serverid", "vz1946889").build())).build(),
+            HttpResponse.builder().statusCode(200).build())
+            .getIpClient();
+
+      client.removeIpFromServerAndRelease("31.192.227.37", "vz1946889");
+   }
+
+   @Test(expectedExceptions = HttpResponseException.class)
+   public void testRemoveAndReleaseWhenResponseIs4xxThrowsHttpException() {
+      IpClient client = requestSendsResponse(
+            HttpRequest.builder().method("POST").endpoint(
+                  URI.create("https://api.glesys.com/ip/remove/format/json"))
+                  .headers(ImmutableMultimap.<String, String>builder().put("Accept", "application/json")
+                        .put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
+                  .payload(newUrlEncodedFormPayload(
+                        ImmutableMultimap.<String, String>builder()
+                              .put("release", "true")
+                              .put("ipaddress", "31.192.227.37")
+                              .put("serverid", "vz1946889").build())).build(),
+            HttpResponse.builder().statusCode(400).build())
+            .getIpClient();
+
+      client.removeIpFromServerAndRelease("31.192.227.37", "vz1946889");
+   }
+
+   public void testSetPrtWhenResponseIs2xx() {
+      IpClient client = requestSendsResponse(
+            HttpRequest.builder().method("POST").endpoint(
+                  URI.create("https://api.glesys.com/ip/setptr/format/json"))
+                  .headers(ImmutableMultimap.<String, String>builder().put("Accept", "application/json")
+                        .put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
+                  .payload(newUrlEncodedFormPayload(
+                        ImmutableMultimap.<String, String>builder()
+                              .put("ipaddress", "31.192.227.37")
+                              .put("data", "sommeptr.").build())).build(),
+            HttpResponse.builder().statusCode(200).payload(payloadFromResourceWithContentType("/ip_get_details.json", MediaType.APPLICATION_JSON)).build())
+            .getIpClient();
+
+      assertEquals(client.setPtr("31.192.227.37", "sommeptr."), getIpInIpDetails());
+   }
+
+   @Test(expectedExceptions = ResourceNotFoundException.class)
+   public void testSetPtrWhenResponseIs4xxThrowsHttpException() {
+      IpClient client = requestSendsResponse(
+            HttpRequest.builder().method("POST").endpoint(
+                  URI.create("https://api.glesys.com/ip/setptr/format/json"))
+                  .headers(ImmutableMultimap.<String, String>builder().put("Accept", "application/json")
+                        .put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
+                  .payload(newUrlEncodedFormPayload(
+                        ImmutableMultimap.<String, String>builder()
+                              .put("ipaddress", "31.192.227.37")
+                              .put("data", "sommeptr.").build())).build(),
+            HttpResponse.builder().statusCode(404).build())
+            .getIpClient();
+
+      client.setPtr("31.192.227.37", "sommeptr.");
+   }
+
+   public void testResetPrtWhenResponseIs2xx() {
+      IpClient client = requestSendsResponse(
+            HttpRequest.builder().method("POST").endpoint(
+                  URI.create("https://api.glesys.com/ip/resetptr/format/json"))
+                  .headers(ImmutableMultimap.<String, String>builder().put("Accept", "application/json")
+                        .put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
+                  .payload(newUrlEncodedFormPayload(
+                        ImmutableMultimap.<String, String>builder()
+                              .put("ipaddress", "31.192.227.37").build())).build(),
+            HttpResponse.builder().statusCode(200).payload(payloadFromResourceWithContentType("/ip_get_details.json", MediaType.APPLICATION_JSON)).build())
+            .getIpClient();
+
+      assertEquals(client.resetPtr("31.192.227.37"), getIpInIpDetails());
+   }
+
+   @Test(expectedExceptions = AuthorizationException.class)
+   public void testResetPtrWhenResponseIs4xxThrowsHttpException() {
+      IpClient client = requestSendsResponse(
+            HttpRequest.builder().method("POST").endpoint(
+                  URI.create("https://api.glesys.com/ip/resetptr/format/json"))
+                  .headers(ImmutableMultimap.<String, String>builder().put("Accept", "application/json")
+                        .put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
+                  .payload(newUrlEncodedFormPayload(
+                        ImmutableMultimap.<String, String>builder()
+                              .put("ipaddress", "31.192.227.37").build())).build(),
+            HttpResponse.builder().statusCode(401).build())
+            .getIpClient();
+
+      client.resetPtr("31.192.227.37");
+   }
 }
\ No newline at end of file
diff --git a/labs/glesys/src/test/java/org/jclouds/glesys/features/IpClientLiveTest.java b/labs/glesys/src/test/java/org/jclouds/glesys/features/IpClientLiveTest.java
index 092f16d..461d4f3 100644
--- a/labs/glesys/src/test/java/org/jclouds/glesys/features/IpClientLiveTest.java
+++ b/labs/glesys/src/test/java/org/jclouds/glesys/features/IpClientLiveTest.java
@@ -19,69 +19,173 @@
 package org.jclouds.glesys.features;
 
 import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
 import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
 import static org.testng.Assert.assertTrue;
 
-import java.util.List;
 import java.util.Set;
 
 import org.jclouds.glesys.domain.IpDetails;
-import org.jclouds.glesys.internal.BaseGleSYSClientLiveTest;
-import org.testng.annotations.BeforeGroups;
+import org.jclouds.glesys.internal.BaseGleSYSClientWithAServerLiveTest;
+import org.jclouds.glesys.options.ListIpOptions;
+import org.testng.annotations.AfterGroups;
+import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Sets;
+
 /**
  * Tests behavior of {@code IpClient}
  *
  * @author Adrian Cole, Mattias Holmqvist
  */
-@Test(groups = "live", testName = "IpClientLiveTest")
-public class IpClientLiveTest extends BaseGleSYSClientLiveTest {
+@Test(groups = "live", testName = "IpClientLiveTest", singleThreaded = true)
+public class IpClientLiveTest extends BaseGleSYSClientWithAServerLiveTest {
 
-   @BeforeGroups(groups = {"live"})
-   public void setupContext() {
-      super.setupContext();
+   @BeforeMethod
+   public void setupClient() {
       client = gleContext.getApi().getIpClient();
    }
 
+   @AfterGroups(groups = {"live"})
+   public void releaseIp() {
+      if (reservedIp != null) {
+         client.release(reservedIp.getAddress());
+      }
+   }
+
    private IpClient client;
+   private IpDetails reservedIp;
 
    @Test
    public void testListFree() throws Exception {
-      Set<String> freeIps = client.listFree("4", "Falkenberg", "Xen");
-      assertTrue(freeIps.size() >= 1);
+      Set<String> freeIps = client.listFree(4, "Falkenberg", "Xen");
+      assertFalse(freeIps.isEmpty());
    }
 
    @Test
-   public void testGetOpenVZDetails() throws Exception {
-      Set<String> openVzIps = client.listFree("4", "Falkenberg", "OpenVZ");
-      assertTrue(openVzIps.size() >= 1);
-      String openVzIp = openVzIps.iterator().next();
-      IpDetails ipDetails = client.getIpDetails(openVzIp);
+   public void reserveIp() throws Exception {
+      Set<String> openVzIps = client.listFree(4, "Falkenberg", "OpenVZ");
+      assertFalse(openVzIps.isEmpty());
+      reservedIp = client.take(Iterables.get(openVzIps, 0));
+      assertTrue(reservedIp.isReserved());
+      checkOpenVZDefailsInFalkenberg(reservedIp);
+   }
+
+   @Test(dependsOnMethods = "reserveIp")
+   public void reserveAndReleaseIp() throws Exception {
+      IpDetails details = client.release(reservedIp.getAddress());
+      assertEquals(details.getAddress(), reservedIp.getAddress());
+      assertFalse(details.isReserved());
+      
+      // reserve an address again!
+      reserveIp();
+   }
+
+   @Test(dependsOnMethods = "reserveIp")
+   public void testList() throws Exception {
+      Set<IpDetails> ownIps = client.listIps();
+      assertTrue(ownIps.contains(reservedIp));
+      ownIps = client.listIps(ListIpOptions.Builder.datacenter(reservedIp.getDatacenter()));
+      assertTrue(ownIps.contains(reservedIp));
+      ownIps = client.listIps(ListIpOptions.Builder.platform(reservedIp.getPlatform()));
+      assertTrue(ownIps.contains(reservedIp));
+      ownIps = client.listIps(ListIpOptions.Builder.ipVersion(reservedIp.getVersion()));
+      assertTrue(ownIps.contains(reservedIp));
+
+      ownIps = client.listIps(ListIpOptions.Builder.datacenter(reservedIp.getDatacenter()),
+            ListIpOptions.Builder.platform(reservedIp.getPlatform()),
+            ListIpOptions.Builder.ipVersion(reservedIp.getVersion()));
+      assertTrue(ownIps.contains(reservedIp));
+
+      ownIps = client.listIps(ListIpOptions.Builder.serverId("xmthisisnotaserverid"));
+      assertTrue(ownIps.isEmpty());
+   }
+   
+   private void checkOpenVZDefailsInFalkenberg(IpDetails ipDetails) {
       assertEquals(ipDetails.getDatacenter(), "Falkenberg");
       assertEquals(ipDetails.getPlatform(), "OpenVZ");
-      assertEquals(ipDetails.getIpversion(), "4");
-      
-      // TODO: Ask Glesys to include address in response for OpenVZ?
-      // assertEquals(ipDetails.getHostText(), openVzIp);
+      assertEquals(ipDetails.getVersion(), 4);
+      assertFalse(ipDetails.getPtr().isEmpty());
+      // broadcast, gateway and netmask are null for OpenVZ
+      assertFalse(ipDetails.getNameServers().isEmpty());
+   }
+   
+   @Test
+   public void testGetOpenVZDetails() throws Exception {
+      Set<String> openVzIps = client.listFree(4, "Falkenberg", "OpenVZ");
+      assertFalse(openVzIps.isEmpty());
+      String openVzIp = openVzIps.iterator().next();
+      IpDetails ipDetails = client.getIp(openVzIp);
+      checkOpenVZDefailsInFalkenberg(ipDetails);
+      assertEquals(ipDetails.getAddress(), openVzIp);
    }
 
    @Test
    public void testGetXenDetails() throws Exception {
-      Set<String> xenVzIps = client.listFree("4", "Falkenberg", "Xen");
-      assertTrue(xenVzIps.size() >= 1);
+      Set<String> xenVzIps = client.listFree(4, "Falkenberg", "Xen");
+      assertFalse(xenVzIps.isEmpty());
       String xenIp = xenVzIps.iterator().next();
-      IpDetails ipDetails = client.getIpDetails(xenIp);
+      IpDetails ipDetails = client.getIp(xenIp);
       assertEquals(ipDetails.getDatacenter(), "Falkenberg");
       assertEquals(ipDetails.getPlatform(), "Xen");
-      assertEquals(ipDetails.getIpversion(), "4");
+      assertEquals(ipDetails.getVersion(), 4);
       assertEquals(ipDetails.getAddress(), xenIp);
-      assertNotNull(ipDetails.getPtr());
+      assertFalse(ipDetails.getPtr().isEmpty());
       assertNotNull(ipDetails.getBroadcast());
       assertNotNull(ipDetails.getGateway());
       assertNotNull(ipDetails.getNetmask());
-      List<String> nameServers = ipDetails.getNameServers();
-      assertNotNull(nameServers);
+      assertFalse(ipDetails.getNameServers().isEmpty());
    }
 
+   @Test(dependsOnMethods = "reserveIp")
+   public void testPtrSetReset() throws Exception {
+      IpDetails original = reservedIp;
+
+      IpDetails modified = client.setPtr(reservedIp.getAddress(), "wibble.");
+      IpDetails modified2 = client.getIp(reservedIp.getAddress());
+
+      assertEquals(modified.getPtr(), "wibble.");
+      assertEquals(modified2, modified);
+      
+      reservedIp = client.resetPtr(reservedIp.getAddress());
+      
+      assertEquals(reservedIp, original);
+  }
+
+   @Test(dependsOnMethods = "reserveIp")
+   public void testAddRemove() throws Exception {
+      IpDetails added = client.addIpToServer(reservedIp.getAddress(), serverId);
+
+      assertEquals(added.getAddress(), reservedIp.getAddress());
+      assertEquals(added.getPtr(), reservedIp.getPtr());
+      assertEquals(added.getServerId(), serverId);
+      
+      IpDetails again = client.getIp(reservedIp.getAddress());
+      assertEquals(again, added);
+
+      IpDetails removed = client.removeIpFromServer(reservedIp.getAddress(), serverId);
+      assertEquals(removed, added.toBuilder().serverId(null).build());
+
+      assertEquals(removed, reservedIp);
+
+      Set<String> openVzIps = Sets.newHashSet(client.listFree(4, "Falkenberg", "OpenVZ"));
+      openVzIps.remove(reservedIp.getAddress());
+      assertFalse(openVzIps.isEmpty());
+      
+      added = client.addIpToServer(reservedIp.getAddress(), serverId);
+      
+      assertEquals(added.getServerId(), serverId);
+
+      removed = client.removeIpFromServerAndRelease(reservedIp.getAddress(), serverId);
+      
+      assertNull(removed.getServerId());
+      assertFalse(removed.isReserved());
+      
+      
+      // reserve an address again!
+      reserveIp();
+   }
 }
diff --git a/labs/glesys/src/test/java/org/jclouds/glesys/features/ServerClientExpectTest.java b/labs/glesys/src/test/java/org/jclouds/glesys/features/ServerClientExpectTest.java
index c6d591d..7af58c3 100644
--- a/labs/glesys/src/test/java/org/jclouds/glesys/features/ServerClientExpectTest.java
+++ b/labs/glesys/src/test/java/org/jclouds/glesys/features/ServerClientExpectTest.java
@@ -27,18 +27,10 @@
 import java.util.LinkedHashMap;
 import java.util.Map;
 
-import org.jclouds.glesys.GleSYSClient;
-import org.jclouds.glesys.domain.AllowedArgumentsForCreateServer;
-import org.jclouds.glesys.domain.Console;
-import org.jclouds.glesys.domain.Cost;
-import org.jclouds.glesys.domain.Ip;
-import org.jclouds.glesys.domain.OSTemplate;
-import org.jclouds.glesys.domain.ResourceUsage;
-import org.jclouds.glesys.domain.Server;
-import org.jclouds.glesys.domain.ServerDetails;
-import org.jclouds.glesys.domain.ServerSpec;
-import org.jclouds.glesys.domain.ServerStatus;
-import org.jclouds.glesys.domain.ServerUptime;
+import javax.ws.rs.core.MediaType;
+
+import org.jclouds.glesys.domain.*;
+import org.jclouds.glesys.internal.BaseGleSYSClientExpectTest;
 import org.jclouds.glesys.options.CloneServerOptions;
 import org.jclouds.glesys.options.CreateServerOptions;
 import org.jclouds.glesys.options.DestroyServerOptions;
@@ -48,7 +40,6 @@
 import org.jclouds.http.HttpResponse;
 import org.jclouds.rest.AuthorizationException;
 import org.jclouds.rest.ResourceNotFoundException;
-import org.jclouds.rest.internal.BaseRestClientExpectTest;
 import org.testng.annotations.Test;
 
 import com.google.common.collect.ImmutableMultimap;
@@ -61,11 +52,7 @@
  * @author Adam Lowe
  */
 @Test(groups = "unit", testName = "ServerAsyncClientTest")
-public class ServerClientExpectTest extends BaseRestClientExpectTest<GleSYSClient> {
-
-   public ServerClientExpectTest() {
-      provider = "glesys";
-   }
+public class ServerClientExpectTest extends BaseGleSYSClientExpectTest {
 
    public void testListServersWhenResponseIs2xx() throws Exception {
       ServerClient client = requestSendsResponse(
@@ -136,23 +123,24 @@
 
       ImmutableSet.Builder<OSTemplate> expectedBuilder = ImmutableSet.builder();
 
-      for (String name : new String[] { "Centos 5", "Centos 5 64-bit", "Centos 6 32-bit", "Centos 6 64-bit",
+      for (String name : new String[]{"Centos 5", "Centos 5 64-bit", "Centos 6 32-bit", "Centos 6 64-bit",
             "Debian 5.0 32-bit", "Debian 5.0 64-bit", "Debian 6.0 32-bit", "Debian 6.0 64-bit", "Fedora Core 11",
             "Fedora Core 11 64-bit", "Gentoo", "Gentoo 64-bit", "Scientific Linux 6", "Scientific Linux 6 64-bit",
-            "Slackware 12", "Ubuntu 10.04 LTS 32-bit", "Ubuntu 10.04 LTS 64-bit", "Ubuntu 11.04 64-bit" }) {
-         expectedBuilder.add(new OSTemplate(name, 5, 128, "linux", "OpenVZ"));
+            "Slackware 12", "Ubuntu 10.04 LTS 32-bit", "Ubuntu 10.04 LTS 64-bit", "Ubuntu 11.04 64-bit"}) {
+         expectedBuilder.add(OSTemplate.builder().name(name).minDiskSize(5).minMemSize(128).os("linux").platform("OpenVZ").build());
       }
 
-      for (String name : new String[] { "CentOS 5.5 x64", "CentOS 5.5 x86", "Centos 6 x64", "Centos 6 x86",
+      for (String name : new String[]{"CentOS 5.5 x64", "CentOS 5.5 x86", "Centos 6 x64", "Centos 6 x86",
             "Debian-6 x64", "Debian 5.0.1 x64", "FreeBSD 8.2", "Gentoo 10.1 x64", "Ubuntu 8.04 x64",
-            "Ubuntu 10.04 LTS 64-bit", "Ubuntu 10.10 x64", "Ubuntu 11.04 x64" }) {
-         expectedBuilder.add(new OSTemplate(name, 5, 512, name.startsWith("FreeBSD") ? "freebsd" : "linux", "Xen"));
+            "Ubuntu 10.04 LTS 64-bit", "Ubuntu 10.10 x64", "Ubuntu 11.04 x64"}) {
+         expectedBuilder.add(OSTemplate.builder().name(name).minDiskSize(5).minMemSize(512)
+               .os(name.startsWith("FreeBSD") ? "freebsd" : "linux").platform("Xen").build());
       }
-      for (String name : new String[] { "Windows Server 2008 R2 x64 std", "Windows Server 2008 R2 x64 web",
-            "Windows Server 2008 x64 web", "Windows Server 2008 x86 web" }) {
-         expectedBuilder.add(new OSTemplate(name, 20, 1024, "windows", "Xen"));
+      for (String name : new String[]{"Windows Server 2008 R2 x64 std", "Windows Server 2008 R2 x64 web",
+            "Windows Server 2008 x64 web", "Windows Server 2008 x86 web"}) {
+         expectedBuilder.add(OSTemplate.builder().name(name).minDiskSize(20).minMemSize(1024).os("windows").platform("Xen").build());
       }
-      
+
       assertEquals(client.listTemplates(), expectedBuilder.build());
    }
 
@@ -172,11 +160,11 @@
    }
 
    public static ServerDetails expectedServerDetails() {
-      Ip ip = Ip.builder().version4().ip("109.74.10.45").cost(2.0).build();
-      Cost cost = Cost.builder().amount(13.22).currency("EUR").timePeriod("month").build();
-      return ServerDetails.builder().id("xm3276891").transferGB(50).hostname("glesys-s-6dd").cpuCores(1).memorySizeMB(512)
-            .diskSizeGB(5).description("glesys-s-6dd").datacenter("Falkenberg").platform("Xen")
-            .templateName("Ubuntu 11.04 x64").state(Server.State.LOCKED).cost(cost).ips(ip).build();
+      Ip ip = Ip.builder().version4().ip("31.192.231.254").version4().cost(2.0).currency("EUR").build();
+      Cost cost = Cost.builder().amount(10.22).currency("EUR").timePeriod("month").build();
+      return ServerDetails.builder().id("vz1840356").transferGB(50).hostname("test-email-jclouds").cpuCores(1).memorySizeMB(512)
+            .diskSizeGB(5).datacenter("Falkenberg").description("glesys-s-6dd").platform("OpenVZ")
+            .templateName("Ubuntu 10.04 LTS 32-bit").state(Server.State.RUNNING).cost(cost).ips(ip).build();
    }
 
    @Test
@@ -262,10 +250,14 @@
                         .put("Accept", "application/json")
                         .put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
                   .payload(newUrlEncodedFormPayload(ImmutableMultimap.<String, String>builder()
-                        .put("serverid", "xm3276891").build())).build(),
+                        .put("serverid", "xm3276891")
+                        .put("description", "this is a different description!")
+                        .put("hostname", "new-hostname")
+                        .build())).build(),
             HttpResponse.builder().statusCode(206).build()).getServerClient();
 
-      client.editServer("xm3276891");
+      client.editServer("xm3276891", EditServerOptions.Builder.description("this is a different description!"),
+            EditServerOptions.Builder.hostname("new-hostname"));
    }
 
    @Test
@@ -290,7 +282,7 @@
 
       client.editServer("xm3276891", options);
    }
-   
+
    @Test
    public void testCloneServerWhenResponseIs2xx() throws Exception {
       ServerClient client = requestSendsResponse(
@@ -302,7 +294,7 @@
                         .put("serverid", "xm3276891")
                         .put("hostname", "hostname1").build())).build(),
             HttpResponse.builder().statusCode(200).payload(payloadFromResource("/server_details.json")).build()).getServerClient();
-      
+
       assertEquals(client.cloneServer("xm3276891", "hostname1"), expectedServerDetails());
    }
 
@@ -534,7 +526,7 @@
                   .headers(ImmutableMultimap.<String, String>builder()
                         .put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
                   .payload(newUrlEncodedFormPayload(ImmutableMultimap.<String, String>builder()
-                        .put("serverid", "server777").put("keepip", "1").build())).build(),
+                        .put("serverid", "server777").put("keepip", "true").build())).build(),
             HttpResponse.builder().statusCode(200).build())
             .getServerClient();
 
@@ -548,21 +540,63 @@
                   .headers(ImmutableMultimap.<String, String>builder()
                         .put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
                   .payload(newUrlEncodedFormPayload(ImmutableMultimap.<String, String>builder()
-                        .put("serverid", "server777").put("keepip", "0").build())).build(),
+                        .put("serverid", "server777").put("keepip", "false").build())).build(),
             HttpResponse.builder().statusCode(401).build())
             .getServerClient();
 
       client.destroyServer("server777", DestroyServerOptions.Builder.discardIp());
    }
 
+   public void testResourceUsageWhenResponseIs2xx() throws Exception {
+      ServerClient client = requestSendsResponse(
+            HttpRequest.builder().method("POST").endpoint(URI.create("https://api.glesys.com/server/resourceusage/format/json"))
+                  .headers(ImmutableMultimap.<String, String>builder()
+                        .put("Accept", "application/json")
+                        .put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
+                  .payload(newUrlEncodedFormPayload(ImmutableMultimap.<String, String>builder()
+                        .put("serverid", "server777").put("resolution", "minute").put("resource", "diskioread").build())).build(),
+            HttpResponse.builder().statusCode(200)
+                  .payload(payloadFromResourceWithContentType("/server_resource_usage.json", MediaType.APPLICATION_JSON))
+                  .build())
+            .getServerClient();
+
+      ResourceUsage expected = ResourceUsage.builder().info(
+            ResourceUsageInfo.builder().resolution("minute").resource("diskioread").unit("KB").build())
+            .values(
+                  ResourceUsageValue.builder().value(0.0).timestamp(dateService.iso8601SecondsDateParse("2012-06-24T14:21:07+02:00")).build(),
+                  ResourceUsageValue.builder().value(5.1).timestamp(dateService.iso8601SecondsDateParse("2012-06-24T14:22:05+02:00")).build(),
+                  ResourceUsageValue.builder().value(0.0).timestamp(dateService.iso8601SecondsDateParse("2012-06-24T14:23:05+02:00")).build(),
+                  ResourceUsageValue.builder().value(10.0).timestamp(dateService.iso8601SecondsDateParse("2012-06-24T14:24:08+02:00")).build(),
+                  ResourceUsageValue.builder().value(0.0).timestamp(dateService.iso8601SecondsDateParse("2012-06-24T14:25:12+02:00")).build(),
+                  ResourceUsageValue.builder().value(0.0).timestamp(dateService.iso8601SecondsDateParse("2012-06-24T14:26:07+02:00")).build(),
+                  ResourceUsageValue.builder().value(0.0).timestamp(dateService.iso8601SecondsDateParse("2012-06-24T14:27:12+02:00")).build(),
+                  ResourceUsageValue.builder().value(0.0).timestamp(dateService.iso8601SecondsDateParse("2012-06-24T14:28:05+02:00")).build()
+            ).build();
+      assertEquals(client.getResourceUsage("server777", "diskioread", "minute").toString(), expected.toString());
+   }
+
+   @Test(expectedExceptions = {AuthorizationException.class})
+   public void testResouceUsageWhenResponseIs4xx() throws Exception {
+      ServerClient client = requestSendsResponse(
+            HttpRequest.builder().method("POST").endpoint(URI.create("https://api.glesys.com/server/resourceusage/format/json"))
+                  .headers(ImmutableMultimap.<String, String>builder()
+                        .put("Accept", "application/json")
+                        .put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build())
+                  .payload(newUrlEncodedFormPayload(ImmutableMultimap.<String, String>builder()
+                        .put("serverid", "server777").put("resolution", "minute").put("resource", "diskioread").build())).build(),
+            HttpResponse.builder().statusCode(401).build())
+            .getServerClient();
+
+      client.getResourceUsage("server777", "diskioread", "minute");
+   }
 
    private ServerStatus expectedServerStatus() {
-      ResourceUsage cpu = ResourceUsage.builder().unit("cores").max(1.0).usage(0.0).build();
-      ResourceUsage disk = ResourceUsage.builder().unit("GB").usage(0.0).max(5).build();
-      ResourceUsage memory = ResourceUsage.builder().unit("MB").usage(0.0).max(512).build();
-      ServerUptime uptime = ServerUptime.builder().current(0).unit("seconds").build();
+      ResourceStatus cpu = ResourceStatus.builder().unit("cores").max(1.0).usage(0.0).build();
+      ResourceStatus disk = ResourceStatus.builder().unit("MB").usage(0.0).max(5120).build();
+      ResourceStatus memory = ResourceStatus.builder().unit("MB").usage(2.0).max(512).build();
+      ServerUptime uptime = ServerUptime.builder().current(21).unit("seconds").build();
       return ServerStatus.builder().state(Server.State.RUNNING).uptime(uptime).
             cpu(cpu).disk(disk).memory(memory).build();
    }
-   
+
 }
diff --git a/labs/glesys/src/test/java/org/jclouds/glesys/features/ServerClientLiveTest.java b/labs/glesys/src/test/java/org/jclouds/glesys/features/ServerClientLiveTest.java
index 6ff5e39..033a869 100644
--- a/labs/glesys/src/test/java/org/jclouds/glesys/features/ServerClientLiveTest.java
+++ b/labs/glesys/src/test/java/org/jclouds/glesys/features/ServerClientLiveTest.java
@@ -26,21 +26,14 @@
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
 
-import org.jclouds.glesys.domain.AllowedArgumentsForCreateServer;
-import org.jclouds.glesys.domain.Console;
-import org.jclouds.glesys.domain.OSTemplate;
-import org.jclouds.glesys.domain.ResourceUsage;
-import org.jclouds.glesys.domain.Server;
-import org.jclouds.glesys.domain.ServerDetails;
-import org.jclouds.glesys.domain.ServerLimit;
-import org.jclouds.glesys.domain.ServerStatus;
-import org.jclouds.glesys.internal.BaseGleSYSClientLiveTest;
+import org.jclouds.glesys.domain.*;
+import org.jclouds.glesys.internal.BaseGleSYSClientWithAServerLiveTest;
 import org.jclouds.glesys.options.CloneServerOptions;
 import org.jclouds.glesys.options.DestroyServerOptions;
+import org.jclouds.glesys.options.EditServerOptions;
 import org.jclouds.glesys.options.ServerStatusOptions;
 import org.jclouds.predicates.RetryablePredicate;
 import org.testng.annotations.AfterGroups;
-import org.testng.annotations.BeforeGroups;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
@@ -52,31 +45,23 @@
  * @author Adrian Cole
  * @author Adam Lowe
  */
-@Test(groups = "live", testName = "ServerClientLiveTest")
-public class ServerClientLiveTest extends BaseGleSYSClientLiveTest {
-   public static final String testHostName1 = "jclouds-test";
+@Test(groups = "live", testName = "ServerClientLiveTest", singleThreaded = true)
+public class ServerClientLiveTest extends BaseGleSYSClientWithAServerLiveTest {
    public static final String testHostName2 = "jclouds-test2";
    
-   @BeforeGroups(groups = {"live"})
-   public void setupContext() {
-      super.setupContext();
+   @BeforeMethod
+   public void setupClient() {
       client = gleContext.getApi().getServerClient();
-      serverStatusChecker = createServer(testHostName1);
-      testServerId = serverStatusChecker.getServerId();
    }
 
    @AfterGroups(groups = {"live"})
-   public void tearDownContext() {
-      client.destroyServer(testServerId, DestroyServerOptions.Builder.discardIp());
+   public void deleteExtraServer() {
       if (testServerId2 != null) {
          client.destroyServer(testServerId2, DestroyServerOptions.Builder.discardIp());
       }
-      super.tearDownContext();
    }
 
    private ServerClient client;
-   private ServerStatusChecker serverStatusChecker;
-   private String testServerId;
    private String testServerId2;
 
    @BeforeMethod
@@ -107,7 +92,6 @@
       assert t.getTransfersInGB().size() > 0 : t;
    }
    
-   @Test
    public void testListTemplates() throws Exception {
       Set<OSTemplate> oSTemplates = client.listTemplates();
 
@@ -126,7 +110,6 @@
       assert t.getMinMemSize() > 0 : t;
     }
    
-   @Test
    public void testListServers() throws Exception {
       Set<Server> response = client.listServers();
       assertNotNull(response);
@@ -142,9 +125,8 @@
       }
    }
 
-   @Test
    public void testServerDetails() throws Exception {
-      ServerDetails details = client.getServerDetails(testServerId);
+      ServerDetails details = client.getServerDetails(serverId);
       checkServer(details);
       assertEquals("Ubuntu 10.04 LTS 32-bit", details.getTemplateName());
       assertEquals("Falkenberg", details.getDatacenter());
@@ -155,48 +137,50 @@
       assertEquals(50, details.getTransferGB());
    }
 
-   @Test
    public void testServerStatus() throws Exception {
-      ServerStatus newStatus = client.getServerStatus(testServerId);
+      ServerStatus newStatus = client.getServerStatus(serverId);
       checkStatus(newStatus);
    }
 
-   @Test(enabled=false) // TODO work a better plan
+   public void testEditServer() throws Exception {
+      ServerDetails edited = client.editServer(serverId, EditServerOptions.Builder.description("this is a different description!"));
+      assertEquals(edited.getDescription(), "this is a different description!");
+
+      edited = client.editServer(serverId, EditServerOptions.Builder.description("another description!"), EditServerOptions.Builder.hostname("host-name1"));
+      assertEquals(edited.getDescription(), "another description!");
+      assertEquals(edited.getHostname(), "host-name1");
+
+      edited = client.resetPassword(serverId, "anotherpass");
+      assertEquals(edited.getHostname(), "host-name1");
+
+      edited = client.editServer(serverId, EditServerOptions.Builder.hostname(hostName));
+      assertEquals(edited.getHostname(), hostName);
+   }
+
+   @Test
    public void testRebootServer() throws Exception {
-      long uptime = 0;
-      
-      while(uptime < 20) {
-         uptime = client.getServerStatus(testServerId).getUptime().getCurrent();
-      }
-      
-      assertTrue(uptime > 19);
-      
-      client.rebootServer(testServerId);
-      
-      Thread.sleep(1000);
+      assertTrue(serverStatusChecker.apply(Server.State.RUNNING));
 
-      uptime = client.getServerStatus(testServerId).getUptime().getCurrent();
+      client.rebootServer(serverId);
       
-      assertTrue(uptime < 20);
-
       assertTrue(serverStatusChecker.apply(Server.State.RUNNING));
    }
 
-   @Test(enabled=false) // TODO
+   @Test
    public void testStopAndStartServer() throws Exception {
-      client.stopServer(testServerId);
+      assertTrue(serverStatusChecker.apply(Server.State.RUNNING));
+
+      client.stopServer(serverId);
 
       assertTrue(serverStatusChecker.apply(Server.State.STOPPED));
 
-      client.startServer(testServerId);
+      client.startServer(serverId);
 
       assertTrue(serverStatusChecker.apply(Server.State.RUNNING));
    }
 
-
-   @Test
    public void testServerLimits() throws Exception {
-      Map<String, ServerLimit> limits = client.getServerLimits(testServerId);
+      Map<String, ServerLimit> limits = client.getServerLimits(serverId);
       assertNotNull(limits);
       for (Map.Entry<String, ServerLimit> entry : limits.entrySet()) {
          assertNotNull(entry.getKey());
@@ -210,19 +194,31 @@
       }
    }
 
-   @Test
+   public void testResourceUsage() throws Exception {
+      // test server has only been in existence for less than a minute - check all servers
+      for (Server server : client.listServers()) {
+         ResourceUsage usage = client.getResourceUsage(server.getId(), "diskioread", "minute");
+         assertEquals(usage.getInfo().getResource(), "diskioread");
+         assertEquals(usage.getInfo().getResolution(), "minute");
+
+         usage = client.getResourceUsage(server.getId(), "cpuusage", "minute");
+         assertEquals(usage.getInfo().getResource(), "cpuusage");
+         assertEquals(usage.getInfo().getResolution(), "minute");
+      }
+   }
+
    public void testConsole() throws Exception {
-      Console console = client.getConsole(testServerId);
+      Console console = client.getConsole(serverId);
       assertNotNull(console);
       assertNotNull(console.getHost());
       assertTrue(console.getPort() > 0 && console.getPort() < 65537);
       assertNotNull(console.getPassword());
    }
 
-   // takes a few minutes and requires an extra server (using 2 already)
+   // takes a few minutes and requires an extra server (used 1 already)
    @Test(enabled=false)
    public void testCloneServer() throws Exception {
-      ServerDetails testServer2 = client.cloneServer(testServerId, testHostName2, CloneServerOptions.Builder.cpucores(1));
+      ServerDetails testServer2 = client.cloneServer(serverId, testHostName2, CloneServerOptions.Builder.cpucores(1));
 
       assertNotNull(testServer2.getId());
       assertEquals(testServer2.getHostname(), "jclouds-test2");
@@ -249,11 +245,9 @@
                   return false;
                }
 
-            }, 300, 10, TimeUnit.SECONDS);
+            }, 600, 30, TimeUnit.SECONDS);
 
-      assertTrue(cloneChecker.apply(Server.State.RUNNING)
-
-      );
+      assertTrue(cloneChecker.apply(Server.State.RUNNING));
    }
 
    private void checkServer(ServerDetails server) {
@@ -273,7 +267,7 @@
       assertNotNull(status.getUptime());
 
       
-      for (ResourceUsage usage : new ResourceUsage[] { status.getCpu(), status.getDisk(), status.getMemory() }) {
+      for (ResourceStatus usage : new ResourceStatus[] { status.getCpu(), status.getDisk(), status.getMemory() }) {
          assertNotNull(usage);
          assert usage.getMax() >= 0.0 : status;
          assert usage.getUsage() >= 0.0 : status;
diff --git a/labs/glesys/src/test/java/org/jclouds/glesys/internal/BaseGleSYSAsyncClientTest.java b/labs/glesys/src/test/java/org/jclouds/glesys/internal/BaseGleSYSAsyncClientTest.java
deleted file mode 100644
index eb76755..0000000
--- a/labs/glesys/src/test/java/org/jclouds/glesys/internal/BaseGleSYSAsyncClientTest.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/**
- * Licensed to jclouds, Inc. (jclouds) under one or more
- * contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  jclouds licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.jclouds.glesys.internal;
-
-import static org.testng.Assert.assertEquals;
-
-import org.jclouds.glesys.GleSYSProviderMetadata;
-import org.jclouds.http.HttpRequest;
-import org.jclouds.http.filters.BasicAuthentication;
-import org.jclouds.providers.ProviderMetadata;
-import org.jclouds.rest.internal.BaseAsyncClientTest;
-
-/**
- * @author Adrian Cole
- */
-public abstract class BaseGleSYSAsyncClientTest<T> extends BaseAsyncClientTest<T> {
-
-   @Override
-   protected void checkFilters(HttpRequest request) {
-      assertEquals(request.getFilters().size(), 1);
-      assertEquals(request.getFilters().get(0).getClass(), BasicAuthentication.class);
-   }
-
-   @Override
-   public ProviderMetadata createProviderMetadata() {
-     return new GleSYSProviderMetadata();   
-   }
-}
diff --git a/labs/glesys/src/test/java/org/jclouds/glesys/internal/BaseGleSYSClientExpectTest.java b/labs/glesys/src/test/java/org/jclouds/glesys/internal/BaseGleSYSClientExpectTest.java
new file mode 100644
index 0000000..db29466
--- /dev/null
+++ b/labs/glesys/src/test/java/org/jclouds/glesys/internal/BaseGleSYSClientExpectTest.java
@@ -0,0 +1,37 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.glesys.internal;
+
+import org.jclouds.date.DateService;
+import org.jclouds.date.internal.SimpleDateFormatDateService;
+import org.jclouds.glesys.GleSYSClient;
+import org.jclouds.rest.internal.BaseRestClientExpectTest;
+
+/**
+ * Configures a glesys client expect test - provides a dateService
+ *
+ * @author Adam Lowe
+ */
+public class BaseGleSYSClientExpectTest extends BaseRestClientExpectTest<GleSYSClient> {
+   protected DateService dateService = new SimpleDateFormatDateService();
+
+   public BaseGleSYSClientExpectTest() {
+      provider = "glesys";
+   }
+}
\ No newline at end of file
diff --git a/labs/glesys/src/test/java/org/jclouds/glesys/internal/BaseGleSYSClientLiveTest.java b/labs/glesys/src/test/java/org/jclouds/glesys/internal/BaseGleSYSClientLiveTest.java
index 8db9fff..9c75151 100644
--- a/labs/glesys/src/test/java/org/jclouds/glesys/internal/BaseGleSYSClientLiveTest.java
+++ b/labs/glesys/src/test/java/org/jclouds/glesys/internal/BaseGleSYSClientLiveTest.java
@@ -18,24 +18,14 @@
  */
 package org.jclouds.glesys.internal;
 
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertFalse;
-import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertTrue;
 
-import java.util.UUID;
 import java.util.concurrent.TimeUnit;
 
 import org.jclouds.compute.internal.BaseComputeServiceContextLiveTest;
 import org.jclouds.glesys.GleSYSAsyncClient;
 import org.jclouds.glesys.GleSYSClient;
-import org.jclouds.glesys.domain.Server;
-import org.jclouds.glesys.domain.ServerDetails;
-import org.jclouds.glesys.domain.ServerSpec;
-import org.jclouds.glesys.domain.ServerStatus;
 import org.jclouds.glesys.features.DomainClient;
-import org.jclouds.glesys.features.ServerClient;
-import org.jclouds.glesys.options.ServerStatusOptions;
 import org.jclouds.predicates.RetryablePredicate;
 import org.jclouds.rest.RestContext;
 import org.testng.annotations.BeforeGroups;
@@ -77,43 +67,4 @@
       assertTrue(result.apply(before + 1));
    }
 
-   protected ServerStatusChecker createServer(String hostName) {
-      ServerClient client = gleContext.getApi().getServerClient();
-
-      ServerDetails testServer = client.createServerWithHostnameAndRootPassword(
-            ServerSpec.builder().datacenter("Falkenberg").platform("OpenVZ").templateName("Ubuntu 10.04 LTS 32-bit")
-                  .diskSizeGB(5).memorySizeMB(512).cpuCores(1).transferGB(50).build(), hostName, UUID.randomUUID()
-                  .toString().replace("-",""));
-
-      assertNotNull(testServer.getId());
-      assertEquals(testServer.getHostname(), hostName);
-      assertFalse(testServer.getIps().isEmpty());
-
-      ServerStatusChecker runningServerCounter = new ServerStatusChecker(client, testServer.getId(), 180, 10,
-            TimeUnit.SECONDS);
-
-      assertTrue(runningServerCounter.apply(Server.State.RUNNING));
-      return runningServerCounter;
-   }
-
-   public static class ServerStatusChecker extends RetryablePredicate<Server.State> {
-      private final String serverId;
-
-      public String getServerId() {
-         return serverId;
-      }
-
-      public ServerStatusChecker(final ServerClient client, final String serverId, long maxWait, long period,
-            TimeUnit unit) {
-         super(new Predicate<Server.State>() {
-
-            public boolean apply(Server.State value) {
-               ServerStatus status = client.getServerStatus(serverId, ServerStatusOptions.Builder.state());
-               return status.getState() == value;
-            }
-
-         }, maxWait, period, unit);
-         this.serverId = serverId;
-      }
-   }
 }
diff --git a/labs/glesys/src/test/java/org/jclouds/glesys/internal/BaseGleSYSClientWithAServerLiveTest.java b/labs/glesys/src/test/java/org/jclouds/glesys/internal/BaseGleSYSClientWithAServerLiveTest.java
new file mode 100644
index 0000000..61436cd
--- /dev/null
+++ b/labs/glesys/src/test/java/org/jclouds/glesys/internal/BaseGleSYSClientWithAServerLiveTest.java
@@ -0,0 +1,128 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.glesys.internal;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+
+import java.util.UUID;
+import java.util.concurrent.TimeUnit;
+
+import org.jclouds.glesys.domain.Server;
+import org.jclouds.glesys.domain.ServerDetails;
+import org.jclouds.glesys.domain.ServerSpec;
+import org.jclouds.glesys.domain.ServerStatus;
+import org.jclouds.glesys.features.DomainClient;
+import org.jclouds.glesys.features.ServerClient;
+import org.jclouds.glesys.options.DestroyServerOptions;
+import org.jclouds.glesys.options.ServerStatusOptions;
+import org.jclouds.predicates.RetryablePredicate;
+import org.testng.annotations.AfterGroups;
+import org.testng.annotations.BeforeGroups;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Predicate;
+
+/**
+ * Tests behavior of {@code GleSYSClient}
+ * 
+ * @author Adrian Cole, Adam Lowe
+ */
+@Test(groups = "live", singleThreaded = true)
+public class BaseGleSYSClientWithAServerLiveTest extends BaseGleSYSClientLiveTest {
+   protected String serverId;
+   protected String hostName = "test-server-jclouds";
+   protected ServerStatusChecker serverStatusChecker;
+
+   public BaseGleSYSClientWithAServerLiveTest() {
+      provider = "glesys";
+   }
+
+   @BeforeGroups(groups = { "integration", "live" })
+   @Override
+   public final void setupContext() {
+      assertNull(serverId, "This method should be called EXACTLY once per run");
+      super.setupContext();
+      serverStatusChecker = createServer(hostName);
+      serverId = serverStatusChecker.getServerId();
+   }
+
+   @AfterGroups(groups = {"integration", "live"})
+   @Override
+   public final void tearDownContext() {
+      gleContext.getApi().getServerClient().destroyServer(serverId, DestroyServerOptions.Builder.discardIp());
+      super.tearDownContext();
+   }
+
+   protected void createDomain(String domain) {
+      final DomainClient client = gleContext.getApi().getDomainClient();
+      int before = client.listDomains().size();
+      client.addDomain(domain);
+      RetryablePredicate<Integer> result = new RetryablePredicate<Integer>(new Predicate<Integer>() {
+         public boolean apply(Integer value) {
+            return client.listDomains().size() == value;
+         }
+      }, 30, 1, TimeUnit.SECONDS);
+
+      assertTrue(result.apply(before + 1));
+   }
+
+   protected ServerStatusChecker createServer(String hostName) {
+      ServerClient client = gleContext.getApi().getServerClient();
+
+      ServerDetails testServer = client.createServerWithHostnameAndRootPassword(
+            ServerSpec.builder().datacenter("Falkenberg").platform("OpenVZ").templateName("Ubuntu 10.04 LTS 32-bit")
+                  .diskSizeGB(5).memorySizeMB(512).cpuCores(1).transferGB(50).build(), hostName, UUID.randomUUID()
+                  .toString().replace("-",""));
+
+      assertNotNull(testServer.getId());
+      assertEquals(testServer.getHostname(), hostName);
+      assertFalse(testServer.getIps().isEmpty());
+
+      ServerStatusChecker runningServerCounter = new ServerStatusChecker(client, testServer.getId(), 180, 10,
+            TimeUnit.SECONDS);
+
+      assertTrue(runningServerCounter.apply(Server.State.RUNNING));
+      return runningServerCounter;
+   }
+
+   public static class ServerStatusChecker extends RetryablePredicate<Server.State> {
+      private final String serverId;
+
+      public String getServerId() {
+         return serverId;
+      }
+
+      public ServerStatusChecker(final ServerClient client, final String serverId, long maxWait, long period,
+            TimeUnit unit) {
+         super(new Predicate<Server.State>() {
+
+            public boolean apply(Server.State value) {
+               ServerStatus status = client.getServerStatus(serverId, ServerStatusOptions.Builder.state());
+               return status.getState() == value;
+            }
+
+         }, maxWait, period, unit);
+         this.serverId = serverId;
+      }
+   }
+}
\ No newline at end of file
diff --git a/labs/glesys/src/test/java/org/jclouds/glesys/parse/ParseFullIpDetailsTest.java b/labs/glesys/src/test/java/org/jclouds/glesys/parse/ParseFullIpDetailsTest.java
index 81c7b86..6f4ae89 100644
--- a/labs/glesys/src/test/java/org/jclouds/glesys/parse/ParseFullIpDetailsTest.java
+++ b/labs/glesys/src/test/java/org/jclouds/glesys/parse/ParseFullIpDetailsTest.java
@@ -23,6 +23,7 @@
 import javax.ws.rs.core.MediaType;
 
 import org.jclouds.glesys.config.GleSYSParserModule;
+import org.jclouds.glesys.domain.Cost;
 import org.jclouds.glesys.domain.IpDetails;
 import org.jclouds.json.BaseItemParserTest;
 import org.jclouds.json.config.GsonModule;
@@ -42,9 +43,11 @@
    @SelectJson("details")
    @Consumes(MediaType.APPLICATION_JSON)
    public IpDetails expected() {
-      return IpDetails.builder().datacenter("Falkenberg").ipversion("4").platform("Xen")
-              .ptr("109-74-10-146-static.serverhotell.net.").address("109.74.10.146").netmask("255.255.254.0")
-              .broadcast("109.74.11.255").gateway("109.74.10.1").nameServers("79.99.4.100", "79.99.4.103").build();
+      return IpDetails.builder().datacenter("Falkenberg").version4()
+            .address("109.74.10.13")
+            .platform("Xen").ptr("109-74-10-13-static.serverhotell.net.").netmask("255.255.254.0").broadcast("109.74.11.255")
+            .gateway("109.74.10.1").nameServers("79.99.4.100", "79.99.4.101")
+            .cost(Cost.builder().amount(2.0).currency("EUR").timePeriod("month").build()).build();
    }
 
    protected Injector injector() {
diff --git a/labs/glesys/src/test/java/org/jclouds/glesys/parse/ParseIpAddressFromResponseTest.java b/labs/glesys/src/test/java/org/jclouds/glesys/parse/ParseIpAddressFromResponseTest.java
index 6ad2e03..242b005 100644
--- a/labs/glesys/src/test/java/org/jclouds/glesys/parse/ParseIpAddressFromResponseTest.java
+++ b/labs/glesys/src/test/java/org/jclouds/glesys/parse/ParseIpAddressFromResponseTest.java
@@ -18,9 +18,6 @@
  */
 package org.jclouds.glesys.parse;
 
-import static java.util.Arrays.asList;
-
-import java.util.HashSet;
 import java.util.Set;
 
 import javax.ws.rs.Consumes;
@@ -32,22 +29,31 @@
 import org.jclouds.rest.annotations.SelectJson;
 import org.testng.annotations.Test;
 
+import com.google.common.collect.ImmutableSet;
 import com.google.inject.Guice;
 import com.google.inject.Injector;
 
 @Test(groups = "unit", testName = "ParseIpAddressFromResponseTest")
 public class ParseIpAddressFromResponseTest extends BaseSetParserTest<String> {
-
+   public static final Set<String> EXPECTED_IPS = ImmutableSet.of("109.74.10.13", "109.74.10.50", "109.74.10.109", "109.74.10.125",
+         "109.74.10.131", "109.74.10.148", "109.74.10.171", "109.74.10.173", "109.74.10.191", "109.74.10.215",
+         "109.74.10.216", "109.74.10.219", "109.74.10.223", "109.74.10.224", "109.74.10.236", "109.74.10.249",
+         "109.74.11.49", "109.74.11.58", "109.74.11.62", "109.74.11.63", "109.74.11.73", "109.74.11.76",
+         "109.74.11.86", "109.74.11.98", "109.74.11.118", "109.74.11.124", "109.74.11.131", "109.74.11.137",
+         "109.74.11.146", "109.74.11.157", "109.74.11.159", "109.74.11.173", "109.74.11.178", "109.74.11.187",
+         "109.74.11.190", "109.74.11.205", "109.74.11.213", "109.74.11.234", "109.74.11.236", "109.74.11.241",
+         "109.74.11.243", "109.74.11.246", "109.74.11.247");
+   
    @Override
    public String resource() {
       return "/ip_list_free.json";
    }
 
    @Override
-   @SelectJson("iplist")
+   @SelectJson("ipaddresses")
    @Consumes(MediaType.APPLICATION_JSON)
    public Set<String> expected() {
-      return new HashSet<String>(asList("31.192.226.131", "31.192.226.133"));
+      return EXPECTED_IPS;
    }
 
    protected Injector injector() {
diff --git a/labs/glesys/src/test/resources/archive_details.json b/labs/glesys/src/test/resources/archive_details.json
index 04be81f..ed0f6b8 100644
--- a/labs/glesys/src/test/resources/archive_details.json
+++ b/labs/glesys/src/test/resources/archive_details.json
@@ -1 +1 @@
-{"response":{"status":{"code":"200","text":"OK"},"details":{"username":"xxxxxx_test1","size_total":"30 GB","size_free":"30 GB","locked":false},"debug":{"input":{"username":"xxxxxx_test1"}}}}
\ No newline at end of file
+{"response":{"status":{"code":"200","text":"OK"},"details":{"username":"xxxxxx_test1","sizetotal":"30 GB","sizefree":"30 GB","locked":false},"debug":{"input":{"username":"xxxxxx_test1"}}}}
\ No newline at end of file
diff --git a/labs/glesys/src/test/resources/archive_list.json b/labs/glesys/src/test/resources/archive_list.json
index 0144c66..31662ff 100644
--- a/labs/glesys/src/test/resources/archive_list.json
+++ b/labs/glesys/src/test/resources/archive_list.json
@@ -1 +1 @@
-{"response":{"status":{"code":"200","text":"OK"},"archives":[{"username":"xxxxx_test1","size_total":"30 GB","size_free":"30 GB","locked":false}],"debug":{"input":[]}}}
\ No newline at end of file
+{"response":{"status":{"code":"200","text":"OK"},"archives":[{"username":"xxxxx_test1","sizetotal":"30 GB","sizefree":"20 GB","locked":false}],"debug":{"input":[]}}}
\ No newline at end of file
diff --git a/labs/glesys/src/test/resources/domain_details.json b/labs/glesys/src/test/resources/domain_details.json
new file mode 100644
index 0000000..2f1afbe
--- /dev/null
+++ b/labs/glesys/src/test/resources/domain_details.json
@@ -0,0 +1 @@
+{"response":{"status":{"code":200,"timestamp":"2012-06-24T11:52:49+02:00","text":"Domain added"},"domain":{"domainname":"cl13016-domain.jclouds.org","createtime":"2012-06-24T11:52:49+02:00","recordcount":9,"usingglesysnameserver":"no"},"debug":{"input":{"domainname":"cl13016-domain.jclouds.org"}}}}
\ No newline at end of file
diff --git a/labs/glesys/src/test/resources/domain_list_records.json b/labs/glesys/src/test/resources/domain_list_records.json
index 96d0277..3bee5f2 100644
--- a/labs/glesys/src/test/resources/domain_list_records.json
+++ b/labs/glesys/src/test/resources/domain_list_records.json
@@ -1 +1 @@
-{"response":{"status":{"code":200,"text":"OK"},"records":[{"id":224546,"domainname":"testglesys.jclouds.org","host":"@","type":"TXT","data":"v=spf1 include:spf.glesys.se -all","ttl":3600},{"id":224545,"domainname":"testglesys.jclouds.org","host":"@","type":"MX","data":"20 mx02.glesys.se.","ttl":3600},{"id":224543,"domainname":"testglesys.jclouds.org","host":"mail","type":"A","data":"79.99.4.40","ttl":3600},{"id":224544,"domainname":"testglesys.jclouds.org","host":"@","type":"MX","data":"10 mx01.glesys.se.","ttl":3600},{"id":224542,"domainname":"testglesys.jclouds.org","host":"www","type":"A","data":"127.0.0.1","ttl":3600},{"id":224541,"domainname":"testglesys.jclouds.org","host":"@","type":"A","data":"127.0.0.1","ttl":3600},{"id":224540,"domainname":"testglesys.jclouds.org","host":"@","type":"NS","data":"ns3.namesystem.se.","ttl":3600},{"id":224539,"domainname":"testglesys.jclouds.org","host":"@","type":"NS","data":"ns2.namesystem.se.","ttl":3600},{"id":224538,"domainname":"testglesys.jclouds.org","host":"@","type":"NS","data":"ns1.namesystem.se.","ttl":3600}],"debug":{"input":{"domainname":"testglesys.jclouds.org"}}}}
\ No newline at end of file
+{"response":{"status":{"code":200,"text":"OK"},"records":[{"recordid":224546,"domainname":"testglesys.jclouds.org","host":"@","type":"TXT","data":"v=spf1 include:spf.glesys.se -all","ttl":3600},{"recordid":224545,"domainname":"testglesys.jclouds.org","host":"@","type":"MX","data":"20 mx02.glesys.se.","ttl":3600},{"recordid":224543,"domainname":"testglesys.jclouds.org","host":"mail","type":"A","data":"79.99.4.40","ttl":3600},{"recordid":224544,"domainname":"testglesys.jclouds.org","host":"@","type":"MX","data":"10 mx01.glesys.se.","ttl":3600},{"recordid":224542,"domainname":"testglesys.jclouds.org","host":"www","type":"A","data":"127.0.0.1","ttl":3600},{"recordid":224541,"domainname":"testglesys.jclouds.org","host":"@","type":"A","data":"127.0.0.1","ttl":3600},{"recordid":224540,"domainname":"testglesys.jclouds.org","host":"@","type":"NS","data":"ns3.namesystem.se.","ttl":3600},{"recordid":224539,"domainname":"testglesys.jclouds.org","host":"@","type":"NS","data":"ns2.namesystem.se.","ttl":3600},{"recordid":224538,"domainname":"testglesys.jclouds.org","host":"@","type":"NS","data":"ns1.namesystem.se.","ttl":3600}],"debug":{"input":{"domainname":"testglesys.jclouds.org"}}}}
\ No newline at end of file
diff --git a/labs/glesys/src/test/resources/domain_record.json b/labs/glesys/src/test/resources/domain_record.json
new file mode 100644
index 0000000..1efda7b
--- /dev/null
+++ b/labs/glesys/src/test/resources/domain_record.json
@@ -0,0 +1 @@
+{"response":{"status":{"code":200,"timestamp":"2012-06-24T11:52:51+02:00","text":"Record added."},"record":{"recordid":256151,"domainname":"cl13016-domain.jclouds.org","host":"test","type":"A","data":"127.0.0.1","ttl":3600},"debug":{"input":{"domainname":"cl13016-domain.jclouds.org","type":"A","host":"test","data":"127.0.0.1"}}}}
\ No newline at end of file
diff --git a/labs/glesys/src/test/resources/email_details.json b/labs/glesys/src/test/resources/email_details.json
new file mode 100644
index 0000000..e42fd53
--- /dev/null
+++ b/labs/glesys/src/test/resources/email_details.json
@@ -0,0 +1 @@
+{"response":{"status":{"code":200,"timestamp":"2012-06-20T12:01:01+02:00","text":"Account created"},"emailaccount":{"emailaccount":"test@CL13016.jclouds.org","quota":{"max":200,"unit":"MB"},"antispamlevel":3,"antivirus":"yes","autorespond":"no","autorespondmessage":null,"autorespondsaveemail":"yes","created":"2012-06-20T12:01:01+02:00","modified":null},"debug":{"input":{"emailaccount":"test@CLXXXX.jclouds.org","password":"password","antivirus":"1","autorespond":"1","autorespondmessage":"out of office"}}}}
diff --git a/labs/glesys/src/test/resources/email_list.json b/labs/glesys/src/test/resources/email_list.json
index b970ad8..5e37373 100644
--- a/labs/glesys/src/test/resources/email_list.json
+++ b/labs/glesys/src/test/resources/email_list.json
@@ -1 +1,12 @@
-{"response":{"status":{"code":"200","text":"OK"},"list":{"emailaccounts":[{"emailaccount":"test2@adamlowe.net","quota":"200 MB","usedquota":"0 MB","antispamlevel":3,"antivirus":true,"autorespond":false,"autorespondmessage":false,"autorespondsaveemail":true,"created":"2011-12-22 12:14:29","modified":"2011-12-22 12:14:31"},{"emailaccount":"test@adamlowe.net","quota":"200 MB","usedquota":"0 MB","antispamlevel":3,"antivirus":true,"autorespond":false,"autorespondmessage":false,"autorespondsaveemail":true,"created":"2011-12-22 12:13:14","modified":"2011-12-22 12:13:35"}]},"debug":{"input":{"domain":"adamlowe.net"}}}}
\ No newline at end of file
+{"response":{
+    "status":{"code":200,"timestamp":"2012-06-24T11:53:55+02:00","text":"OK"},
+    "list":{
+        "emailaccounts":[
+            {"emailaccount":"test1@cl13016.test.jclouds.org","quota":{"max":200,"unit":"MB"},"antispamlevel":3,"antivirus":"yes","autorespond":"no","autorespondmessage":null,"autorespondsaveemail":"yes","created":"2012-06-24T11:53:45+02:00","modified":null},
+            {"emailaccount":"test@cl13016.test.jclouds.org","quota":{"max":200,"unit":"MB"},"antispamlevel":3,"antivirus":"no","autorespond":"no","autorespondmessage":null,"autorespondsaveemail":"yes","created":"2012-06-24T11:53:44+02:00","modified":"2012-06-24T11:53:48+02:00"}
+        ],
+        "emailaliases":[
+            {"emailalias":"test2@cl13016.test.jclouds.org","goto":"test1@cl13016.test.jclouds.org"}
+        ]
+    },
+    "debug":{"input":{"domainname":"cl13016.test.jclouds.org"}}}}
\ No newline at end of file
diff --git a/labs/glesys/src/test/resources/email_overview.json b/labs/glesys/src/test/resources/email_overview.json
index d98a8c8..4285814 100644
--- a/labs/glesys/src/test/resources/email_overview.json
+++ b/labs/glesys/src/test/resources/email_overview.json
@@ -1 +1,6 @@
-{"response":{"status":{"code":"200","text":"OK"},"summary":{"accounts":2,"maxaccounts":"50","aliases":0,"maxaliases":1000},"domains":[{"domain":"adamlowe.net","accounts":2,"aliases":0}],"debug":{"input":[]}}}
\ No newline at end of file
+{"response":{"status":{"code":200,"timestamp":"2012-06-24T11:53:53+02:00","text":"OK"},
+    "overview":{
+        "summary":{"accounts":2,"maxaccounts":50,"aliases":1,"maxaliases":1000},
+        "domains":[{
+            "domainname":"cl13016.test.jclouds.org","accounts":2,"aliases":1,"usingglesysemailserver":"no"}]},
+    "debug":{"input":[]}}}"
diff --git a/labs/glesys/src/test/resources/ip_get_details.json b/labs/glesys/src/test/resources/ip_get_details.json
index ec6724e..e3e3bab 100644
--- a/labs/glesys/src/test/resources/ip_get_details.json
+++ b/labs/glesys/src/test/resources/ip_get_details.json
@@ -1 +1,16 @@
-{"response":{"status":{"code":"200","text":"OK"},"details":{"datacenter":"Falkenberg","ipversion":4,"PTR":"31-192-227-37-static.serverhotell.net.","platform":"OpenVZ"},"debug":{"input":{"ipaddress":"31.192.227.37"}}}}
\ No newline at end of file
+{"response":{"status":{"code":200,"timestamp":"2012-06-21T13:11:26+02:00","text":"OK"},
+    "details":{
+        "ipaddress":"31.192.227.113",
+        "netmask":null,
+        "broadcast":null,
+        "gateway":null,
+        "nameservers":["79.99.4.100","79.99.4.101"],
+        "platform":"OpenVZ",
+        "datacenter":"Falkenberg",
+        "ipversion":4,
+        "serverid":null,
+        "reserved":"no",
+        "ptr":"31-192-227-113-static.serverhotell.net.",
+        "cost":{"amount":2,"currency":"EUR","timeperiod":"month"}
+    },
+    "debug":{"input":{"ipaddress":"31.192.227.113"}}}}
\ No newline at end of file
diff --git a/labs/glesys/src/test/resources/ip_get_details_xen.json b/labs/glesys/src/test/resources/ip_get_details_xen.json
index d34ce20..d98977b 100644
--- a/labs/glesys/src/test/resources/ip_get_details_xen.json
+++ b/labs/glesys/src/test/resources/ip_get_details_xen.json
@@ -1 +1,15 @@
-{"response":{"status":{"code":"200","text":"OK"},"details":{"datacenter":"Falkenberg","ipversion":4,"PTR":"109-74-10-146-static.serverhotell.net.","address":"109.74.10.146","netmask":"255.255.254.0","broadcast":"109.74.11.255","gateway":"109.74.10.1","nameservers":["79.99.4.100","79.99.4.103"],"platform":"Xen"},"debug":{"input":{"ipaddress":"109.74.10.146"}}}}
\ No newline at end of file
+{"response":{"status":{"code":200,"timestamp":"2012-06-21T13:11:25+02:00","text":"OK"},
+    "details":{
+        "ipaddress":"109.74.10.13",
+        "netmask":"255.255.254.0",
+        "broadcast":"109.74.11.255",
+        "gateway":"109.74.10.1",
+        "nameservers":["79.99.4.100","79.99.4.101"],
+        "platform":"Xen",
+        "datacenter":"Falkenberg",
+        "ipversion":4,
+        "serverid":null,
+        "reserved":"no",
+        "ptr":"109-74-10-13-static.serverhotell.net.",
+        "cost":{"amount":2,"currency":"EUR","timeperiod":"month"}},
+    "debug":{"input":{"ipaddress":"109.74.10.13"}}}}
diff --git a/labs/glesys/src/test/resources/ip_list_free.json b/labs/glesys/src/test/resources/ip_list_free.json
index 3583d21..f4fe353 100644
--- a/labs/glesys/src/test/resources/ip_list_free.json
+++ b/labs/glesys/src/test/resources/ip_list_free.json
@@ -1 +1 @@
-{"response":{"status":{"code":"200","text":"OK"},"ipinfo":{"ipversion":4,"datacenter":"Falkenberg","platform":"OpenVZ"},"iplist":["31.192.226.131","31.192.226.133"],"debug":{"input":{"ipversion":"4","datacenter":"Falkenberg","platform":"OpenVZ"}}}}
\ No newline at end of file
+{"response":{"status":{"code":200,"timestamp":"2012-06-21T13:01:55+02:00","text":"OK"},"iplist":{"ipversion":4,"datacenter":"Falkenberg","platform":"Xen","ipaddresses":["109.74.10.13","109.74.10.50","109.74.10.109","109.74.10.125","109.74.10.131","109.74.10.148","109.74.10.171","109.74.10.173","109.74.10.191","109.74.10.215","109.74.10.216","109.74.10.219","109.74.10.223","109.74.10.224","109.74.10.236","109.74.10.249","109.74.11.49","109.74.11.58","109.74.11.62","109.74.11.63","109.74.11.73","109.74.11.76","109.74.11.86","109.74.11.98","109.74.11.118","109.74.11.124","109.74.11.131","109.74.11.137","109.74.11.146","109.74.11.157","109.74.11.159","109.74.11.173","109.74.11.178","109.74.11.187","109.74.11.190","109.74.11.205","109.74.11.213","109.74.11.234","109.74.11.236","109.74.11.241","109.74.11.243","109.74.11.246","109.74.11.247"]},"debug":{"input":{"ipversion":"4","datacenter":"Falkenberg","platform":"Xen"}}}}
\ No newline at end of file
diff --git a/labs/glesys/src/test/resources/ip_list_own.json b/labs/glesys/src/test/resources/ip_list_own.json
new file mode 100644
index 0000000..4d1818b
--- /dev/null
+++ b/labs/glesys/src/test/resources/ip_list_own.json
@@ -0,0 +1,5 @@
+{"response":{"status":{"code":200,"timestamp":"2012-06-24T11:54:43+02:00","text":"OK"},
+    "iplist":[
+        {"ipaddress":"31.192.230.68","netmask":null,"broadcast":null,"gateway":null,"nameservers":["79.99.4.100","79.99.4.101"],"platform":"OpenVZ","datacenter":"Falkenberg","ipversion":4,"serverid":null,"reserved":"yes","ptr":"31-192-230-68-static.serverhotell.net.","cost":{"amount":2,"currency":"EUR","timeperiod":"month"}},
+        {"ipaddress":"31.192.231.148","netmask":null,"broadcast":null,"gateway":null,"nameservers":["79.99.4.100","79.99.4.101"],"platform":"OpenVZ","datacenter":"Falkenberg","ipversion":4,"serverid":"vz1609110","reserved":"yes","ptr":"31-192-231-148-static.serverhotell.net.","cost":{"amount":2,"currency":"EUR","timeperiod":"month"}}
+    ],"debug":{"input":[]}}}
\ No newline at end of file
diff --git a/labs/glesys/src/test/resources/server_created.json b/labs/glesys/src/test/resources/server_created.json
deleted file mode 100644
index 417d5ce..0000000
--- a/labs/glesys/src/test/resources/server_created.json
+++ /dev/null
@@ -1 +0,0 @@
-{"response":{"status":{"code":"200","text":"OK"},"server":{"serverid":"xm3630641","hostname":"jclouds-test-host","iplist":[{"ip":"109.74.10.27","version":"4","cost":"2.00"}]},"debug":{"input":{"cpucores":"1","memorysize":"512","datacenter":"Falkenberg","transfer":"500","rootpw":"password","hostname":"jclouds-test-host","platform":"Xen","template":"Debian-6 x64","disksize":"10","description":""}}}}
diff --git a/labs/glesys/src/test/resources/server_details.json b/labs/glesys/src/test/resources/server_details.json
index 2083d23..55a79c4 100644
--- a/labs/glesys/src/test/resources/server_details.json
+++ b/labs/glesys/src/test/resources/server_details.json
@@ -1 +1,16 @@
-{"response":{"status":{"code":200,"timestamp":"2012-02-10T11:11:05+01:00","text":"OK"},"server":{"serverid":"xm3276891","hostname":"glesys-s-6dd","description":"glesys-s-6dd","cpucores":1,"memorysize":512,"disksize":5,"transfer":50,"templatename":"Ubuntu 11.04 x64","datacenter":"Falkenberg","managedhosting":"no","platform":"Xen","cost":{"amount":13.22,"currency":"EUR","timeperiod":"month"},"iplist":[{"ipaddress":"109.74.10.45","version":4,"cost":2,"currency":"EUR"}], "state":"locked"},"debug":{"input":{"serverid":"xm3276891"}}}}
\ No newline at end of file
+{"response":{"status":{"code":200,"timestamp":"2012-06-21T14:10:57+02:00","text":"OK"},
+    "server":{
+        "serverid":"vz1840356",
+        "hostname":"test-email-jclouds",
+        "description":"glesys-s-6dd",
+        "cpucores":1,
+        "memorysize":512,
+        "disksize":5,
+        "transfer":50,
+        "templatename":"Ubuntu 10.04 LTS 32-bit",
+        "datacenter":"Falkenberg",
+        "managedhosting":"no",
+        "platform":"OpenVZ",
+        "cost":{"amount":10.22,"currency":"EUR","timeperiod":"month"},
+        "iplist":[{"ipaddress":"31.192.231.254","version":4,"cost":2,"currency":"EUR"}],"state":"running"},
+        "debug":{"input":{"includestate":"1","serverid":"vz1840356"}}}}
\ No newline at end of file
diff --git a/labs/glesys/src/test/resources/server_resource_usage.json b/labs/glesys/src/test/resources/server_resource_usage.json
new file mode 100644
index 0000000..744e3b0
--- /dev/null
+++ b/labs/glesys/src/test/resources/server_resource_usage.json
@@ -0,0 +1,16 @@
+{"response":{
+    "status":{"code":200,"timestamp":"2012-06-24T14:28:10+02:00","text":"OK"},
+    "usage":{
+        "info":{"type":"diskioread","resolution":"minute","unit":"KB"},
+        "values":[
+            {"timestamp":"2012-06-24T14:21:07+02:00","value":0},
+            {"timestamp":"2012-06-24T14:22:05+02:00","value":5.1},
+            {"timestamp":"2012-06-24T14:23:05+02:00","value":0},
+            {"timestamp":"2012-06-24T14:24:08+02:00","value":10},
+            {"timestamp":"2012-06-24T14:25:12+02:00","value":0},
+            {"timestamp":"2012-06-24T14:26:07+02:00","value":0},
+            {"timestamp":"2012-06-24T14:27:12+02:00","value":0},
+            {"timestamp":"2012-06-24T14:28:05+02:00","value":0}
+        ]
+    },
+    "debug":{"input":{"serverid":"vz1166090","resolution":"minute","resource":"diskioread"}}}}
\ No newline at end of file
diff --git a/labs/glesys/src/test/resources/server_status.json b/labs/glesys/src/test/resources/server_status.json
index 5c3ac6a..d2a7f82 100644
--- a/labs/glesys/src/test/resources/server_status.json
+++ b/labs/glesys/src/test/resources/server_status.json
@@ -1 +1 @@
-{"response":{"status":{"code":200,"timestamp":"2012-02-10T11:21:50+01:00","text":"OK"},"server":{"state":"running","cpu":{"usage":0,"max":1,"unit":"cores"},"memory":{"usage":null,"max":512,"unit":"MB"},"disk":{"usage":null,"max":5,"unit":"GB"},"transfer":{"usage":0,"max":50,"unit":"GB last 30 days"},"uptime":{"current":null,"unit":"seconds"}},"debug":{"input":{"serverid":"xm3276891"}}}}
\ No newline at end of file
+{"response":{"status":{"code":200,"timestamp":"2012-06-21T15:29:31+02:00","text":"OK"},"server":{"state":"running","cpu":{"usage":0,"max":1,"unit":"cores"},"memory":{"usage":2,"max":512,"unit":"MB"},"disk":{"usage":0,"max":5120,"unit":"MB"},"transfer":{"usage":0,"max":50,"unit":"GB last 30 days"},"uptime":{"current":21,"unit":"seconds"},"warnings":[]},"debug":{"input":{"serverid":"vz1321233"}}}}
\ No newline at end of file
diff --git a/labs/iam/pom.xml b/labs/iam/pom.xml
new file mode 100644
index 0000000..99a6e1f
--- /dev/null
+++ b/labs/iam/pom.xml
@@ -0,0 +1,108 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+  Licensed to jclouds, Inc. (jclouds) under one or more
+  contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  jclouds licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.jclouds</groupId>
+    <artifactId>jclouds-project</artifactId>
+    <version>1.5.0-SNAPSHOT</version>
+    <relativePath>../../project/pom.xml</relativePath>
+  </parent>
+  <groupId>org.jclouds.labs</groupId>
+  <artifactId>iam</artifactId>
+  <name>jcloud iam api</name>
+  <description>jclouds components to access an implementation of Identity and Access Management (IAM)</description>
+  <packaging>bundle</packaging>
+
+  <properties>
+    <test.iam.endpoint>https://iam.amazonaws.com</test.iam.endpoint>
+    <test.iam.api-version>2010-05-08</test.iam.api-version>
+    <test.iam.build-version></test.iam.build-version>
+    <test.iam.identity>${test.aws.identity}</test.iam.identity>
+    <test.iam.credential>${test.aws.credential}</test.iam.credential>
+
+    <jclouds.osgi.export>org.jclouds.iam*;version="${project.version}"</jclouds.osgi.export>
+    <jclouds.osgi.import>org.jclouds*;version="${project.version}",*</jclouds.osgi.import>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.jclouds.common</groupId>
+      <artifactId>aws-common</artifactId>
+      <version>${project.version}</version>
+      <type>jar</type>
+    </dependency>
+    <dependency>
+      <groupId>org.jclouds</groupId>
+      <artifactId>jclouds-core</artifactId>
+      <version>${project.version}</version>
+      <type>test-jar</type>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.jclouds.driver</groupId>
+      <artifactId>jclouds-log4j</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>log4j</groupId>
+      <artifactId>log4j</artifactId>
+      <version>1.2.16</version>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <profiles>
+    <profile>
+      <id>live</id>
+      <build>
+        <plugins>
+          <plugin>
+            <groupId>org.apache.maven.plugins</groupId>
+            <artifactId>maven-surefire-plugin</artifactId>
+            <executions>
+              <execution>
+                <id>integration</id>
+                <phase>integration-test</phase>
+                <goals>
+                  <goal>test</goal>
+                </goals>
+                <configuration>
+                  <systemPropertyVariables>
+                    <test.iam.endpoint>${test.iam.endpoint}</test.iam.endpoint>
+                    <test.iam.api-version>${test.iam.api-version}</test.iam.api-version>
+                    <test.iam.build-version>${test.iam.build-version}</test.iam.build-version>
+                    <test.iam.identity>${test.iam.identity}</test.iam.identity>
+                    <test.iam.credential>${test.iam.credential}</test.iam.credential>
+                  </systemPropertyVariables>
+                </configuration>
+              </execution>
+            </executions>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
+  </profiles>
+
+</project>
+
diff --git a/labs/iam/src/main/java/org/jclouds/iam/IAM.java b/labs/iam/src/main/java/org/jclouds/iam/IAM.java
new file mode 100644
index 0000000..a79fcb2
--- /dev/null
+++ b/labs/iam/src/main/java/org/jclouds/iam/IAM.java
@@ -0,0 +1,43 @@
+package org.jclouds.iam;
+
+import org.jclouds.collect.PaginatedSet;
+import org.jclouds.collect.PaginatedSets;
+import org.jclouds.iam.domain.User;
+import org.jclouds.iam.features.UserClient;
+import org.jclouds.iam.options.ListUsersOptions;
+
+import com.google.common.base.Function;
+
+/**
+ * Utilities for using IAM.
+ * 
+ * @author Adrian Cole
+ */
+public class IAM {
+
+   /**
+    * List users based on the criteria in the {@link ListUsersOptions} passed in.
+    * 
+    * @param userClient
+    *           the {@link UserClient} to use for the request
+    * @param options
+    *           the {@link ListUsersOptions} describing the ListUsers request
+    * 
+    * @return iterable of users fitting the criteria
+    */
+   public static Iterable<User> list(final UserClient userClient, final ListUsersOptions options) {
+      return PaginatedSets.lazyContinue(userClient.list(options), new Function<String, PaginatedSet<User>>() {
+
+         @Override
+         public PaginatedSet<User> apply(String input) {
+            return userClient.list(options.clone().marker(input));
+         }
+
+         @Override
+         public String toString() {
+            return "listUsers(" + options + ")";
+         }
+      });
+   }
+
+}
diff --git a/labs/iam/src/main/java/org/jclouds/iam/IAMApiMetadata.java b/labs/iam/src/main/java/org/jclouds/iam/IAMApiMetadata.java
new file mode 100644
index 0000000..639a6eb
--- /dev/null
+++ b/labs/iam/src/main/java/org/jclouds/iam/IAMApiMetadata.java
@@ -0,0 +1,95 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.iam;
+
+import static org.jclouds.aws.reference.AWSConstants.PROPERTY_AUTH_TAG;
+import static org.jclouds.aws.reference.AWSConstants.PROPERTY_HEADER_TAG;
+
+import java.net.URI;
+import java.util.Properties;
+
+import org.jclouds.apis.ApiMetadata;
+import org.jclouds.iam.config.IAMRestClientModule;
+import org.jclouds.rest.RestContext;
+import org.jclouds.rest.internal.BaseRestApiMetadata;
+
+import com.google.common.reflect.TypeToken;
+
+/**
+ * Implementation of {@link ApiMetadata} for Amazon's IAM api.
+ * 
+ * @author Adrian Cole
+ */
+public class IAMApiMetadata extends BaseRestApiMetadata {
+
+   /** The serialVersionUID */
+   private static final long serialVersionUID = 3450830053589179249L;
+
+   public static final TypeToken<RestContext<? extends IAMClient, ? extends IAMAsyncClient>> CONTEXT_TOKEN = new TypeToken<RestContext<? extends IAMClient, ? extends IAMAsyncClient>>() {
+      private static final long serialVersionUID = -5070937833892503232L;
+   };
+
+   @Override
+   public Builder toBuilder() {
+      return new Builder(getApi(), getAsyncApi()).fromApiMetadata(this);
+   }
+
+   public IAMApiMetadata() {
+      this(new Builder(IAMClient.class, IAMAsyncClient.class));
+   }
+
+   protected IAMApiMetadata(Builder builder) {
+      super(Builder.class.cast(builder));
+   }
+
+   public static Properties defaultProperties() {
+      Properties properties = BaseRestApiMetadata.defaultProperties();
+      properties.setProperty(PROPERTY_AUTH_TAG, "AWS");
+      properties.setProperty(PROPERTY_HEADER_TAG, "amz");
+      return properties;
+   }
+
+   public static class Builder extends BaseRestApiMetadata.Builder {
+
+      protected Builder(Class<?> client, Class<?> asyncClient) {
+         super(client, asyncClient);
+         id("iam")
+         .name("Amazon IAM Api")
+         .identityName("Access Key ID")
+         .credentialName("Secret Access Key")
+         .version("2010-05-08")
+         .documentation(URI.create("http://docs.amazonwebservices.com/IAM/latest/APIReference/"))
+         .defaultEndpoint("https://iam.amazonaws.com")
+         .defaultProperties(IAMApiMetadata.defaultProperties())
+         .defaultModule(IAMRestClientModule.class);
+      }
+
+      @Override
+      public IAMApiMetadata build() {
+         return new IAMApiMetadata(this);
+      }
+      
+      @Override
+      public Builder fromApiMetadata(ApiMetadata in) {
+         super.fromApiMetadata(in);
+         return this;
+      }
+   }
+
+}
\ No newline at end of file
diff --git a/labs/iam/src/main/java/org/jclouds/iam/IAMAsyncClient.java b/labs/iam/src/main/java/org/jclouds/iam/IAMAsyncClient.java
new file mode 100644
index 0000000..a1be67c
--- /dev/null
+++ b/labs/iam/src/main/java/org/jclouds/iam/IAMAsyncClient.java
@@ -0,0 +1,64 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.iam;
+
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+
+import org.jclouds.aws.filters.FormSigner;
+import org.jclouds.iam.domain.User;
+import org.jclouds.iam.features.UserAsyncClient;
+import org.jclouds.iam.xml.UserHandler;
+import org.jclouds.rest.annotations.Delegate;
+import org.jclouds.rest.annotations.FormParams;
+import org.jclouds.rest.annotations.RequestFilters;
+import org.jclouds.rest.annotations.VirtualHost;
+import org.jclouds.rest.annotations.XMLResponseParser;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ * Provides access to Amazon IAM via the Query API
+ * <p/>
+ * 
+ * @see <a
+ *      href="http://docs.amazonwebservices.com/IAM/latest/APIReference"
+ *      />
+ * @author Adrian Cole
+ */
+@RequestFilters(FormSigner.class)
+@VirtualHost
+public interface IAMAsyncClient {
+
+   /**
+    * @see IAMClient#getCurrentUser()
+    */
+   @POST
+   @Path("/")
+   @XMLResponseParser(UserHandler.class)
+   @FormParams(keys = "Action", values = "GetUser")
+   ListenableFuture<User> getCurrentUser();
+   
+   /**
+    * Provides asynchronous access to User features.
+    */
+   @Delegate
+   UserAsyncClient getUserClient();
+
+}
diff --git a/labs/iam/src/main/java/org/jclouds/iam/IAMClient.java b/labs/iam/src/main/java/org/jclouds/iam/IAMClient.java
new file mode 100644
index 0000000..1a8ee42
--- /dev/null
+++ b/labs/iam/src/main/java/org/jclouds/iam/IAMClient.java
@@ -0,0 +1,49 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.iam;
+
+import java.util.concurrent.TimeUnit;
+
+import org.jclouds.concurrent.Timeout;
+import org.jclouds.iam.domain.User;
+import org.jclouds.iam.features.UserClient;
+import org.jclouds.rest.annotations.Delegate;
+
+/**
+ * Provides access to Amazon IAM via the Query API
+ * <p/>
+ * 
+ * @see <a
+ *      href="http://docs.amazonwebservices.com/IAM/latest/APIReference"
+ *      />
+ * @author Adrian Cole
+ */
+@Timeout(duration = 30, timeUnit = TimeUnit.SECONDS)
+public interface IAMClient {
+   /**
+    * Retrieves information about the current user, including the user's path, GUID, and ARN.
+    */
+   User getCurrentUser();
+
+   /**
+    * Provides synchronous access to User features.
+    */
+   @Delegate
+   UserClient getUserClient();
+}
diff --git a/labs/iam/src/main/java/org/jclouds/iam/config/IAMRestClientModule.java b/labs/iam/src/main/java/org/jclouds/iam/config/IAMRestClientModule.java
new file mode 100644
index 0000000..c570fec
--- /dev/null
+++ b/labs/iam/src/main/java/org/jclouds/iam/config/IAMRestClientModule.java
@@ -0,0 +1,48 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.iam.config;
+
+import java.util.Map;
+
+import org.jclouds.aws.config.FormSigningRestClientModule;
+import org.jclouds.iam.IAMAsyncClient;
+import org.jclouds.iam.IAMClient;
+import org.jclouds.iam.features.UserAsyncClient;
+import org.jclouds.iam.features.UserClient;
+import org.jclouds.rest.ConfiguresRestClient;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.reflect.TypeToken;
+
+/**
+ * Configures the Monitoring connection.
+ * 
+ * @author Adrian Cole
+ */
+@ConfiguresRestClient
+public class IAMRestClientModule extends FormSigningRestClientModule<IAMClient, IAMAsyncClient> {
+   public static final Map<Class<?>, Class<?>> DELEGATE_MAP = ImmutableMap.<Class<?>, Class<?>> builder()//
+         .put(UserClient.class, UserAsyncClient.class)
+         .build();
+   
+   public IAMRestClientModule() {
+      super(TypeToken.of(IAMClient.class), TypeToken.of(IAMAsyncClient.class), DELEGATE_MAP);
+   }
+
+}
diff --git a/labs/iam/src/main/java/org/jclouds/iam/domain/User.java b/labs/iam/src/main/java/org/jclouds/iam/domain/User.java
new file mode 100644
index 0000000..124bbcb
--- /dev/null
+++ b/labs/iam/src/main/java/org/jclouds/iam/domain/User.java
@@ -0,0 +1,209 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.iam.domain;
+
+import java.util.Date;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Optional;
+
+/**
+ * @see <a href="http://docs.amazonwebservices.com/IAM/latest/APIReference/API_GetUser.html" />
+ * 
+ * @author Adrian Cole
+ */
+public class User {
+   public static Builder<?> builder() {
+      return new ConcreteBuilder();
+   }
+
+   public Builder<?> toBuilder() {
+      return new ConcreteBuilder().fromUser(this);
+   }
+
+   public static abstract class Builder<T extends Builder<T>>  {
+      protected abstract T self();
+
+      private Optional<String> path = Optional.absent();
+      private String id;
+      private Optional<String> name = Optional.absent();
+      private String arn;
+      private Date createDate;
+
+      /**
+       * @see User#getPath()
+       */
+      public T path(String path) {
+         this.path = Optional.fromNullable(path);
+         return self();
+      }
+
+      /**
+       * @see User#getId()
+       */
+      public T id(String id) {
+         this.id = id;
+         return self();
+      }
+
+      /**
+       * @see User#getName()
+       */
+      public T name(String name) {
+         this.name = Optional.fromNullable(name);
+         return self();
+      }
+
+      /**
+       * @see User#getArn()
+       */
+      public T arn(String arn) {
+         this.arn = arn;
+         return self();
+      }
+
+      /**
+       * @see User#getCreateDate()
+       */
+      public T createDate(Date createDate) {
+         this.createDate = createDate;
+         return self();
+      }
+      
+      public User build() {
+         return new User(path, name, id, arn, createDate);
+      }
+
+      public T fromUser(User in) {
+         return this
+               .path(in.getPath().orNull())
+               .name(in.getName().orNull())
+               .id(in.getId())
+               .arn(in.getArn())
+               .createDate(in.getCreateDate())
+               ;
+      }
+   }
+
+   private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
+      @Override
+      protected ConcreteBuilder self() {
+         return this;
+      }
+   }
+   
+   private final Optional<String> path;
+   private final Optional<String> name;
+   private final String id;
+   private final String arn;
+   private final Date createDate;
+   
+   protected User(Optional<String> path, Optional<String> name, String id, String arn, Date createDate) {
+      this.path = path;
+      this.name = name;
+      this.id = id;
+      this.arn = arn;
+      this.createDate = createDate;
+   }
+
+   /**
+    * you can also optionally give the entity a path that you define. You might use the path to
+    * identify which division or part of the organization the entity belongs in. For example:
+    * /division_abc/subdivision_xyz/product_1234/engineering/
+    */
+   public Optional<String> getPath() {
+      return path;
+   }
+
+   /**
+    * When you create a user, a role, or a group, or when you upload a server certificate, you give
+    * it a friendly name, such as Bob, TestApp1, Developers, or ProdServerCert. Whenever you need to
+    * specify a particular entity in an API call to IAM (for example, to delete a user, or update a
+    * group with a new user), you use the friendly name.
+    */
+   public Optional<String> getName() {
+      return name;
+   }
+
+   /**
+    * We assign each user, group, and server certificate a globally unique identifier (GUID), which
+    * we return to you when you use the API or CLI to create it. We recommend you store the GUID in
+    * your own database along with the user, group, or certificate name. Internally we use the GUID
+    * to identify the user, group, or certificate, and we translate the value into the ARN or
+    * friendly name as appropriate when displaying the user, group, or certificate information to
+    * you. If you delete a user, group, or server certificate, any residual remote references to
+    * that item display the GUID as the friendly name part in the ARN. If you've stored the GUID in
+    * your own system, you can then use the displayed GUID to identify the deleted item being
+    * referred to.
+    */
+   public String getId() {
+      return id;
+   }
+
+   /**
+    * Although most resources have a friendly name (for example, a user named Bob or a group named
+    * Developers), the access policy language requires you to specify the resource or resources
+    * using the following Amazon Resource Name (ARN) format.
+    * 
+    * {@code arn:aws:<service>:<region>:<namespace>:<relative-id>}
+    */
+   public String getArn() {
+      return arn;
+   }
+
+   /**
+    * Date the user was created
+    */
+   public Date getCreateDate() {
+      return createDate;
+   }
+   
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public int hashCode() {
+      return Objects.hashCode(id, arn);
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public boolean equals(Object obj) {
+      if (this == obj)
+         return true;
+      if (obj == null)
+         return false;
+      if (getClass() != obj.getClass())
+         return false;
+      User other = (User) obj;
+      return Objects.equal(this.id, other.id) && Objects.equal(this.arn, other.arn);
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public String toString() {
+      return Objects.toStringHelper(this).add("path", path).add("name", name).add("id", id).add("arn", arn).add(
+               "createDate", createDate).toString();
+   }
+
+}
diff --git a/labs/iam/src/main/java/org/jclouds/iam/features/UserAsyncClient.java b/labs/iam/src/main/java/org/jclouds/iam/features/UserAsyncClient.java
new file mode 100644
index 0000000..a36749b
--- /dev/null
+++ b/labs/iam/src/main/java/org/jclouds/iam/features/UserAsyncClient.java
@@ -0,0 +1,88 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.iam.features;
+
+import javax.ws.rs.FormParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+
+import org.jclouds.aws.filters.FormSigner;
+import org.jclouds.collect.PaginatedSet;
+import org.jclouds.iam.domain.User;
+import org.jclouds.iam.options.ListUsersOptions;
+import org.jclouds.iam.xml.ListUsersResultHandler;
+import org.jclouds.iam.xml.UserHandler;
+import org.jclouds.rest.annotations.ExceptionParser;
+import org.jclouds.rest.annotations.FormParams;
+import org.jclouds.rest.annotations.RequestFilters;
+import org.jclouds.rest.annotations.VirtualHost;
+import org.jclouds.rest.annotations.XMLResponseParser;
+import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ * Provides access to Amazon IAM via the Query API
+ * <p/>
+ * 
+ * @see <a href="http://docs.amazonwebservices.com/IAM/latest/APIReference" />
+ * @author Adrian Cole
+ */
+@RequestFilters(FormSigner.class)
+@VirtualHost
+public interface UserAsyncClient {
+
+   /**
+    * @see UserClient#getCurrent()
+    */
+   @POST
+   @Path("/")
+   @XMLResponseParser(UserHandler.class)
+   @FormParams(keys = "Action", values = "GetUser")
+   ListenableFuture<User> getCurrent();
+   
+   /**
+    * @see UserClient#get()
+    */
+   @POST
+   @Path("/")
+   @XMLResponseParser(UserHandler.class)
+   @FormParams(keys = "Action", values = "GetUser")
+   @ExceptionParser(ReturnNullOnNotFoundOr404.class)
+   ListenableFuture<User> get(@FormParam("UserName") String name);
+   
+   /**
+    * @see UserClient#list()
+    */
+   @POST
+   @Path("/")
+   @XMLResponseParser(ListUsersResultHandler.class)
+   @FormParams(keys = "Action", values = "ListUsers")
+   ListenableFuture<PaginatedSet<User>> list();
+
+   /**
+    * @see UserClient#list(ListUsersOptions)
+    */
+   @POST
+   @Path("/")
+   @XMLResponseParser(ListUsersResultHandler.class)
+   @FormParams(keys = "Action", values = "ListUsers")
+   ListenableFuture<PaginatedSet<User>> list(ListUsersOptions options);
+
+}
diff --git a/labs/iam/src/main/java/org/jclouds/iam/features/UserClient.java b/labs/iam/src/main/java/org/jclouds/iam/features/UserClient.java
new file mode 100644
index 0000000..9e7205d
--- /dev/null
+++ b/labs/iam/src/main/java/org/jclouds/iam/features/UserClient.java
@@ -0,0 +1,75 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.iam.features;
+
+import java.util.concurrent.TimeUnit;
+
+import org.jclouds.collect.PaginatedSet;
+import org.jclouds.concurrent.Timeout;
+import org.jclouds.iam.domain.User;
+import org.jclouds.iam.options.ListUsersOptions;
+import org.jclouds.javax.annotation.Nullable;
+
+/**
+ * Provides access to Amazon IAM via the Query API
+ * <p/>
+ * 
+ * @see <a href="http://docs.amazonwebservices.com/IAM/latest/APIReference" />
+ * @author Adrian Cole
+ */
+@Timeout(duration = 30, timeUnit = TimeUnit.SECONDS)
+public interface UserClient {
+   /**
+    * Retrieves information about the current user, including the user's path, GUID, and ARN.
+    */
+   User getCurrent();
+
+   /**
+    * Retrieves information about the specified user, including the user's path, GUID, and ARN.
+    * 
+    * @param name
+    *           Name of the user to get information about.
+    * @return null if not found
+    */
+   @Nullable
+   User get(String name);
+
+   /**
+    * Lists the users that have the specified path prefix. If there are none, the action returns an
+    * empty list.
+    * 
+    * <br/>
+    * You can paginate the results using the {@link ListUsersOptions parameter}
+    * 
+    * @param options
+    *           the options describing the users query
+    * 
+    * @return the response object
+    */
+   PaginatedSet<User> list(ListUsersOptions options);
+
+   /**
+    * Lists the users that have the specified path prefix. If there are none, the action returns an
+    * empty list.
+    * 
+    * @return the response object
+    */
+   PaginatedSet<User> list();
+
+}
diff --git a/labs/iam/src/main/java/org/jclouds/iam/options/ListUsersOptions.java b/labs/iam/src/main/java/org/jclouds/iam/options/ListUsersOptions.java
new file mode 100644
index 0000000..0fc2ad4
--- /dev/null
+++ b/labs/iam/src/main/java/org/jclouds/iam/options/ListUsersOptions.java
@@ -0,0 +1,169 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.iam.options;
+
+import org.jclouds.http.options.BaseHttpRequestOptions;
+import org.jclouds.javax.annotation.Nullable;
+
+import com.google.common.base.Objects;
+import com.google.common.collect.Multimap;
+
+/**
+ * Options used to list available users.
+ * 
+ * @see <a href="http://docs.amazonwebservices.com/IAM/latest/APIReference/API_ListUsers.html" />
+ * 
+ * @author Adrian Cole
+ */
+public class ListUsersOptions extends BaseHttpRequestOptions implements Cloneable {
+
+   private Integer maxItems;
+   private String pathPrefix;
+   private String marker;
+
+   /**
+    * @see ListUsersOptions#getMarker()
+    */
+   public ListUsersOptions marker(String marker) {
+      this.marker = marker;
+      return this;
+   }
+
+   /**
+    * @see ListUsersOptions#getMaxItems()
+    */
+   public ListUsersOptions maxItems(Integer maxItems) {
+      this.maxItems = maxItems;
+      return this;
+   }
+
+   /**
+    * @see ListUsersOptions#getPathPrefix()
+    */
+   public ListUsersOptions pathPrefix(String pathPrefix) {
+      this.pathPrefix = pathPrefix;
+      return this;
+   }
+
+   /**
+    * Use this parameter only when paginating results to indicate the maximum number of user names
+    * you want in the response. If there are additional user names beyond the maximum you specify,
+    * the IsTruncated response element is true.
+    */
+   @Nullable
+   public Integer getMaxItems() {
+      return maxItems;
+   }
+
+   /**
+    * The path prefix for filtering the results. For example: /division_abc/subdivision_xyz/, which
+    * would get all user names whose path starts with /division_abc/subdivision_xyz/.
+    * <p/>
+    * This parameter is optional. If it is not included, it defaults to a slash (/), listing all
+    * user names.
+    */
+   @Nullable
+   public String getPathPrefix() {
+      return pathPrefix;
+   }
+
+   /**
+    * Use this parameter only when paginating results, and only in a subsequent request after you've
+    * received a response where the results are truncated. Set it to the value of the Marker element
+    * in the response you just received.
+    */
+   @Nullable
+   public String getMarker() {
+      return marker;
+   }
+
+   public static class Builder {
+
+      /**
+       * @see ListUsersOptions#getMarker()
+       */
+      public static ListUsersOptions marker(String marker) {
+         return new ListUsersOptions().marker(marker);
+      }
+
+      /**
+       * @see ListUsersOptions#getMaxItems()
+       */
+      public static ListUsersOptions maxItems(Integer maxItems) {
+         return new ListUsersOptions().maxItems(maxItems);
+      }
+
+      /**
+       * @see ListUsersOptions#getPathPrefix()
+       */
+      public static ListUsersOptions pathPrefix(String pathPrefix) {
+         return new ListUsersOptions().pathPrefix(pathPrefix);
+      }
+   }
+
+   @Override
+   public Multimap<String, String> buildFormParameters() {
+      Multimap<String, String> params = super.buildFormParameters();
+      if (marker != null)
+         params.put("Marker", marker);
+      if (maxItems != null)
+         params.put("MaxItems", maxItems.toString());
+      if (pathPrefix != null)
+         params.put("PathPrefix", pathPrefix);
+      return params;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public int hashCode() {
+      return Objects.hashCode(marker, maxItems, pathPrefix);
+   }
+
+   @Override
+   public ListUsersOptions clone() {
+      return new ListUsersOptions().marker(marker).maxItems(maxItems).pathPrefix(pathPrefix);
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public boolean equals(Object obj) {
+      if (this == obj)
+         return true;
+      if (obj == null)
+         return false;
+      if (getClass() != obj.getClass())
+         return false;
+      ListUsersOptions other = ListUsersOptions.class.cast(obj);
+      return Objects.equal(this.marker, other.marker) && Objects.equal(this.maxItems, other.maxItems)
+               && Objects.equal(this.pathPrefix, other.pathPrefix);
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public String toString() {
+      return Objects.toStringHelper(this).omitNullValues().add("marker", marker).add("maxItems", maxItems).add(
+               "pathPrefix", pathPrefix).toString();
+   }
+}
diff --git a/labs/iam/src/main/java/org/jclouds/iam/xml/ListUsersResultHandler.java b/labs/iam/src/main/java/org/jclouds/iam/xml/ListUsersResultHandler.java
new file mode 100644
index 0000000..11a9dfa
--- /dev/null
+++ b/labs/iam/src/main/java/org/jclouds/iam/xml/ListUsersResultHandler.java
@@ -0,0 +1,105 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.iam.xml;
+
+import java.util.Set;
+
+import org.jclouds.collect.PaginatedSet;
+import org.jclouds.http.functions.ParseSax;
+import org.jclouds.iam.domain.User;
+import org.jclouds.util.SaxUtils;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+
+import com.google.common.collect.Sets;
+import com.google.inject.Inject;
+
+/**
+ * @see <a href="http://docs.amazonwebservices.com/IAM/latest/APIReference/API_ListUsers.html" />
+ *
+ * @author Adrian Cole
+ */
+public class ListUsersResultHandler extends ParseSax.HandlerForGeneratedRequestWithResult<PaginatedSet<User>> {
+
+   private final UserHandler userHandler;
+
+   private StringBuilder currentText = new StringBuilder();
+   private Set<User> users = Sets.newLinkedHashSet();
+   private boolean inUsers;
+   private String marker;
+
+   @Inject
+   public ListUsersResultHandler(UserHandler userHandler) {
+      this.userHandler = userHandler;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public PaginatedSet<User> getResult() {
+      return PaginatedSet.copyOfWithMarker(users, marker);
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public void startElement(String url, String name, String qName, Attributes attributes) throws SAXException {
+      if (SaxUtils.equalsOrSuffix(qName, "Users")) {
+         inUsers = true;
+      }
+      if (inUsers) {
+         userHandler.startElement(url, name, qName, attributes);
+      }
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public void endElement(String uri, String name, String qName) throws SAXException {
+      if (inUsers) {
+         if (qName.equals("Users")) {
+            inUsers = false;
+         } else if (qName.equals("member")) {
+            users.add(userHandler.getResult());
+         } else {
+            userHandler.endElement(uri, name, qName);
+         }
+      } else if (qName.equals("Marker")) {
+         marker = SaxUtils.currentOrNull(currentText);
+      }
+
+      currentText = new StringBuilder();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public void characters(char ch[], int start, int length) {
+      if (inUsers) {
+         userHandler.characters(ch, start, length);
+      } else {
+         currentText.append(ch, start, length);
+      }
+   }
+
+}
diff --git a/labs/iam/src/main/java/org/jclouds/iam/xml/UserHandler.java b/labs/iam/src/main/java/org/jclouds/iam/xml/UserHandler.java
new file mode 100644
index 0000000..35db67d
--- /dev/null
+++ b/labs/iam/src/main/java/org/jclouds/iam/xml/UserHandler.java
@@ -0,0 +1,84 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.iam.xml;
+
+import javax.inject.Inject;
+
+import org.jclouds.date.DateService;
+import org.jclouds.http.functions.ParseSax;
+import org.jclouds.iam.domain.User;
+import org.jclouds.util.SaxUtils;
+import org.xml.sax.SAXException;
+
+/**
+ * @see <a href="http://docs.amazonwebservices.com/IAM/latest/APIReference/API_GetUser.html" />
+ * 
+ * @author Adrian Cole
+ */
+public class UserHandler extends ParseSax.HandlerForGeneratedRequestWithResult<User> {
+   private final DateService dateService;
+
+   @Inject
+   protected UserHandler(DateService dateService){
+      this.dateService = dateService;
+   }
+   
+   private StringBuilder currentText = new StringBuilder();
+   private User.Builder<?> builder = User.builder();
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public User getResult() {
+      try {
+         return builder.build();
+      } finally {
+         builder = User.builder();
+      }
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public void endElement(String uri, String name, String qName) throws SAXException {
+      if (qName.equals("Path")) {
+         builder.path(SaxUtils.currentOrNull(currentText));
+      } else if (qName.equals("UserName")) {
+         builder.name(SaxUtils.currentOrNull(currentText));
+      } else if (qName.equals("UserId")) {
+         builder.id(SaxUtils.currentOrNull(currentText));
+      } else if (qName.equals("Arn")) {
+         builder.arn(SaxUtils.currentOrNull(currentText));
+      } else if (qName.equals("CreateDate")) {
+         builder.createDate(dateService.iso8601SecondsDateParse(SaxUtils.currentOrNull(currentText)));
+      }
+      currentText = new StringBuilder();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public void characters(char ch[], int start, int length) {
+      currentText.append(ch, start, length);
+   }
+
+}
diff --git a/labs/iam/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata b/labs/iam/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata
new file mode 100644
index 0000000..5dd9302
--- /dev/null
+++ b/labs/iam/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata
@@ -0,0 +1 @@
+org.jclouds.iam.IAMApiMetadata
\ No newline at end of file
diff --git a/labs/iam/src/test/java/org/jclouds/iam/IAMApiMetadataTest.java b/labs/iam/src/test/java/org/jclouds/iam/IAMApiMetadataTest.java
new file mode 100644
index 0000000..a637870
--- /dev/null
+++ b/labs/iam/src/test/java/org/jclouds/iam/IAMApiMetadataTest.java
@@ -0,0 +1,39 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.iam;
+
+import org.jclouds.View;
+import org.jclouds.rest.internal.BaseRestApiMetadataTest;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.reflect.TypeToken;
+
+/**
+ * 
+ * @author Adrian Cole
+ */
+@Test(groups = "unit", testName = "IAMApiMetadataTest")
+public class IAMApiMetadataTest extends BaseRestApiMetadataTest {
+
+   // no tenant abstraction, yet
+   public IAMApiMetadataTest() {
+      super(new IAMApiMetadata(), ImmutableSet.<TypeToken<? extends View>> of());
+   }
+}
diff --git a/labs/iam/src/test/java/org/jclouds/iam/IAMClientLiveTest.java b/labs/iam/src/test/java/org/jclouds/iam/IAMClientLiveTest.java
new file mode 100644
index 0000000..46678dd
--- /dev/null
+++ b/labs/iam/src/test/java/org/jclouds/iam/IAMClientLiveTest.java
@@ -0,0 +1,58 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.iam;
+
+import org.jclouds.apis.BaseContextLiveTest;
+import org.jclouds.rest.RestContext;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import com.google.common.reflect.TypeToken;
+
+/**
+ * Tests behavior of {@code IAM}.
+ * 
+ * @author Adrian Cole
+ */
+public class IAMClientLiveTest extends BaseContextLiveTest<RestContext<? extends IAMClient, ? extends IAMAsyncClient>> {
+
+   public IAMClientLiveTest() {
+      provider = "iam";
+   }
+
+   private IAMClient client;
+
+   @Override
+   @BeforeClass(groups = { "integration", "live" })
+   public void setupContext() {
+      super.setupContext();
+      client = context.getApi();
+   }
+
+   @Override
+   protected TypeToken<RestContext<? extends IAMClient, ? extends IAMAsyncClient>> contextType() {
+      return IAMApiMetadata.CONTEXT_TOKEN;
+   }
+
+   @Test
+   protected void testWired() {
+      client.getCurrentUser();
+  }
+
+}
diff --git a/labs/iam/src/test/java/org/jclouds/iam/IAMTest.java b/labs/iam/src/test/java/org/jclouds/iam/IAMTest.java
new file mode 100644
index 0000000..a376d8a
--- /dev/null
+++ b/labs/iam/src/test/java/org/jclouds/iam/IAMTest.java
@@ -0,0 +1,62 @@
+package org.jclouds.iam;
+
+import static org.easymock.EasyMock.anyObject;
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+
+import org.easymock.EasyMock;
+import org.jclouds.collect.PaginatedSet;
+import org.jclouds.iam.domain.User;
+import org.jclouds.iam.features.UserClient;
+import org.jclouds.iam.options.ListUsersOptions;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+
+/**
+ * Tests behavior of {@code IAM}.
+ *
+ * @author Adrian Cole
+ */
+@Test(testName = "IAMTest")
+public class IAMTest {
+
+
+   @Test
+   public void testSinglePageResult() throws Exception {
+      UserClient userClient = createMock(UserClient.class);
+      ListUsersOptions options = new ListUsersOptions();
+      PaginatedSet<User> response = PaginatedSet.copyOf(ImmutableSet.of(createMock(User.class)));
+      
+      expect(userClient.list(options))
+            .andReturn(response)
+            .once();
+
+      EasyMock.replay(userClient);
+
+      Assert.assertEquals(1, Iterables.size(IAM.list(userClient, options)));
+   }
+
+
+   @Test
+   public void testMultiPageResult() throws Exception {
+      UserClient userClient = createMock(UserClient.class);
+      ListUsersOptions options = new ListUsersOptions();
+      PaginatedSet<User> response1 = PaginatedSet.copyOfWithMarker(ImmutableSet.of(createMock(User.class)), "NEXTTOKEN");
+      PaginatedSet<User> response2 = PaginatedSet.copyOf(ImmutableSet.of(createMock(User.class)));
+
+      expect(userClient.list(anyObject(ListUsersOptions.class)))
+            .andReturn(response1)
+            .once();
+      expect(userClient.list(anyObject(ListUsersOptions.class)))
+            .andReturn(response2)
+            .once();
+
+      EasyMock.replay(userClient);
+
+      Assert.assertEquals(2, Iterables.size(IAM.list(userClient, options)));
+   }
+
+}
diff --git a/labs/iam/src/test/java/org/jclouds/iam/features/UserClientExpectTest.java b/labs/iam/src/test/java/org/jclouds/iam/features/UserClientExpectTest.java
new file mode 100644
index 0000000..2ed548e
--- /dev/null
+++ b/labs/iam/src/test/java/org/jclouds/iam/features/UserClientExpectTest.java
@@ -0,0 +1,188 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unles 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 expres or implied.  See the License for the
+ * specific language governing permisions and limitations
+ * under the License.
+ */
+package org.jclouds.iam.features;
+
+import static org.jclouds.iam.options.ListUsersOptions.Builder.pathPrefix;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNull;
+
+import java.net.URI;
+import java.util.TimeZone;
+
+import org.jclouds.http.HttpRequest;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.iam.IAMClient;
+import org.jclouds.iam.internal.BaseIAMClientExpectTest;
+import org.jclouds.iam.parse.GetUserResponseTest;
+import org.jclouds.iam.parse.ListUsersResponseTest;
+import org.jclouds.rest.ResourceNotFoundException;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableMultimap;
+
+/**
+ * @author Adrian Cole
+ */
+@Test(groups = "unit", testName = "UserClientExpectTest")
+public class UserClientExpectTest extends BaseIAMClientExpectTest {
+
+   public UserClientExpectTest() {
+      TimeZone.setDefault(TimeZone.getTimeZone("America/Los_Angeles"));
+   }
+
+   public void testGetCurrentWhenResponseIs2xx() throws Exception {
+      HttpRequest get = HttpRequest.builder()
+                                    .method("POST")
+                                    .endpoint(URI.create("https://iam.amazonaws.com/"))
+                                    .headers(ImmutableMultimap.<String, String> builder()
+                                             .put("Host", "iam.amazonaws.com")
+                                             .build())
+                                    .payload(
+                                       payloadFromStringWithContentType(
+                                             "Action=GetUser" +
+                                                   "&Signature=2UamWqKKgoSbaZpvixX0LKqGW%2FIIP9L319DLEUtYu3A%3D" +
+                                                   "&SignatureMethod=HmacSHA256" +
+                                                   "&SignatureVersion=2" +
+                                                   "&Timestamp=2009-11-08T15%3A54%3A08.897Z" +
+                                                   "&Version=2010-05-08" +
+                                                   "&AWSAccessKeyId=identity",
+                                             "application/x-www-form-urlencoded"))
+                                    .build();
+      HttpResponse getResponse = HttpResponse.builder().statusCode(200)
+            .payload(payloadFromResourceWithContentType("/get_user.xml", "text/xml")).build();
+
+      IAMClient clientWhenExist = requestSendsResponse(
+            get, getResponse);
+
+      assertEquals(clientWhenExist.getUserClient().getCurrent().toString(), new GetUserResponseTest().expected().toString());
+   }
+   
+   HttpRequest get = HttpRequest.builder()
+                                       .method("POST")
+                                       .endpoint(URI.create("https://iam.amazonaws.com/"))
+                                       .headers(ImmutableMultimap.<String, String> builder()
+                                                .put("Host", "iam.amazonaws.com")
+                                                .build())
+                                       .payload(
+                                          payloadFromStringWithContentType(
+                                                "Action=GetUser" +
+                                                      "&Signature=cnY%2FAaG656cruOmb3y7YHtjnPB1qg3aavff6PPxIMs0%3D" +
+                                                      "&SignatureMethod=HmacSHA256" +
+                                                      "&SignatureVersion=2" +
+                                                      "&Timestamp=2009-11-08T15%3A54%3A08.897Z" +
+                                                      "&UserName=name" +
+                                                      "&Version=2010-05-08" +
+                                                      "&AWSAccessKeyId=identity",
+                                                "application/x-www-form-urlencoded"))
+                                       .build();
+   
+   
+   public void testGetWhenResponseIs2xx() throws Exception {
+
+      HttpResponse getResponse = HttpResponse.builder().statusCode(200)
+            .payload(payloadFromResourceWithContentType("/get_user.xml", "text/xml")).build();
+
+      IAMClient clientWhenExist = requestSendsResponse(
+            get, getResponse);
+
+      assertEquals(clientWhenExist.getUserClient().get("name").toString(), new GetUserResponseTest().expected().toString());
+   }
+
+   public void testGetWhenResponseIs404() throws Exception {
+
+      HttpResponse getResponse = HttpResponse.builder().statusCode(404).build();
+
+      IAMClient clientWhenDontExist = requestSendsResponse(
+            get, getResponse);
+
+      assertNull(clientWhenDontExist.getUserClient().get("name"));
+   }
+
+   HttpRequest list = HttpRequest.builder()
+                                       .method("POST")
+                                       .endpoint(URI.create("https://iam.amazonaws.com/"))
+                                       .headers(ImmutableMultimap.<String, String> builder()
+                                                .put("Host", "iam.amazonaws.com")
+                                                .build())
+                                       .payload(
+                                          payloadFromStringWithContentType(
+                                                "Action=ListUsers" +
+                                                      "&Signature=ed4OrONGuVlGpHSY8u5X2m9LVwx6oiihu7HbvA0iZkY%3D" +
+                                                      "&SignatureMethod=HmacSHA256" +
+                                                      "&SignatureVersion=2" +
+                                                      "&Timestamp=2009-11-08T15%3A54%3A08.897Z" +
+                                                      "&Version=2010-05-08" +
+                                                      "&AWSAccessKeyId=identity",
+                                                "application/x-www-form-urlencoded"))
+                                       .build();
+   
+   public void testListWhenResponseIs2xx() throws Exception {
+
+      HttpResponse listResponse = HttpResponse.builder().statusCode(200)
+            .payload(payloadFromResourceWithContentType("/list_users.xml", "text/xml")).build();
+
+      IAMClient clientWhenExist = requestSendsResponse(
+            list, listResponse);
+
+      assertEquals(clientWhenExist.getUserClient().list().toString(), new ListUsersResponseTest().expected().toString());
+   }
+
+   // TODO: this should really be an empty set
+   @Test(expectedExceptions = ResourceNotFoundException.class)
+   public void testListWhenResponseIs404() throws Exception {
+
+      HttpResponse listResponse = HttpResponse.builder().statusCode(404).build();
+
+      IAMClient clientWhenDontExist = requestSendsResponse(
+            list, listResponse);
+
+      clientWhenDontExist.getUserClient().list();
+   }
+   
+   public void testListWithOptionsWhenResponseIs2xx() throws Exception {
+      HttpRequest listWithOptions =
+            HttpRequest.builder()
+                       .method("POST")
+                       .endpoint(URI.create("https://iam.amazonaws.com/"))
+                       .headers(ImmutableMultimap.<String, String>builder()
+                                                 .put("Host", "iam.amazonaws.com")
+                                                 .build())
+                       .payload(payloadFromStringWithContentType(
+                                                  "Action=ListUsers" +
+                                                  "&Marker=MARKER" +
+                                                  "&PathPrefix=%2Ffoo" +
+                                                  "&Signature=1%2BeCgNIAjHr%2BraNdDd3rsVC5Qok3AuTrJOa5mZwmE7g%3D" +
+                                                  "&SignatureMethod=HmacSHA256" +
+                                                  "&SignatureVersion=2" +
+                                                  "&Timestamp=2009-11-08T15%3A54%3A08.897Z" +
+                                                  "&Version=2010-05-08" +
+                                                  "&AWSAccessKeyId=identity",
+                                            "application/x-www-form-urlencoded"))
+                       .build();
+      
+      HttpResponse listWithOptionsResponse = HttpResponse.builder().statusCode(200)
+               .payload(payloadFromResourceWithContentType("/list_users.xml", "text/xml")).build();
+
+      IAMClient clientWhenWithOptionsExist = requestSendsResponse(listWithOptions,
+               listWithOptionsResponse);
+
+      assertEquals(clientWhenWithOptionsExist.getUserClient().list(pathPrefix("/foo").marker("MARKER")).toString(),
+               new ListUsersResponseTest().expected().toString());
+   }
+}
diff --git a/labs/iam/src/test/java/org/jclouds/iam/features/UserClientLiveTest.java b/labs/iam/src/test/java/org/jclouds/iam/features/UserClientLiveTest.java
new file mode 100644
index 0000000..d635e0a
--- /dev/null
+++ b/labs/iam/src/test/java/org/jclouds/iam/features/UserClientLiveTest.java
@@ -0,0 +1,73 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.iam.features;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import org.jclouds.collect.PaginatedSet;
+import org.jclouds.iam.domain.User;
+import org.jclouds.iam.internal.BaseIAMClientLiveTest;
+import org.jclouds.iam.options.ListUsersOptions;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+/**
+ * @author Adrian Cole
+ */
+@Test(groups = "live", testName = "UserClientLiveTest")
+public class UserClientLiveTest extends BaseIAMClientLiveTest {
+
+   @Test
+   protected void testGetCurrentUser() {
+      User user = client().getCurrent();
+      checkUser(user);
+   }
+
+   private void checkUser(User user) {
+      checkNotNull(user.getArn(), "Arn cannot be null for a User.");
+      checkNotNull(user.getId(), "Id cannot be null for a User.");
+      checkNotNull(user.getName(), "While Name can be null for a User, its Optional wrapper cannot.");
+      checkNotNull(user.getPath(), "While Path can be null for a User, its Optional wrapper cannot.");
+      checkNotNull(user.getCreateDate(), "CreateDate cannot be null for a User.");
+   }
+
+   @Test
+   protected void testListUsers() {
+      PaginatedSet<User> response = client().list();
+      
+      for (User user : response) {
+         checkUser(user);
+      }
+      
+      if (response.size() > 0) {
+         User user = response.iterator().next();
+         Assert.assertEquals(client().get(user.getName().get()), user);
+      }
+
+      // Test with a Marker, even if it's null
+      response = client().list(ListUsersOptions.Builder.marker(response.getNextMarker()));
+      for (User user : response) {
+         checkUser(user);
+      }
+   }
+
+   protected UserClient client() {
+      return context.getApi().getUserClient();
+   }
+}
diff --git a/labs/iam/src/test/java/org/jclouds/iam/internal/BaseIAMAsyncClientExpectTest.java b/labs/iam/src/test/java/org/jclouds/iam/internal/BaseIAMAsyncClientExpectTest.java
new file mode 100644
index 0000000..daa3407
--- /dev/null
+++ b/labs/iam/src/test/java/org/jclouds/iam/internal/BaseIAMAsyncClientExpectTest.java
@@ -0,0 +1,38 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.iam.internal;
+
+import java.util.Properties;
+
+import org.jclouds.http.HttpRequest;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.iam.IAMAsyncClient;
+
+import com.google.common.base.Function;
+import com.google.inject.Module;
+
+/**
+ * 
+ * @author Adrian Cole
+ */
+public class BaseIAMAsyncClientExpectTest extends BaseIAMExpectTest<IAMAsyncClient> {
+   public IAMAsyncClient createClient(Function<HttpRequest, HttpResponse> fn, Module module, Properties props) {
+      return createInjector(fn, module, props).getInstance(IAMAsyncClient.class);
+   }
+}
diff --git a/labs/iam/src/test/java/org/jclouds/iam/internal/BaseIAMClientExpectTest.java b/labs/iam/src/test/java/org/jclouds/iam/internal/BaseIAMClientExpectTest.java
new file mode 100644
index 0000000..6e8dd28
--- /dev/null
+++ b/labs/iam/src/test/java/org/jclouds/iam/internal/BaseIAMClientExpectTest.java
@@ -0,0 +1,29 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.iam.internal;
+
+import org.jclouds.iam.IAMClient;
+
+/**
+ * 
+ * @author Adrian Cole
+ */
+public class BaseIAMClientExpectTest extends BaseIAMExpectTest<IAMClient> {
+
+}
diff --git a/labs/iam/src/test/java/org/jclouds/iam/internal/BaseIAMClientLiveTest.java b/labs/iam/src/test/java/org/jclouds/iam/internal/BaseIAMClientLiveTest.java
new file mode 100644
index 0000000..617f7f2
--- /dev/null
+++ b/labs/iam/src/test/java/org/jclouds/iam/internal/BaseIAMClientLiveTest.java
@@ -0,0 +1,47 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.iam.internal;
+
+import org.jclouds.apis.BaseContextLiveTest;
+import org.jclouds.iam.IAMApiMetadata;
+import org.jclouds.iam.IAMAsyncClient;
+import org.jclouds.iam.IAMClient;
+import org.jclouds.rest.RestContext;
+import org.testng.annotations.Test;
+
+import com.google.common.reflect.TypeToken;
+
+/**
+ * 
+ * @author Adrian Cole
+ */
+@Test(groups = "live")
+public class BaseIAMClientLiveTest extends
+         BaseContextLiveTest<RestContext<? extends IAMClient, ? extends IAMAsyncClient>> {
+
+   public BaseIAMClientLiveTest() {
+      provider = "iam";
+   }
+
+   @Override
+   protected TypeToken<RestContext<? extends IAMClient, ? extends IAMAsyncClient>> contextType() {
+      return IAMApiMetadata.CONTEXT_TOKEN;
+   }
+
+}
diff --git a/labs/iam/src/test/java/org/jclouds/iam/internal/BaseIAMExpectTest.java b/labs/iam/src/test/java/org/jclouds/iam/internal/BaseIAMExpectTest.java
new file mode 100644
index 0000000..da0b890
--- /dev/null
+++ b/labs/iam/src/test/java/org/jclouds/iam/internal/BaseIAMExpectTest.java
@@ -0,0 +1,51 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.iam.internal;
+
+import org.jclouds.date.DateService;
+import org.jclouds.iam.config.IAMRestClientModule;
+import org.jclouds.rest.ConfiguresRestClient;
+import org.jclouds.rest.internal.BaseRestClientExpectTest;
+
+import com.google.inject.Module;
+
+/**
+ * 
+ * @author Adrian Cole
+ */
+public class BaseIAMExpectTest<T> extends BaseRestClientExpectTest<T> {
+
+   public BaseIAMExpectTest() {
+      provider = "iam";
+   }
+   
+   @ConfiguresRestClient
+   private static final class TestIAMRestClientModule extends IAMRestClientModule {
+
+      @Override
+      protected String provideTimeStamp(final DateService dateService) {
+         return "2009-11-08T15:54:08.897Z";
+      }
+   }
+
+   @Override
+   protected Module createModule() {
+      return new TestIAMRestClientModule();
+   }
+}
diff --git a/labs/iam/src/test/java/org/jclouds/iam/options/ListUsersOptionsTest.java b/labs/iam/src/test/java/org/jclouds/iam/options/ListUsersOptionsTest.java
new file mode 100644
index 0000000..57e77c2
--- /dev/null
+++ b/labs/iam/src/test/java/org/jclouds/iam/options/ListUsersOptionsTest.java
@@ -0,0 +1,68 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.iam.options;
+
+import static org.jclouds.iam.options.ListUsersOptions.Builder.marker;
+import static org.jclouds.iam.options.ListUsersOptions.Builder.maxItems;
+import static org.jclouds.iam.options.ListUsersOptions.Builder.pathPrefix;
+import static org.testng.Assert.assertEquals;
+
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * Tests behavior of {@code ListUsersOptions}
+ * 
+ * @author Adrian Cole
+ */
+@Test(groups = "maxItems", testName = "ListUsersOptionsTest")
+public class ListUsersOptionsTest {
+
+   public void testMarker() {
+      ListUsersOptions options = new ListUsersOptions().marker("FFFFF");
+      assertEquals(ImmutableSet.of("FFFFF"), options.buildFormParameters().get("Marker"));
+   }
+
+   public void testMarkerStatic() {
+      ListUsersOptions options = marker("FFFFF");
+      assertEquals(ImmutableSet.of("FFFFF"), options.buildFormParameters().get("Marker"));
+   }
+
+   public void testMaxItems() {
+      ListUsersOptions options = new ListUsersOptions().maxItems(1000);
+      assertEquals(ImmutableSet.of("1000"), options.buildFormParameters().get("MaxItems"));
+   }
+
+   public void testMaxItemsStatic() {
+      ListUsersOptions options = maxItems(1000);
+      assertEquals(ImmutableSet.of("1000"), options.buildFormParameters().get("MaxItems"));
+   }
+
+   public void testPathPrefix() {
+      ListUsersOptions options = new ListUsersOptions().pathPrefix("/division_abc/subdivision_xyz/");
+      assertEquals(ImmutableSet.of("/division_abc/subdivision_xyz/"), options.buildFormParameters().get("PathPrefix"));
+   }
+
+   public void testPathPrefixStatic() {
+      ListUsersOptions options = pathPrefix("/division_abc/subdivision_xyz/");
+      assertEquals(ImmutableSet.of("/division_abc/subdivision_xyz/"), options.buildFormParameters().get("PathPrefix"));
+   }
+
+}
diff --git a/labs/iam/src/test/java/org/jclouds/iam/parse/GetUserResponseTest.java b/labs/iam/src/test/java/org/jclouds/iam/parse/GetUserResponseTest.java
new file mode 100644
index 0000000..d274cb9
--- /dev/null
+++ b/labs/iam/src/test/java/org/jclouds/iam/parse/GetUserResponseTest.java
@@ -0,0 +1,60 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.iam.parse;
+
+import static org.testng.Assert.assertEquals;
+
+import java.io.InputStream;
+
+import org.jclouds.date.internal.SimpleDateFormatDateService;
+import org.jclouds.http.functions.BaseHandlerTest;
+import org.jclouds.iam.domain.User;
+import org.jclouds.iam.xml.UserHandler;
+import org.testng.annotations.Test;
+
+/**
+ * @author Adrian Cole
+ */
+// NOTE:without testName, this will not call @Before* and fail w/NPE during surefire
+@Test(groups = "unit", testName = "GetUserResponseTest")
+public class GetUserResponseTest extends BaseHandlerTest {
+
+   public void test() {
+      InputStream is = getClass().getResourceAsStream("/get_user.xml");
+
+      User expected = expected();
+
+      UserHandler handler = injector.getInstance(UserHandler.class);
+      User result = factory.create(handler).parse(is);
+
+      assertEquals(result, expected);
+      assertEquals(result.getPath(), expected.getPath());
+      assertEquals(result.getName(), expected.getName());
+   }
+
+   public User expected() {
+      return User.builder()
+                 .path("/division_abc/subdivision_xyz/")
+                 .name("Bob")
+                 .id("AIDACKCEVSQ6C2EXAMPLE")
+                 .arn("arn:aws:iam::123456789012:user/division_abc/subdivision_xyz/Bob")
+                 .createDate(new SimpleDateFormatDateService().iso8601SecondsDateParse("2009-03-06T21:47:48Z")).build();
+   }
+
+}
diff --git a/labs/iam/src/test/java/org/jclouds/iam/parse/ListUsersResponseTest.java b/labs/iam/src/test/java/org/jclouds/iam/parse/ListUsersResponseTest.java
new file mode 100644
index 0000000..27c9f3a
--- /dev/null
+++ b/labs/iam/src/test/java/org/jclouds/iam/parse/ListUsersResponseTest.java
@@ -0,0 +1,69 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.iam.parse;
+
+import static org.testng.Assert.assertEquals;
+
+import java.io.InputStream;
+
+import org.jclouds.collect.PaginatedSet;
+import org.jclouds.date.internal.SimpleDateFormatDateService;
+import org.jclouds.http.functions.BaseHandlerTest;
+import org.jclouds.iam.domain.User;
+import org.jclouds.iam.xml.ListUsersResultHandler;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * @author Adrian Cole
+ */
+// NOTE:without testName, this will not call @Before* and fail w/NPE during surefire
+@Test(groups = "unit", testName = "ListUsersResponseTest")
+public class ListUsersResponseTest extends BaseHandlerTest {
+
+   public void test() {
+      InputStream is = getClass().getResourceAsStream("/list_users.xml");
+
+      PaginatedSet<User> expected = expected();
+
+      ListUsersResultHandler handler = injector.getInstance(ListUsersResultHandler.class);
+      PaginatedSet<User> result = factory.create(handler).parse(is);
+
+      assertEquals(result.toString(), expected.toString());
+
+   }
+
+   public PaginatedSet<User> expected() {
+      return PaginatedSet.copyOf(ImmutableSet.of(
+               User.builder()
+                   .path("/division_abc/subdivision_xyz/engineering/")
+                   .name("Andrew")
+                   .id("AID2MAB8DPLSRHEXAMPLE")
+                   .arn("arn:aws:iam::123456789012:user/division_abc/subdivision_xyz/engineering/Andrew")
+                   .createDate(new SimpleDateFormatDateService().iso8601SecondsDateParse("2009-03-06T21:47:48Z")).build(),
+               User.builder()
+                   .path("/division_abc/subdivision_xyz/engineering/")
+                   .name("Jackie")
+                   .id("AIDIODR4TAW7CSEXAMPLE")
+                   .arn("arn:aws:iam::123456789012:user/division_abc/subdivision_xyz/engineering/Jackie")
+                   .createDate(new SimpleDateFormatDateService().iso8601SecondsDateParse("2009-03-06T21:47:48Z")).build()));
+   }
+
+}
diff --git a/labs/iam/src/test/resources/get_user.xml b/labs/iam/src/test/resources/get_user.xml
new file mode 100644
index 0000000..f4d1704
--- /dev/null
+++ b/labs/iam/src/test/resources/get_user.xml
@@ -0,0 +1,16 @@
+<GetUserResponse>
+   <GetUserResult>
+      <User>
+         <Path>/division_abc/subdivision_xyz/</Path>
+         <UserName>Bob</UserName>
+         <UserId>AIDACKCEVSQ6C2EXAMPLE</UserId>
+         <Arn>
+         arn:aws:iam::123456789012:user/division_abc/subdivision_xyz/Bob
+         </Arn>
+         <CreateDate>2009-03-06T21:47:48Z</CreateDate>
+      </User>
+   </GetUserResult>
+   <ResponseMetadata>
+      <RequestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</RequestId>
+   </ResponseMetadata>
+</GetUserResponse>
\ No newline at end of file
diff --git a/labs/iam/src/test/resources/list_users.xml b/labs/iam/src/test/resources/list_users.xml
new file mode 100644
index 0000000..a5e36af
--- /dev/null
+++ b/labs/iam/src/test/resources/list_users.xml
@@ -0,0 +1,24 @@
+<ListUsersResponse>
+   <ListUsersResult>
+      <Users>
+         <member>
+            <Path>/division_abc/subdivision_xyz/engineering/</Path>
+            <UserName>Andrew</UserName>
+            <UserId>AID2MAB8DPLSRHEXAMPLE</UserId>
+            <Arn>arn:aws:iam::123456789012:user/division_abc/subdivision_xyz/engineering/Andrew</Arn>
+            <CreateDate>2009-03-06T21:47:48Z</CreateDate>
+         </member>
+         <member>
+            <Path>/division_abc/subdivision_xyz/engineering/</Path>
+            <UserName>Jackie</UserName>
+            <UserId>AIDIODR4TAW7CSEXAMPLE</UserId>
+            <Arn>arn:aws:iam::123456789012:user/division_abc/subdivision_xyz/engineering/Jackie</Arn>
+            <CreateDate>2009-03-06T21:47:48Z</CreateDate>
+         </member>
+      </Users>
+      <IsTruncated>false</IsTruncated>
+   </ListUsersResult>
+   <ResponseMetadata>
+      <RequestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</RequestId>
+   </ResponseMetadata>
+</ListUsersResponse>
\ No newline at end of file
diff --git a/labs/elb/src/test/resources/log4j.xml b/labs/iam/src/test/resources/log4j.xml
similarity index 75%
rename from labs/elb/src/test/resources/log4j.xml
rename to labs/iam/src/test/resources/log4j.xml
index 8c36a78..7527432 100644
--- a/labs/elb/src/test/resources/log4j.xml
+++ b/labs/iam/src/test/resources/log4j.xml
@@ -52,8 +52,8 @@
         </layout>
     </appender>
     <!-- A time/date based rolling appender -->
-    <appender name="LOADBALANCERFILE" class="org.apache.log4j.DailyRollingFileAppender">
-        <param name="File" value="target/test-data/jclouds-loadbalancer.log" />
+    <appender name="BLOBSTOREFILE" class="org.apache.log4j.DailyRollingFileAppender">
+        <param name="File" value="target/test-data/jclouds-blobstore.log" />
         <param name="Append" value="true" />
         <param name="DatePattern" value="'.'yyyy-MM-dd" />
         <param name="Threshold" value="TRACE" />
@@ -84,10 +84,36 @@
         </layout>
     </appender>
 
+    <!-- A time/date based rolling appender -->
+    <appender name="SSHFILE" class="org.apache.log4j.DailyRollingFileAppender">
+        <param name="File" value="target/test-data/jclouds-ssh.log" />
+        <param name="Append" value="true" />
+
+        <!-- Rollover at midnight each day -->
+        <param name="DatePattern" value="'.'yyyy-MM-dd" />
+
+        <param name="Threshold" value="TRACE" />
+
+        <layout class="org.apache.log4j.PatternLayout">
+            <!-- The default pattern: Date Priority [Category] Message\n -->
+            <param name="ConversionPattern" value="%d %-5p [%c] (%t) %m%n" />
+
+            <!--
+                The full pattern: Date MS Priority [Category]
+                (Thread:NDC) Message\n <param name="ConversionPattern"
+                value="%d %-5r %-5p [%c] (%t:%x) %m%n"/>
+            -->
+        </layout>
+    </appender>
+
     <appender name="ASYNCCOMPUTE" class="org.apache.log4j.AsyncAppender">
         <appender-ref ref="COMPUTEFILE" />
     </appender>
 
+    <appender name="ASYNCSSH" class="org.apache.log4j.AsyncAppender">
+        <appender-ref ref="SSHFILE" />
+    </appender>
+
     <appender name="ASYNC" class="org.apache.log4j.AsyncAppender">
         <appender-ref ref="FILE" />
     </appender>
@@ -96,8 +122,8 @@
         <appender-ref ref="WIREFILE" />
     </appender>
 
-    <appender name="ASYNCLOADBALANCER" class="org.apache.log4j.AsyncAppender">
-        <appender-ref ref="LOADBALANCERFILE" />
+    <appender name="ASYNCBLOBSTORE" class="org.apache.log4j.AsyncAppender">
+        <appender-ref ref="BLOBSTOREFILE" />
     </appender>
     <!-- ================ -->
     <!-- Limit categories -->
@@ -113,13 +139,17 @@
         <appender-ref ref="ASYNCWIRE" />
     </category>
 
+    <category name="jclouds.ssh">
+        <priority value="DEBUG" />
+        <appender-ref ref="ASYNCSSH" />
+    </category>
     <category name="jclouds.wire">
         <priority value="DEBUG" />
         <appender-ref ref="ASYNCWIRE" />
     </category>
-    <category name="jclouds.loadbalancer">
+    <category name="jclouds.blobstore">
         <priority value="DEBUG" />
-        <appender-ref ref="ASYNCLOADBALANCER" />
+        <appender-ref ref="ASYNCBLOBSTORE" />
     </category>
     <category name="jclouds.compute">
         <priority value="TRACE" />
diff --git a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/config/SDCComputeServiceContextModule.java b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/config/SDCComputeServiceContextModule.java
index b8c5a29..1dc909f 100644
--- a/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/config/SDCComputeServiceContextModule.java
+++ b/labs/joyent-sdc/src/main/java/org/jclouds/joyent/sdc/v6_5/compute/config/SDCComputeServiceContextModule.java
@@ -56,11 +56,11 @@
 import org.jclouds.joyent.sdc.v6_5.domain.datacenterscoped.MachineInDatacenter;
 import org.jclouds.joyent.sdc.v6_5.domain.datacenterscoped.PackageInDatacenter;
 import org.jclouds.util.Iterables2;
+import org.jclouds.util.Suppliers2;
 
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Function;
 import com.google.common.base.Supplier;
-import com.google.common.base.Suppliers;
 import com.google.common.cache.CacheBuilder;
 import com.google.common.cache.CacheLoader;
 import com.google.common.cache.LoadingCache;
@@ -134,7 +134,7 @@
    @Singleton
    protected Supplier<Map<String, Location>> createLocationIndexedById(
          @Memoized Supplier<Set<? extends Location>> locations) {
-      return Suppliers.compose(new Function<Set<? extends Location>, Map<String, Location>>() {
+      return Suppliers2.compose(new Function<Set<? extends Location>, Map<String, Location>>() {
 
          @Override
          public Map<String, Location> apply(Set<? extends Location> arg0) {
diff --git a/labs/nodepool/pom.xml b/labs/nodepool/pom.xml
index a60c8c7..b874118 100644
--- a/labs/nodepool/pom.xml
+++ b/labs/nodepool/pom.xml
@@ -1,26 +1,54 @@
-<?xml version="1.0"?>
-<project
-  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
-  xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+  Licensed to jclouds, Inc. (jclouds) under one or more
+  contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  jclouds licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
   <modelVersion>4.0.0</modelVersion>
   <parent>
-    <artifactId>jclouds-project</artifactId>
     <groupId>org.jclouds</groupId>
+    <artifactId>jclouds-project</artifactId>
     <version>1.5.0-SNAPSHOT</version>
     <relativePath>../../project/pom.xml</relativePath>
   </parent>
   <groupId>org.jclouds.labs</groupId>
   <artifactId>nodepool</artifactId>
-  <name>jclouds nodepool</name>
+  <name>jcloud nodepool api</name>
+  <description>jclouds components to access an implementation of Joyent SDC</description>
   <packaging>bundle</packaging>
+
   <properties>
-    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
-    <jsch.version>0.1.46</jsch.version>
-    <test.aws-ec2.identity>${test.aws.identity}</test.aws-ec2.identity>
-    <test.aws-ec2.credential>${test.aws.credential}</test.aws-ec2.credential>
+    <jclouds.nodepool.backend-provider>byon</jclouds.nodepool.backend-provider>
+    <jclouds.nodepool.backend-modules>org.jclouds.logging.slf4j.config.SLF4JLoggingModule,org.jclouds.sshj.config.SshjSshClientModule</jclouds.nodepool.backend-modules>
+    <test.nodepool.endpoint>FIXME_ENDPOINT</test.nodepool.endpoint>
+    <test.nodepool.api-version></test.nodepool.api-version>
+    <test.nodepool.build-version></test.nodepool.build-version>
+    <test.nodepool.identity>FIXME_IDENTITY</test.nodepool.identity>
+    <test.nodepool.credential>FIXME_CREDENTIALS</test.nodepool.credential>
     <jclouds.osgi.export>org.jclouds.nodepool*;version="${project.version}"</jclouds.osgi.export>
-    <jclouds.osgi.import>org.jclouds*;version="${project.version}",*</jclouds.osgi.import>
+    <jclouds.osgi.import>
+      org.jclouds.rest.internal;version="${project.version}",
+      org.jclouds*;version="${project.version}",
+      *
+    </jclouds.osgi.import>
   </properties>
+
   <dependencies>
     <dependency>
       <groupId>org.jclouds</groupId>
@@ -29,33 +57,37 @@
     </dependency>
     <dependency>
       <groupId>org.jclouds</groupId>
+      <artifactId>jclouds-blobstore</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.jclouds.api</groupId>
+      <artifactId>filesystem</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.jclouds</groupId>
+      <artifactId>jclouds-compute</artifactId>
+      <version>${project.version}</version>
+      <type>test-jar</type>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.jclouds.api</groupId>
+      <artifactId>byon</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.jclouds</groupId>
       <artifactId>jclouds-core</artifactId>
       <version>${project.version}</version>
       <type>test-jar</type>
       <scope>test</scope>
     </dependency>
     <dependency>
-      <groupId>org.jclouds</groupId>
-      <artifactId>jclouds-compute</artifactId>
-      <version>${project.version}</version>
-      <type>test-jar</type>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
-      <groupId>ch.qos.logback</groupId>
-      <artifactId>logback-classic</artifactId>
-      <version>1.0.0</version>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.jclouds.provider</groupId>
-      <artifactId>aws-ec2</artifactId>
-      <version>${project.version}</version>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
       <groupId>org.jclouds.driver</groupId>
-      <artifactId>jclouds-enterprise</artifactId>
+      <artifactId>jclouds-slf4j</artifactId>
       <version>${project.version}</version>
       <scope>test</scope>
     </dependency>
@@ -66,21 +98,45 @@
       <scope>test</scope>
     </dependency>
     <dependency>
-      <groupId>org.jclouds</groupId>
-      <artifactId>jclouds-scriptbuilder</artifactId>
-      <version>${project.version}</version>
-    </dependency>
-    <dependency>
-      <groupId>org.jclouds.driver</groupId>
-      <artifactId>jclouds-jsch</artifactId>
-      <version>${project.version}</version>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
-      <groupId>com.jcraft</groupId>
-      <artifactId>jsch</artifactId>
+      <groupId>ch.qos.logback</groupId>
+      <artifactId>logback-classic</artifactId>
+      <version>1.0.0</version>
       <scope>test</scope>
     </dependency>
   </dependencies>
+  
+  <profiles>
+    <profile>
+      <id>live</id>
+      <build>
+        <plugins>
+          <plugin>
+            <groupId>org.apache.maven.plugins</groupId>
+            <artifactId>maven-surefire-plugin</artifactId>
+            <executions>
+              <execution>
+                <id>integration</id>
+                <phase>integration-test</phase>
+                <goals>
+                  <goal>test</goal>
+                </goals>
+                <configuration>
+                  <systemPropertyVariables>
+                    <test.nodepool.endpoint>${test.nodepool.endpoint}</test.nodepool.endpoint>
+                    <test.nodepool.api-version>${test.nodepool.api-version}</test.nodepool.api-version>
+                    <test.nodepool.build-version>${test.nodepool.build-version}</test.nodepool.build-version>
+                    <test.nodepool.identity>${test.nodepool.identity}</test.nodepool.identity>
+                    <test.nodepool.credential>${test.nodepool.credential}</test.nodepool.credential>
+                    <jclouds.nodepool.backend-provider>${jclouds.nodepool.backend-provider}</jclouds.nodepool.backend-provider>
+                    <jclouds.nodepool.backend-modules>${jclouds.nodepool.backend-modules}</jclouds.nodepool.backend-modules>
+                  </systemPropertyVariables>
+                </configuration>
+              </execution>
+            </executions>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
+  </profiles>
 
 </project>
diff --git a/labs/nodepool/src/main/java/org/jclouds/nodepool/Backend.java b/labs/nodepool/src/main/java/org/jclouds/nodepool/Backend.java
new file mode 100644
index 0000000..0347b3c
--- /dev/null
+++ b/labs/nodepool/src/main/java/org/jclouds/nodepool/Backend.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.nodepool;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import javax.inject.Qualifier;
+
+/**
+ * Designates that this Resource qualifies an object to the back-end of the pool
+ * 
+ * @author Adrian Cole
+ */
+@Target( { ANNOTATION_TYPE, FIELD, METHOD, PARAMETER })
+@Retention(RUNTIME)
+@Qualifier
+public @interface Backend {
+
+}
diff --git a/labs/nodepool/src/main/java/org/jclouds/nodepool/NodePoolApiMetadata.java b/labs/nodepool/src/main/java/org/jclouds/nodepool/NodePoolApiMetadata.java
index 849349d..da8499d 100644
--- a/labs/nodepool/src/main/java/org/jclouds/nodepool/NodePoolApiMetadata.java
+++ b/labs/nodepool/src/main/java/org/jclouds/nodepool/NodePoolApiMetadata.java
@@ -18,18 +18,26 @@
  */
 package org.jclouds.nodepool;
 
-import static org.jclouds.nodepool.config.NodePoolComputeServiceProperties.BACKING_GROUP_PROPERTY;
-import static org.jclouds.nodepool.config.NodePoolComputeServiceProperties.MAX_SIZE_PROPERTY;
-import static org.jclouds.nodepool.config.NodePoolComputeServiceProperties.MIN_SIZE_PROPERTY;
-import static org.jclouds.nodepool.config.NodePoolComputeServiceProperties.REMOVE_DESTROYED_PROPERTY;
+import static org.jclouds.nodepool.config.NodePoolProperties.BACKEND_GROUP;
+import static org.jclouds.nodepool.config.NodePoolProperties.BACKEND_MODULES;
+import static org.jclouds.nodepool.config.NodePoolProperties.MAX_SIZE;
+import static org.jclouds.nodepool.config.NodePoolProperties.METADATA_CONTAINER;
+import static org.jclouds.nodepool.config.NodePoolProperties.MIN_SIZE;
+import static org.jclouds.nodepool.config.NodePoolProperties.REMOVE_DESTROYED;
 
 import java.net.URI;
 import java.util.Properties;
 
 import org.jclouds.apis.internal.BaseApiMetadata;
 import org.jclouds.compute.ComputeServiceContext;
+import org.jclouds.nodepool.config.BindBackendComputeService;
+import org.jclouds.nodepool.config.BindInputStreamToFilesystemBlobStore;
+import org.jclouds.nodepool.config.NodePoolComputeServiceContextModule;
 import org.jclouds.rest.internal.BaseRestApiMetadata;
 
+import com.google.common.collect.ImmutableSet;
+import com.google.inject.Module;
+
 public class NodePoolApiMetadata extends BaseApiMetadata {
 
    /** The serialVersionUID */
@@ -54,19 +62,28 @@
 
    public static Properties defaultProperties() {
       Properties properties = BaseRestApiMetadata.defaultProperties();
-      properties.setProperty(BACKING_GROUP_PROPERTY, "nodepool");
-      properties.setProperty(MAX_SIZE_PROPERTY, 10 + "");
-      properties.setProperty(MIN_SIZE_PROPERTY, 5 + "");
-      properties.setProperty(REMOVE_DESTROYED_PROPERTY, "false");
+      properties.setProperty(BACKEND_GROUP, "nodepool");
+      properties.setProperty(METADATA_CONTAINER, "nodes");
+      properties.setProperty(BACKEND_MODULES, "org.jclouds.logging.slf4j.config.SLF4JLoggingModule,org.jclouds.sshj.config.SshjSshClientModule");
+      properties.setProperty(MAX_SIZE, 10 + "");
+      properties.setProperty(MIN_SIZE, 5 + "");
+      properties.setProperty(REMOVE_DESTROYED, "false");
       return properties;
    }
 
    public static class Builder extends BaseApiMetadata.Builder {
       protected Builder() {
-         id("nodepool").name("node pool provider wrapper").identityName("Unused").defaultIdentity("nodepool")
-                  .defaultEndpoint("nodepool").documentation(
-                           URI.create("http://www.jclouds.org/documentation/userguide/compute")).view(
-                           ComputeServiceContext.class).defaultProperties(NodePoolApiMetadata.defaultProperties());
+         id("nodepool")
+         .name("node pool provider wrapper")
+         .identityName("backend identity")
+         .endpointName("backend endpoint").defaultEndpoint("fixme")
+         .documentation(URI.create("http://www.jclouds.org/documentation/userguide/compute"))
+         .view(ComputeServiceContext.class)
+         .defaultModules(ImmutableSet.<Class<? extends Module>> builder()
+               .add(NodePoolComputeServiceContextModule.class)
+               .add(BindInputStreamToFilesystemBlobStore.class)
+               .add(BindBackendComputeService.class).build())
+         .defaultProperties(NodePoolApiMetadata.defaultProperties());
       }
 
       @Override
diff --git a/labs/nodepool/src/main/java/org/jclouds/nodepool/NodePoolComputeService.java b/labs/nodepool/src/main/java/org/jclouds/nodepool/NodePoolComputeService.java
deleted file mode 100644
index c656809..0000000
--- a/labs/nodepool/src/main/java/org/jclouds/nodepool/NodePoolComputeService.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Licensed to jclouds, Inc. (jclouds) under one or more
- * contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  jclouds licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.jclouds.nodepool;
-
-import java.io.Closeable;
-import java.io.IOException;
-
-import org.jclouds.compute.ComputeService;
-import org.jclouds.nodepool.config.NodePoolComputeServiceProperties;
-import org.jclouds.nodepool.internal.EagerNodePoolComputeService;
-
-import com.google.common.util.concurrent.ListenableFuture;
-import com.google.inject.ImplementedBy;
-
-/**
- * A {@link ComputeService} wrapper that uses a pool of pre-loaded nodes to speed up creation times.
- * 
- * This interface extends the ComputeService with a backing pool of nodes, configured during
- * construction. The {@link #startPool()} and {@link #close()} methods are used to create and
- * destroy the pool and its associated nodes.
- * 
- * @author Andrew Kennedy
- * @author Gustavo Morozowski
- * @author David Alves
- * 
- * @see <a href="https://github.com/jclouds/jclouds/wiki/NodePool-Notes">NodePool Notes</a>
- * @see NodePoolComputeServiceProperties
- * @since 1.5.0
- */
-@ImplementedBy(EagerNodePoolComputeService.class)
-public interface NodePoolComputeService extends ComputeService, Closeable {
-
-   /**
-    * Starts the pool, may or may not start the actual nodes, depending on the implementation, i.e.
-    * the returned Set may be empty.
-    */
-   ListenableFuture<Void> startPool();
-
-   /**
-    * Returns true of the pool has been started by calling the {@link #startPool()} method.
-    */
-   boolean isStarted();
-
-   /**
-    * Returns the number of ready (pre-allocated) nodes in the pool.
-    */
-   int ready();
-
-   /**
-    * Returns the current size of the pool (nodes allocated on the backing compute service)
-    */
-   int size();
-
-   /**
-    * Returns the maximum amout of node the pool will allocate in the backing compute service.
-    */
-   int maxSize();
-
-   /**
-    * Close the pool and destroy all associated nodes.
-    */
-   void close() throws IOException;
-
-}
\ No newline at end of file
diff --git a/labs/nodepool/src/main/java/org/jclouds/nodepool/NodePoolComputeServiceAdapter.java b/labs/nodepool/src/main/java/org/jclouds/nodepool/NodePoolComputeServiceAdapter.java
new file mode 100644
index 0000000..3fb16f7
--- /dev/null
+++ b/labs/nodepool/src/main/java/org/jclouds/nodepool/NodePoolComputeServiceAdapter.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.nodepool;
+
+import org.jclouds.compute.JCloudsNativeComputeServiceAdapter;
+import org.jclouds.nodepool.internal.EagerNodePoolComputeServiceAdapter;
+
+import com.google.inject.ImplementedBy;
+
+@ImplementedBy(EagerNodePoolComputeServiceAdapter.class)
+public interface NodePoolComputeServiceAdapter extends JCloudsNativeComputeServiceAdapter {
+
+   int idleNodes();
+
+   int maxNodes();
+
+   int minNodes();
+
+   int usedNodes();
+
+   int currentSize();
+
+}
diff --git a/labs/nodepool/src/main/java/org/jclouds/nodepool/NodePoolComputeServiceContext.java b/labs/nodepool/src/main/java/org/jclouds/nodepool/NodePoolComputeServiceContext.java
new file mode 100644
index 0000000..1a54575
--- /dev/null
+++ b/labs/nodepool/src/main/java/org/jclouds/nodepool/NodePoolComputeServiceContext.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.nodepool;
+
+import javax.inject.Singleton;
+
+import org.jclouds.Context;
+import org.jclouds.compute.ComputeService;
+import org.jclouds.compute.Utils;
+import org.jclouds.compute.internal.ComputeServiceContextImpl;
+import org.jclouds.location.Provider;
+
+import com.google.common.reflect.TypeToken;
+import com.google.inject.Inject;
+
+@Singleton
+public class NodePoolComputeServiceContext extends ComputeServiceContextImpl {
+
+   private final NodePoolComputeServiceAdapter adapter;
+
+   @Inject
+   public NodePoolComputeServiceContext(@Provider Context backend, @Provider TypeToken<? extends Context> backendType,
+         ComputeService computeService, Utils utils, NodePoolComputeServiceAdapter adapter) {
+      super(backend, backendType, computeService, utils);
+      this.adapter = adapter;
+   }
+
+   public NodePoolStats getPoolStats() {
+      return new NodePoolStats(adapter.currentSize(), adapter.idleNodes(), adapter.usedNodes(), adapter.maxNodes(),
+            adapter.minNodes());
+   }
+}
diff --git a/labs/nodepool/src/main/java/org/jclouds/nodepool/NodePoolStats.java b/labs/nodepool/src/main/java/org/jclouds/nodepool/NodePoolStats.java
new file mode 100644
index 0000000..c06ebd2
--- /dev/null
+++ b/labs/nodepool/src/main/java/org/jclouds/nodepool/NodePoolStats.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.jclouds.nodepool;
+
+/**
+ * NodePool statistics and status.
+ * 
+ * @author David Alves
+ * 
+ */
+public class NodePoolStats {
+
+   private final int currentSize;
+   private final int idleNodes;
+   private final int usedNodes;
+   private final int maxNodes;
+   private final int minNodes;
+
+   NodePoolStats(int currentSize, int idleNodes, int usedNodes, int maxNodes, int minNodes) {
+      this.currentSize = currentSize;
+      this.idleNodes = idleNodes;
+      this.usedNodes = usedNodes;
+      this.maxNodes = maxNodes;
+      this.minNodes = minNodes;
+   }
+
+   /**
+    * The number of nodes currently allocated in the backend provider and in the pool.
+    */
+   public int currentSize() {
+      return currentSize;
+   }
+
+   /**
+    * The number of nodes in the pool not being used.
+    */
+   public int idleNodes() {
+      return idleNodes;
+   }
+
+   /**
+    * The number of nodes in the pool that are currently being used.
+    */
+   public int usedNodes() {
+      return usedNodes;
+   }
+
+   /**
+    * The maximum size the pool will reach.
+    */
+   public int maxNodes() {
+      return maxNodes;
+   }
+
+   /**
+    * The minimum size of the pool.
+    */
+   public int minNodes() {
+      return minNodes;
+   }
+
+}
diff --git a/labs/nodepool/src/main/java/org/jclouds/nodepool/config/BindBackendComputeService.java b/labs/nodepool/src/main/java/org/jclouds/nodepool/config/BindBackendComputeService.java
new file mode 100644
index 0000000..b3c3429
--- /dev/null
+++ b/labs/nodepool/src/main/java/org/jclouds/nodepool/config/BindBackendComputeService.java
@@ -0,0 +1,130 @@
+/*
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.nodepool.config;
+
+import java.net.URI;
+import java.util.Properties;
+import java.util.Set;
+
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import org.jclouds.ContextBuilder;
+import org.jclouds.compute.ComputeService;
+import org.jclouds.compute.ComputeServiceContext;
+import org.jclouds.compute.domain.Template;
+import org.jclouds.domain.Credentials;
+import org.jclouds.internal.FilterStringsBoundToInjectorByName;
+import org.jclouds.lifecycle.Closer;
+import org.jclouds.location.Provider;
+import org.jclouds.nodepool.Backend;
+import org.jclouds.rest.annotations.ApiVersion;
+import org.jclouds.rest.annotations.BuildVersion;
+import org.jclouds.util.Suppliers2;
+
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
+import com.google.inject.Exposed;
+import com.google.inject.Module;
+import com.google.inject.Provides;
+
+public class BindBackendComputeService extends BindJcloudsModules {
+
+  
+   @Provides
+   @Singleton
+   @Backend
+   protected String provideBackendProvider(@Named(NodePoolProperties.BACKEND_PROVIDER) String provider){
+      return provider;
+   }
+   
+   // things wrapped in suppliers are intentional. They can cause network i/o
+   // and shouldn't be invoked until after the injector is created.
+   
+   @Provides
+   @Singleton
+   @Backend
+   @Exposed
+   protected Supplier<ComputeService> makeBackendComputeService(@Backend final String provider,
+         @Backend final Set<Module> modules, @Provider final Credentials creds,
+         @Backend final Supplier<Properties> overrides, final Closer closer) {
+
+      return Suppliers.memoize(new Supplier<ComputeService>() {
+
+         @Override
+         public ComputeService get() {
+            ComputeServiceContext ctx = ContextBuilder.newBuilder(provider)
+                                                      .credentials(creds.identity, creds.credential)
+                                                      .overrides(overrides.get())
+                                                      .modules(modules)
+                                                      .buildView(ComputeServiceContext.class);
+            closer.addToClose(ctx);
+            return ctx.getComputeService();
+         }
+
+      });
+   }
+
+   private static final Predicate<String> keys = new Predicate<String>() {
+
+      @Override
+      public boolean apply(String input) {
+         return !input.startsWith("jclouds.nodepool") && !input.startsWith("nodepool");
+      }
+
+   };
+
+   @Provides
+   @Singleton
+   @Backend
+   protected Supplier<Properties> propertiesFor(final FilterStringsBoundToInjectorByName filterStringsBoundByName,
+         @Backend final String provider, @Provider final Supplier<URI> endpoint, @ApiVersion final String apiVersion,
+         @BuildVersion final String buildVersion) {
+      return Suppliers.memoize(new Supplier<Properties>() {
+
+         @Override
+         public Properties get() {
+            Properties props = new Properties();
+            props.putAll(filterStringsBoundByName.apply(keys));
+            props.put(provider + ".endpoint", endpoint.get().toASCIIString());
+            props.put(provider + ".api-version", apiVersion);
+            props.put(provider + ".build-version", buildVersion);
+            return props;
+         }
+
+      });
+   }
+
+   @Provides
+   @Singleton
+   @Backend
+   @Exposed
+   protected Supplier<Template> makeBackendTemplate(@Backend Supplier<ComputeService> compute) {
+      return Suppliers.memoize(Suppliers2.compose(new Function<ComputeService, Template>() {
+
+         @Override
+         public Template apply(ComputeService input) {
+            return input.templateBuilder().build();
+         }
+
+      }, compute));
+   }
+}
diff --git a/labs/nodepool/src/main/java/org/jclouds/nodepool/config/BindInputStreamToFilesystemBlobStore.java b/labs/nodepool/src/main/java/org/jclouds/nodepool/config/BindInputStreamToFilesystemBlobStore.java
new file mode 100644
index 0000000..ffb7665
--- /dev/null
+++ b/labs/nodepool/src/main/java/org/jclouds/nodepool/config/BindInputStreamToFilesystemBlobStore.java
@@ -0,0 +1,88 @@
+/*
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.nodepool.config;
+
+import java.io.File;
+import java.io.InputStream;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import org.jclouds.ContextBuilder;
+import org.jclouds.blobstore.BlobStoreContext;
+import org.jclouds.filesystem.reference.FilesystemConstants;
+import org.jclouds.lifecycle.Closer;
+import org.jclouds.nodepool.Backend;
+import org.jclouds.util.Suppliers2;
+
+import com.google.common.base.Function;
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
+import com.google.inject.Exposed;
+import com.google.inject.Module;
+import com.google.inject.Provides;
+
+public class BindInputStreamToFilesystemBlobStore extends BindJcloudsModules {
+
+   @Override
+   protected void configure() {
+   }
+
+   @Provides
+   @Singleton
+   @Exposed
+   protected Supplier<Map<String, InputStream>> provideInputStreamMapFromBlobStore(Supplier<BlobStoreContext> in,
+         @Named(NodePoolProperties.METADATA_CONTAINER) final String container) {
+      return Suppliers.memoize(Suppliers2.compose(new Function<BlobStoreContext, Map<String, InputStream>>() {
+
+         @Override
+         public Map<String, InputStream> apply(BlobStoreContext input) {
+            input.getBlobStore().createContainerInLocation(null, container);
+            return input.createInputStreamMap(container);
+         }
+
+      }, in));
+   }
+
+   @Provides
+   @Singleton
+   protected Supplier<BlobStoreContext> makeBlobStoreContext(@Named(NodePoolProperties.BASEDIR) final String basedir,
+         @Backend final Set<Module> modules, final Closer closer) {
+      final Properties overrides = new Properties();
+      overrides.setProperty(FilesystemConstants.PROPERTY_BASEDIR, basedir);
+      return Suppliers.memoize(new Supplier<BlobStoreContext>() {
+
+         @Override
+         public BlobStoreContext get() {
+            // GAE alert!
+            new File(basedir).mkdirs();
+            BlobStoreContext returnVal = ContextBuilder.newBuilder("filesystem")
+                                                       .overrides(overrides)
+                                                       .modules(modules) 
+                                                       .buildView(BlobStoreContext.class);
+            closer.addToClose(returnVal);
+            return returnVal;
+         }
+         
+      });
+   }
+}
diff --git a/labs/nodepool/src/main/java/org/jclouds/nodepool/config/BindJcloudsModules.java b/labs/nodepool/src/main/java/org/jclouds/nodepool/config/BindJcloudsModules.java
new file mode 100644
index 0000000..6dcab7f
--- /dev/null
+++ b/labs/nodepool/src/main/java/org/jclouds/nodepool/config/BindJcloudsModules.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.nodepool.config;
+
+import java.util.Set;
+
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import org.jclouds.nodepool.Backend;
+
+import com.google.common.base.Function;
+import com.google.common.base.Splitter;
+import com.google.common.base.Throwables;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.inject.Module;
+import com.google.inject.PrivateModule;
+import com.google.inject.Provides;
+
+public class BindJcloudsModules extends PrivateModule {
+
+   @Override
+   protected void configure() {
+   }
+
+   @Provides
+   @Singleton
+   @Backend
+   protected Set<Module> provideBackendModules(@Named(NodePoolProperties.BACKEND_MODULES) String moduleString) {
+      return ImmutableSet.copyOf(Iterables.transform(Splitter.on(',').split(moduleString),
+            new Function<String, Module>() {
+
+               @Override
+               public Module apply(String input) {
+                  try {
+                     return Module.class.cast(Class.forName(input).newInstance());
+                  } catch (InstantiationException e) {
+                     throw Throwables.propagate(e);
+                  } catch (IllegalAccessException e) {
+                     throw Throwables.propagate(e);
+                  } catch (ClassNotFoundException e) {
+                     throw Throwables.propagate(e);
+                  }
+               }
+            }));
+   }
+}
diff --git a/labs/nodepool/src/main/java/org/jclouds/nodepool/config/NodePoolComputeServiceContextModule.java b/labs/nodepool/src/main/java/org/jclouds/nodepool/config/NodePoolComputeServiceContextModule.java
new file mode 100644
index 0000000..8ced740
--- /dev/null
+++ b/labs/nodepool/src/main/java/org/jclouds/nodepool/config/NodePoolComputeServiceContextModule.java
@@ -0,0 +1,26 @@
+package org.jclouds.nodepool.config;
+
+import org.jclouds.compute.config.JCloudsNativeComputeServiceAdapterContextModule;
+import org.jclouds.compute.domain.Hardware;
+import org.jclouds.compute.domain.Image;
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.domain.Location;
+import org.jclouds.nodepool.NodePoolComputeServiceAdapter;
+import org.jclouds.nodepool.internal.JsonNodeMetadataStore;
+import org.jclouds.nodepool.internal.NodeMetadataStore;
+
+public class NodePoolComputeServiceContextModule extends JCloudsNativeComputeServiceAdapterContextModule {
+
+   public NodePoolComputeServiceContextModule() {
+      super(NodePoolComputeServiceAdapter.class);
+   }
+   
+   @Override
+   protected void configure() {
+      super.configure();
+      bind(NodeMetadataStore.class).to(JsonNodeMetadataStore.class);
+      install(new LocationsFromComputeServiceAdapterModule<NodeMetadata, Hardware, Image, Location>() {
+      });
+   }
+
+}
diff --git a/labs/nodepool/src/main/java/org/jclouds/nodepool/config/NodePoolComputeServiceProperties.java b/labs/nodepool/src/main/java/org/jclouds/nodepool/config/NodePoolComputeServiceProperties.java
deleted file mode 100644
index 96fae93..0000000
--- a/labs/nodepool/src/main/java/org/jclouds/nodepool/config/NodePoolComputeServiceProperties.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Licensed to jclouds, Inc. (jclouds) under one or more
- * contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  jclouds licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.jclouds.nodepool.config;
-
-/**
- * Constants for the {@link org.jclouds.nodepool.NodePoolComputeService}.
- */
-public interface NodePoolComputeServiceProperties {
-
-   /**
-    * Property to set the name of the backing group used for pooled nodes.
-    */
-   public static final String BACKING_GROUP_PROPERTY = "jclouds.nodepool.backing-group";
-
-   /**
-    * Property to set the template that will be used to create the nodes in the pool.
-    */
-   public static final String BACKING_TEMPLATE_PROPERTY = "jclouds.nodepool.backing-template";
-
-   /**
-    * Property to set the maximum size of the pool. Set this to {@literal -1} to have an unlimited
-    * pool size.
-    */
-   public static final String MAX_SIZE_PROPERTY = "jclouds.nodepool.max-size";
-
-   /**
-    * Property to set the minimum (initial) size of the pool.
-    */
-   public static final String MIN_SIZE_PROPERTY = "jclouds.nodepool.min-size";
-
-   /**
-    * Property to set the pool behaviour to remove destroyed nodes rather than returning them to the
-    * pool for re-use.
-    */
-   public static final String REMOVE_DESTROYED_PROPERTY = "jclouds.nodepool.remove-destroyed";
-
-}
\ No newline at end of file
diff --git a/labs/nodepool/src/main/java/org/jclouds/nodepool/config/NodePoolProperties.java b/labs/nodepool/src/main/java/org/jclouds/nodepool/config/NodePoolProperties.java
new file mode 100644
index 0000000..80aeb86
--- /dev/null
+++ b/labs/nodepool/src/main/java/org/jclouds/nodepool/config/NodePoolProperties.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.nodepool.config;
+
+/**
+ * Constants for the {@link org.jclouds.nodepool.NodePoolComputeService}.
+ */
+public interface NodePoolProperties {
+
+
+   /**
+    * Property to set the name of the backend group used for pooled nodes.
+    */
+   public static final String BACKEND_GROUP = "jclouds.nodepool.backend-group";
+
+   /**
+    * Property to set the provider or api of backend the pool
+    */
+   public static final String BACKEND_PROVIDER = "jclouds.nodepool.backend-provider";
+
+   /**
+    * Property to set the modules the backend will use for ssh and logging, comma delimited
+    */
+   public static final String BACKEND_MODULES = "jclouds.nodepool.backend-modules";
+
+   /**
+    * Property to set the basedir where metadata will be stored
+    */
+   public static final String BASEDIR = "jclouds.nodepool.basedir";
+   
+   /**
+    * Property to set the container where metadata will be stored
+    */
+   public static final String METADATA_CONTAINER = "jclouds.nodepool.metadata-container";
+
+   /**
+    * Property to set the maximum size of the pool. Set this to {@literal -1} to have an unlimited
+    * pool size.
+    */
+   public static final String MAX_SIZE = "jclouds.nodepool.max-size";
+
+   /**
+    * Property to set the minimum (initial) size of the pool.
+    */
+   public static final String MIN_SIZE = "jclouds.nodepool.min-size";
+
+   /**
+    * Property to set the pool behaviour to remove destroyed nodes rather than returning them to the
+    * pool for re-use.
+    */
+   public static final String REMOVE_DESTROYED = "jclouds.nodepool.remove-destroyed";
+
+}
\ No newline at end of file
diff --git a/labs/nodepool/src/main/java/org/jclouds/nodepool/internal/BaseNodePoolComputeService.java b/labs/nodepool/src/main/java/org/jclouds/nodepool/internal/BaseNodePoolComputeService.java
deleted file mode 100644
index 554cd46..0000000
--- a/labs/nodepool/src/main/java/org/jclouds/nodepool/internal/BaseNodePoolComputeService.java
+++ /dev/null
@@ -1,335 +0,0 @@
-package org.jclouds.nodepool.internal;
-
-import static com.google.common.base.Preconditions.checkState;
-import static com.google.common.collect.Iterables.filter;
-import static com.google.common.collect.Iterables.find;
-
-import java.util.Map;
-import java.util.NoSuchElementException;
-import java.util.Set;
-import java.util.Map.Entry;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import org.jclouds.compute.ComputeService;
-import org.jclouds.compute.ComputeServiceContext;
-import org.jclouds.compute.RunNodesException;
-import org.jclouds.compute.RunScriptOnNodesException;
-import org.jclouds.compute.domain.ComputeMetadata;
-import org.jclouds.compute.domain.ExecResponse;
-import org.jclouds.compute.domain.Hardware;
-import org.jclouds.compute.domain.Image;
-import org.jclouds.compute.domain.NodeMetadata;
-import org.jclouds.compute.domain.NodeMetadataBuilder;
-import org.jclouds.compute.domain.Template;
-import org.jclouds.compute.domain.TemplateBuilder;
-import org.jclouds.compute.extensions.ImageExtension;
-import org.jclouds.compute.options.RunScriptOptions;
-import org.jclouds.compute.options.TemplateOptions;
-import org.jclouds.domain.Location;
-import org.jclouds.nodepool.NodePoolComputeService;
-import org.jclouds.scriptbuilder.domain.Statement;
-
-import com.google.common.base.Function;
-import com.google.common.base.Optional;
-import com.google.common.base.Predicate;
-import com.google.common.base.Predicates;
-import com.google.common.collect.FluentIterable;
-import com.google.common.collect.HashMultimap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Multimap;
-import com.google.common.collect.Sets;
-import com.google.common.util.concurrent.ListenableFuture;
-
-/**
- * A base class for {@link NodePoolComputeService}, takes care of keeping (not changing assignments)
- * and of everything that does not change the pool.
- * 
- * @author David Alves
- * 
- */
-public abstract class BaseNodePoolComputeService implements NodePoolComputeService {
-
-   protected final ComputeService backingComputeService;
-   protected final String poolGroupName;
-   protected final Template template;
-   protected final Image image;
-   protected final Hardware hardware;
-   protected final Location location;
-
-   // assignments of nodes to group names
-   protected final Multimap<String, NodeMetadata> assignments = HashMultimap.create();
-
-   protected final AtomicBoolean started = new AtomicBoolean(false);
-
-   public BaseNodePoolComputeService(ComputeServiceContext backingComputeServiceContext, String poolGroupNamePrefix,
-            Template backingTemplate) {
-      this.backingComputeService = backingComputeServiceContext.getComputeService();
-      this.poolGroupName = poolGroupNamePrefix;
-      this.template = backingTemplate == null ? this.backingComputeService.templateBuilder().build() : backingTemplate;
-      this.image = this.template.getImage();
-      this.hardware = this.template.getHardware();
-      this.location = this.template.getLocation();
-   }
-
-   /**
-    * Checks which nodes match the users predicate and builds a predicate that returns true to their
-    * specific ids.
-    * 
-    * @param filter
-    * @return
-    */
-   private Predicate<NodeMetadata> transformUserPredicateSpecificIdPredicate(Predicate<NodeMetadata> filter) {
-      Iterable<Map.Entry<String, NodeMetadata>> relevantAssginemnts = filterAssignmentsBasedOnUserPredicate(filter);
-      final Set<String> ids = Sets.newHashSet();
-      for (Map.Entry<String, NodeMetadata> assignment : relevantAssginemnts) {
-         ids.add(assignment.getValue().getId());
-      }
-      return new Predicate<NodeMetadata>() {
-         @Override
-         public boolean apply(NodeMetadata input) {
-            return ids.contains(input.getId());
-         }
-      };
-
-   }
-
-   @Override
-   public boolean isStarted() {
-      return started.get();
-   }
-
-   // TODO this is n^2 expensive. s
-   private Map<? extends NodeMetadata, ExecResponse> transformBackendExecutionMapIntoFrontend(
-            Map<? extends NodeMetadata, ExecResponse> backendMap) {
-      Map<NodeMetadata, ExecResponse> frontendMap = Maps.newHashMapWithExpectedSize(backendMap.size());
-      for (Map.Entry<? extends NodeMetadata, ExecResponse> entry : backendMap.entrySet()) {
-         Map.Entry<String, NodeMetadata> assignmentEntry = findAssigmentEntry(entry.getKey().getId());
-         frontendMap
-                  .put(toFrontendNodemetadata(assignmentEntry.getValue(), assignmentEntry.getKey()), entry.getValue());
-      }
-      return frontendMap;
-   }
-
-   protected Map.Entry<String, NodeMetadata> findAssigmentEntry(final String id) {
-      // TODO reverse lookup data structure would be faster but will pools be that big ?
-      return find(assignments.entries(), new Predicate<Map.Entry<String, NodeMetadata>>() {
-         @Override
-         public boolean apply(Entry<String, NodeMetadata> entry) {
-            return entry.getValue().getId().equals(id);
-         }
-      });
-   }
-
-   protected NodeMetadata toFrontendNodemetadata(NodeMetadata backendNodeMetadata, String group) {
-      return NodeMetadataBuilder.fromNodeMetadata(backendNodeMetadata).group(group).build();
-   }
-
-   /**
-    * Because a lot of predicates are based on group info we need that to check wether the predicate
-    * matches.
-    */
-   protected Iterable<Map.Entry<String, NodeMetadata>> filterAssignmentsBasedOnUserPredicate(
-            final Predicate<NodeMetadata> userFilter) {
-      return filter(assignments.entries(), new Predicate<Map.Entry<String, NodeMetadata>>() {
-         @Override
-         public boolean apply(Entry<String, NodeMetadata> input) {
-            return userFilter.apply(toFrontendNodemetadata(input.getValue(), input.getKey()));
-         }
-      });
-   }
-
-   @Override
-   public NodeMetadata getNodeMetadata(String id) {
-      checkState(started.get(), "pool is not started");
-      Map.Entry<String, NodeMetadata> assigmentEntry = findAssigmentEntry(id);
-      return toFrontendNodemetadata(assigmentEntry.getValue(), assigmentEntry.getKey());
-   }
-
-   @Override
-   public Map<? extends NodeMetadata, ExecResponse> runScriptOnNodesMatching(Predicate<NodeMetadata> filter,
-            String runScript) throws RunScriptOnNodesException {
-      return runScriptOnNodesMatching(filter, runScript, new RunScriptOptions());
-   }
-
-   @Override
-   public Map<? extends NodeMetadata, ExecResponse> runScriptOnNodesMatching(Predicate<NodeMetadata> filter,
-            Statement runScript) throws RunScriptOnNodesException {
-      return runScriptOnNodesMatching(filter, runScript, new RunScriptOptions());
-   }
-
-   @Override
-   public Map<? extends NodeMetadata, ExecResponse> runScriptOnNodesMatching(Predicate<NodeMetadata> filter,
-            String runScript, RunScriptOptions options) throws RunScriptOnNodesException {
-      checkState(started.get(), "pool is not started");
-      return transformBackendExecutionMapIntoFrontend(backingComputeService.runScriptOnNodesMatching(
-               transformUserPredicateSpecificIdPredicate(filter), runScript, options));
-   }
-
-   @Override
-   public Map<? extends NodeMetadata, ExecResponse> runScriptOnNodesMatching(Predicate<NodeMetadata> filter,
-            Statement runScript, RunScriptOptions options) throws RunScriptOnNodesException {
-      checkState(started.get(), "pool is not started");
-      return transformBackendExecutionMapIntoFrontend(backingComputeService.runScriptOnNodesMatching(
-               transformUserPredicateSpecificIdPredicate(filter), runScript, options));
-   }
-
-   @Override
-   public Set<? extends ComputeMetadata> listNodes() {
-      checkState(started.get(), "pool is not started");
-      return listNodesDetailsMatching(Predicates.alwaysTrue());
-   }
-
-   @SuppressWarnings( { "rawtypes", "unchecked" })
-   @Override
-   public Set<? extends NodeMetadata> listNodesDetailsMatching(Predicate filter) {
-      checkState(started.get(), "pool is not started");
-      return FluentIterable.from(filterAssignmentsBasedOnUserPredicate(filter)).transform(
-               new Function<Map.Entry<String, NodeMetadata>, NodeMetadata>() {
-                  @Override
-                  public NodeMetadata apply(Entry<String, NodeMetadata> input) {
-                     return toFrontendNodemetadata(input.getValue(), input.getKey());
-                  }
-               }).toImmutableSet();
-   }
-
-   @Override
-   public void rebootNodesMatching(final Predicate<NodeMetadata> filter) {
-      checkState(started.get(), "pool is not started");
-      backingComputeService.rebootNodesMatching(transformUserPredicateSpecificIdPredicate(filter));
-   }
-
-   @Override
-   public void resumeNodesMatching(Predicate<NodeMetadata> filter) {
-      checkState(started.get(), "pool is not started");
-      backingComputeService.resumeNodesMatching(transformUserPredicateSpecificIdPredicate(filter));
-   }
-
-   @Override
-   public void suspendNodesMatching(Predicate<NodeMetadata> filter) {
-      checkState(started.get(), "pool is not started");
-      backingComputeService.suspendNodesMatching(transformUserPredicateSpecificIdPredicate(filter));
-   }
-
-   @Override
-   public ComputeServiceContext getContext() {
-      // not sure this is enough, should we have our own?
-      return backingComputeService.getContext();
-   }
-
-   // we ignore user provided templates and options
-
-   @Override
-   public Set<? extends NodeMetadata> createNodesInGroup(String group, int count, Template template)
-            throws RunNodesException {
-      return createNodesInGroup(group, count);
-   }
-
-   @Override
-   public Set<? extends NodeMetadata> createNodesInGroup(String group, int count, TemplateOptions templateOptions)
-            throws RunNodesException {
-      return createNodesInGroup(group, count);
-   }
-
-   @Override
-   public TemplateBuilder templateBuilder() {
-      return backingComputeService.templateBuilder().fromTemplate(template);
-   }
-
-   @Override
-   public TemplateOptions templateOptions() {
-      return backingComputeService.templateOptions();
-   }
-
-   @Override
-   public Set<? extends Hardware> listHardwareProfiles() {
-      return ImmutableSet.of(hardware);
-   }
-
-   @Override
-   public Set<? extends Image> listImages() {
-      return ImmutableSet.of(image);
-
-   }
-
-   @Override
-   public Image getImage(String id) {
-      return image.getId().equals(id) ? image : null;
-   }
-
-   @Override
-   public Set<? extends Location> listAssignableLocations() {
-      return ImmutableSet.of(location);
-   }
-
-   @Override
-   public void suspendNode(String id) {
-      if (findAssigmentEntry(id) != null) {
-         backingComputeService.suspendNode(id);
-      }
-      throw new NoSuchElementException(id);
-   }
-
-   @Override
-   public void resumeNode(String id) {
-      if (findAssigmentEntry(id) != null) {
-         backingComputeService.resumeNode(id);
-      }
-      throw new NoSuchElementException(id);
-   }
-
-   @Override
-   public void rebootNode(String id) {
-      if (findAssigmentEntry(id) != null) {
-         backingComputeService.rebootNode(id);
-      }
-      throw new NoSuchElementException(id);
-   }
-
-   @Override
-   public ExecResponse runScriptOnNode(String id, Statement runScript) {
-      if (findAssigmentEntry(id) != null) {
-         return runScriptOnNode(id, runScript, new RunScriptOptions());
-      }
-      throw new NoSuchElementException(id);
-   }
-
-   @Override
-   public ExecResponse runScriptOnNode(String id, String runScript) {
-      if (findAssigmentEntry(id) != null) {
-         return runScriptOnNode(id, runScript, new RunScriptOptions());
-      }
-      throw new NoSuchElementException(id);
-
-   }
-
-   @Override
-   public ExecResponse runScriptOnNode(String id, Statement runScript, RunScriptOptions options) {
-      if (findAssigmentEntry(id) != null) {
-         return backingComputeService.runScriptOnNode(id, runScript, options);
-      }
-      throw new NoSuchElementException(id);
-   }
-
-   @Override
-   public ListenableFuture<ExecResponse> submitScriptOnNode(String id, Statement runScript, RunScriptOptions options) {
-      if (findAssigmentEntry(id) != null) {
-         return backingComputeService.submitScriptOnNode(id, runScript, options);
-      }
-      throw new NoSuchElementException(id);
-   }
-
-   @Override
-   public ExecResponse runScriptOnNode(String id, String runScript, RunScriptOptions options) {
-      if (findAssigmentEntry(id) != null) {
-         return backingComputeService.runScriptOnNode(id, runScript, options);
-      }
-      throw new NoSuchElementException(id);
-   }
-
-   @Override
-   public Optional<ImageExtension> getImageExtension() {
-      return Optional.absent();
-   }
-
-}
diff --git a/labs/nodepool/src/main/java/org/jclouds/nodepool/internal/BaseNodePoolComputeServiceAdapter.java b/labs/nodepool/src/main/java/org/jclouds/nodepool/internal/BaseNodePoolComputeServiceAdapter.java
new file mode 100644
index 0000000..b360dfb
--- /dev/null
+++ b/labs/nodepool/src/main/java/org/jclouds/nodepool/internal/BaseNodePoolComputeServiceAdapter.java
@@ -0,0 +1,140 @@
+/*
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.nodepool.internal;
+
+import static com.google.common.base.Preconditions.checkState;
+import static org.jclouds.nodepool.config.NodePoolProperties.BACKEND_GROUP;
+
+import java.util.NoSuchElementException;
+import java.util.Set;
+
+import javax.inject.Named;
+
+import org.jclouds.compute.ComputeService;
+import org.jclouds.compute.RunNodesException;
+import org.jclouds.compute.domain.Hardware;
+import org.jclouds.compute.domain.Image;
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.domain.Template;
+import org.jclouds.compute.predicates.NodePredicates;
+import org.jclouds.domain.Location;
+import org.jclouds.nodepool.Backend;
+import org.jclouds.nodepool.NodePoolComputeServiceAdapter;
+
+import com.google.common.base.Supplier;
+import com.google.common.base.Throwables;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+
+/**
+ * A base class for {@link NodePoolComputeService}, takes care of keeping (not
+ * changing assignments) and of everything that does not change the pool.
+ * 
+ * @author David Alves
+ * 
+ */
+public abstract class BaseNodePoolComputeServiceAdapter implements NodePoolComputeServiceAdapter {
+
+   protected final Supplier<ComputeService> backendComputeService;
+   protected final Supplier<Template> backendTemplate;
+   protected final String poolGroupName;
+   protected final NodeMetadataStore metadataStore;
+
+   public BaseNodePoolComputeServiceAdapter(@Backend Supplier<ComputeService> backendComputeService,
+         @Backend Supplier<Template> backendTemplate, @Named(BACKEND_GROUP) String poolGroupNamePrefix,
+         NodeMetadataStore metadataStore) {
+      this.backendComputeService = backendComputeService;
+      this.poolGroupName = poolGroupNamePrefix;
+      this.backendTemplate = backendTemplate;
+      this.metadataStore = metadataStore;
+   }
+
+   @Override
+   public NodeMetadata getNode(String id) {
+      NodeMetadata backendMetadata = backendComputeService.get().getNodeMetadata(id);
+      checkState(backendMetadata.getGroup().equals(backendMetadata));
+      return metadataStore.load(backendMetadata);
+   }
+   
+   @Override
+   public Iterable<NodeMetadata> listNodes() {
+      return metadataStore.loadAll(getBackendNodes());
+   }
+
+   @Override
+   public Iterable<Hardware> listHardwareProfiles() {
+      return ImmutableSet.of(backendTemplate.get().getHardware());
+   }
+
+   @Override
+   public Iterable<Image> listImages() {
+      return ImmutableSet.of(backendTemplate.get().getImage());
+
+   }
+
+   @Override
+   public Iterable<Location> listLocations() {
+      return ImmutableSet.of(backendTemplate.get().getLocation());
+   }
+
+   @Override
+   public Image getImage(String id) {
+      Image backendImage = backendTemplate.get().getImage();
+      return backendImage.getId().equals(id) ? backendImage : null;
+   }
+
+   @Override
+   public void suspendNode(String id) {
+      if (getNode(id) != null) {
+         backendComputeService.get().suspendNode(id);
+      }
+      throw new NoSuchElementException(id);
+   }
+
+   @Override
+   public void resumeNode(String id) {
+      if (getNode(id) != null) {
+         backendComputeService.get().resumeNode(id);
+      }
+      throw new NoSuchElementException(id);
+   }
+
+   @Override
+   public void rebootNode(String id) {
+      if (getNode(id) != null) {
+         backendComputeService.get().rebootNode(id);
+      }
+      throw new NoSuchElementException(id);
+   }
+
+
+   protected Set<NodeMetadata> getBackendNodes() {
+      return ImmutableSet.copyOf(Iterables.filter(backendComputeService.get().listNodesDetailsMatching(
+            NodePredicates.all()), NodePredicates.inGroup(poolGroupName)));
+   }
+
+   protected void addToPool(int number) {
+      try {
+         backendComputeService.get().createNodesInGroup(poolGroupName, number, backendTemplate.get());
+      } catch (RunNodesException e) {
+         throw Throwables.propagate(e);
+      }
+   }
+
+}
diff --git a/labs/nodepool/src/main/java/org/jclouds/nodepool/internal/EagerNodePoolComputeService.java b/labs/nodepool/src/main/java/org/jclouds/nodepool/internal/EagerNodePoolComputeService.java
deleted file mode 100644
index f3d0481d..0000000
--- a/labs/nodepool/src/main/java/org/jclouds/nodepool/internal/EagerNodePoolComputeService.java
+++ /dev/null
@@ -1,250 +0,0 @@
-package org.jclouds.nodepool.internal;
-
-import static com.google.common.base.Preconditions.checkState;
-import static com.google.common.collect.Iterables.removeIf;
-import static com.google.common.collect.Iterables.transform;
-import static org.jclouds.nodepool.config.NodePoolComputeServiceProperties.BACKING_GROUP_PROPERTY;
-import static org.jclouds.nodepool.config.NodePoolComputeServiceProperties.BACKING_TEMPLATE_PROPERTY;
-import static org.jclouds.nodepool.config.NodePoolComputeServiceProperties.MAX_SIZE_PROPERTY;
-import static org.jclouds.nodepool.config.NodePoolComputeServiceProperties.MIN_SIZE_PROPERTY;
-import static org.jclouds.nodepool.config.NodePoolComputeServiceProperties.REMOVE_DESTROYED_PROPERTY;
-
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReentrantLock;
-
-import javax.annotation.Nullable;
-import javax.annotation.Resource;
-import javax.inject.Inject;
-import javax.inject.Named;
-
-import org.jclouds.Constants;
-import org.jclouds.compute.ComputeServiceContext;
-import org.jclouds.compute.RunNodesException;
-import org.jclouds.compute.domain.NodeMetadata;
-import org.jclouds.compute.domain.NodeMetadataBuilder;
-import org.jclouds.compute.domain.Template;
-import org.jclouds.compute.domain.NodeMetadata.Status;
-import org.jclouds.compute.predicates.NodePredicates;
-import org.jclouds.compute.reference.ComputeServiceConstants;
-import org.jclouds.concurrent.Futures;
-import org.jclouds.logging.Logger;
-import org.jclouds.nodepool.NodePoolComputeService;
-
-import com.google.common.base.Function;
-import com.google.common.base.Predicate;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Sets;
-import com.google.common.util.concurrent.ListenableFuture;
-
-/**
- * An eager {@link NodePoolComputeService}. Eagerly builds and maintains a pool of nodes. It's only
- * "started" after min nodes are allocated and available.
- * 
- * @author David Alves
- * 
- */
-public class EagerNodePoolComputeService extends BaseNodePoolComputeService {
-
-   @Resource
-   @Named(ComputeServiceConstants.COMPUTE_LOGGER)
-   protected Logger logger = Logger.NULL;
-
-   private final int maxSize;
-   private final boolean reuseDestroyed;
-   private final int minSize;
-   private final ExecutorService executor;
-
-   // set of available nodes
-   private Set<NodeMetadata> available = Sets.newHashSet();
-
-   // lock associated with changes to the pool since they happen asynchronously
-   private final Lock lock = new ReentrantLock();
-
-   // all the nodes in the pool (associated or not)
-   private final Set<NodeMetadata> poolNodes = Sets.newLinkedHashSet();
-
-   @Inject
-   public EagerNodePoolComputeService(ComputeServiceContext backingComputeServiceContext,
-            @Named(BACKING_GROUP_PROPERTY) String poolGroupPrefix,
-            @Named(MAX_SIZE_PROPERTY) int maxSize, @Named(MIN_SIZE_PROPERTY) int minSize,
-            @Named(REMOVE_DESTROYED_PROPERTY) boolean readdDestroyed,
-            @Nullable @Named(BACKING_TEMPLATE_PROPERTY) Template backingTemplate,
-            @Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor) {
-      super(backingComputeServiceContext, poolGroupPrefix, backingTemplate);
-      this.maxSize = maxSize;
-      this.minSize = minSize;
-      this.reuseDestroyed = readdDestroyed;
-      this.executor = executor;
-   }
-
-   @Override
-   public synchronized Set<? extends NodeMetadata> createNodesInGroup(String group, int count) throws RunNodesException {
-      checkState(started.get(), "pool is not started");
-      try {
-         return assignPoolNodes(group, count);
-      } catch (Exception e) {
-         Set<NodeMetadata> nodes = Collections.emptySet();
-         Map<String, Exception> executionExceptions = ImmutableMap.of("poolnode", e);
-         Map<NodeMetadata, Exception> failedNodes = ImmutableMap.of(new NodeMetadataBuilder().id("poolnode").status(
-                  Status.ERROR).build(), e);
-         throw new RunNodesException(group, count, template, nodes, executionExceptions, failedNodes);
-      }
-   }
-
-   @Override
-   public synchronized void destroyNode(String id) {
-      checkState(started.get(), "pool is not started");
-      unassignNode(id);
-   }
-
-   @Override
-   public synchronized Set<? extends NodeMetadata> destroyNodesMatching(Predicate<NodeMetadata> filter) {
-      checkState(started.get(), "pool is not started");
-      // copy the set of nodes to unassign because we'll be altering the assignments map.
-      Set<Map.Entry<String, NodeMetadata>> poolNodesToUnassign = Sets
-               .newHashSet(filterAssignmentsBasedOnUserPredicate(filter));
-      // TODO this should be done in parallel since it can take quite a while, moreover the contract
-      // for any destroy node action should probably be that the pool has at least minSize nodes
-      // before it returns. need to think it through a bit better.
-      for (Map.Entry<String, NodeMetadata> poolNode : poolNodesToUnassign) {
-         unassignNode(poolNode.getValue().getId());
-      }
-      return Sets.newHashSet(transform(poolNodesToUnassign,
-               new Function<Map.Entry<String, NodeMetadata>, NodeMetadata>() {
-                  @Override
-                  public NodeMetadata apply(final Map.Entry<String, NodeMetadata> input) {
-                     assignments.remove(input.getKey(), input.getValue());
-                     return toFrontendNodemetadata(input.getValue(), input.getKey());
-                  }
-               }));
-
-   }
-
-   /**
-    * Adds nodes to the pool, using the pool's group name. Lock the pool so that no-one tries to
-    * increase/decrease until we're finished but we'll return from the method well before the pool
-    * as enough nodes.
-    */
-   private ListenableFuture<Void> increasePoolSize(final int size) {
-      lock.lock();
-      logger.debug(">> increasing pool size,  available: %s total: %s min; %s max: %s increasing to: %s", available
-               .size(), poolNodes.size(), minSize, maxSize, size);
-      return Futures.makeListenable(executor.submit(new Callable<Void>() {
-         @Override
-         public Void call() throws Exception {
-            try {
-               Set<? extends NodeMetadata> original = backingComputeService.createNodesInGroup(poolGroupName, size,
-                        template);
-               poolNodes.addAll(original);
-               available.addAll(original);
-               logger.debug("<< pool size increased, available: %s total: %s min; %s max: %s increasing to: %s",
-                        available.size(), poolNodes.size(), minSize, maxSize, size);
-               if (started.compareAndSet(false, true)) {
-                  logger.info("pool started, status: %s min; %s max: %s", available.size(), minSize, maxSize);
-               }
-               return null;
-            } finally {
-               lock.unlock();
-            }
-         }
-      }), executor);
-   }
-
-   /**
-    * Unassigns the node with the provided id. If the we're set to reuse the nodes it adds it to the
-    * available pool, if not is destroys the backing node, removes if from the poll and increases
-    * the pool size by one.
-    */
-   private NodeMetadata unassignNode(final String nodeId) {
-      Map.Entry<String, NodeMetadata> entry = findAssigmentEntry(nodeId);
-      assignments.remove(entry.getKey(), entry.getValue());
-      // if we're reusing destroyed simply add to the available nodes
-      if (reuseDestroyed) {
-         available.add(entry.getValue());
-         return entry.getValue();
-      }
-      // if not we need to destroy the backing node
-      lock.lock();
-      try {
-         backingComputeService.destroyNode(nodeId);
-         removeIf(poolNodes, new Predicate<NodeMetadata>() {
-            @Override
-            public boolean apply(NodeMetadata input) {
-               return input.getId().equals(nodeId);
-            }
-         });
-         if (poolNodes.size() < minSize) {
-            increasePoolSize(1);
-         }
-      } finally {
-         lock.unlock();
-      }
-      return entry.getValue();
-   }
-
-   /**
-    * Used to assign size pool nodes to a group. If not enough nodes are available we check if we
-    * can increase the pool if that is enough, otherwise we complain.
-    */
-   private Set<? extends NodeMetadata> assignPoolNodes(String groupName, int size) throws InterruptedException,
-            ExecutionException {
-      if (available.size() < size) {
-         if (poolNodes.size() + size > maxSize) {
-            // TODO think of a better exception
-            throw new IllegalStateException(
-                     "not enough nodes available  and cannot add enough nodes to pool [available: " + available.size()
-                              + " total: " + poolNodes.size() + " min: " + minSize + " max: " + maxSize
-                              + " requested: " + size + "]");
-         }
-         increasePoolSize(size - available.size()).get();
-      }
-      Set<NodeMetadata> groupNodes = Sets.newHashSet();
-      Iterator<NodeMetadata> iter = available.iterator();
-      for (int i = 0; i < size && iter.hasNext(); i++) {
-         NodeMetadata node = iter.next();
-         assignments.put(groupName, node);
-         iter.remove();
-         groupNodes.add(toFrontendNodemetadata(node, groupName));
-      }
-      return groupNodes;
-   }
-
-   @Override
-   public ListenableFuture<Void> startPool() {
-      return increasePoolSize(minSize);
-   }
-
-   @Override
-   public void close() {
-      // lock just to make sure we have the correct pool size
-      if (started.compareAndSet(true, false)) {
-         logger.info("Closing pooled compute service with {} nodes", size());
-         available.clear();
-         assignments.clear();
-         backingComputeService.destroyNodesMatching(NodePredicates.inGroup(poolGroupName));
-      }
-   }
-
-   @Override
-   public int ready() {
-      return available.size();
-   }
-
-   @Override
-   public int size() {
-      return poolNodes.size();
-   }
-
-   @Override
-   public int maxSize() {
-      return maxSize;
-   }
-
-}
diff --git a/labs/nodepool/src/main/java/org/jclouds/nodepool/internal/EagerNodePoolComputeServiceAdapter.java b/labs/nodepool/src/main/java/org/jclouds/nodepool/internal/EagerNodePoolComputeServiceAdapter.java
new file mode 100644
index 0000000..b2bc01d
--- /dev/null
+++ b/labs/nodepool/src/main/java/org/jclouds/nodepool/internal/EagerNodePoolComputeServiceAdapter.java
@@ -0,0 +1,141 @@
+/*
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.jclouds.nodepool.internal;
+
+import static com.google.common.base.Preconditions.checkState;
+import static org.jclouds.nodepool.config.NodePoolProperties.BACKEND_GROUP;
+import static org.jclouds.nodepool.config.NodePoolProperties.MAX_SIZE;
+import static org.jclouds.nodepool.config.NodePoolProperties.MIN_SIZE;
+import static org.jclouds.nodepool.config.NodePoolProperties.REMOVE_DESTROYED;
+
+import java.util.Set;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.Resource;
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import org.jclouds.compute.ComputeService;
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.domain.Template;
+import org.jclouds.compute.reference.ComputeServiceConstants;
+import org.jclouds.logging.Logger;
+import org.jclouds.nodepool.Backend;
+
+import com.google.common.base.Supplier;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Sets;
+import com.google.common.collect.Sets.SetView;
+
+/**
+ * An eager {@link NodePoolComputeService}. Eagerly builds and maintains a pool
+ * of nodes. It's only "started" after min nodes are allocated and available.
+ * 
+ * @author David Alves
+ * 
+ */
+@Singleton
+public class EagerNodePoolComputeServiceAdapter extends BaseNodePoolComputeServiceAdapter {
+
+   @Resource
+   @Named(ComputeServiceConstants.COMPUTE_LOGGER)
+   protected Logger logger = Logger.NULL;
+
+   private final int maxSize;
+   private final int minSize;
+   private final boolean reuseDestroyed;
+
+   @Inject
+   public EagerNodePoolComputeServiceAdapter(@Backend Supplier<ComputeService> backendComputeService,
+         @Backend Supplier<Template> backendTemplate, @Named(BACKEND_GROUP) String poolGroupPrefix,
+         @Named(MAX_SIZE) int maxSize, @Named(MIN_SIZE) int minSize, @Named(REMOVE_DESTROYED) boolean readdDestroyed,
+         NodeMetadataStore storage) {
+      super(backendComputeService, backendTemplate, poolGroupPrefix, storage);
+      this.maxSize = maxSize;
+      this.minSize = minSize;
+      this.reuseDestroyed = readdDestroyed;
+   }
+
+   @PostConstruct
+   public void startEagerPool() {
+      Set<? extends NodeMetadata> backendNodes = getBackendNodes();
+      if (backendNodes.size() < minSize) {
+         addToPool(backendNodes.size() - minSize);
+      }
+   }
+
+   @Override
+   public NodeWithInitialCredentials createNodeWithGroupEncodedIntoName(String group, String name, Template template) {
+      int count = 1;
+      synchronized (this) {
+         Set<NodeMetadata> backendNodes = getBackendNodes();
+         Set<NodeMetadata> frontendNodes = metadataStore.loadAll(backendNodes);
+
+         checkState(frontendNodes.size() + count < maxSize,
+               "cannot add more nodes to pool [requested: %s, current: %s, max: %s]", count, frontendNodes.size(),
+               maxSize);
+
+         SetView<NodeMetadata> availableNodes = Sets.difference(backendNodes, frontendNodes);
+
+         NodeMetadata node = metadataStore.store(Iterables.get(availableNodes, 0), template.getOptions(), group);
+         return new NodeWithInitialCredentials(node);
+      }
+   }
+
+   @Override
+   public synchronized void destroyNode(String id) {
+      checkState(getNode(id) != null);
+      metadataStore.deleteMapping(id);
+
+      if (!reuseDestroyed) {
+         backendComputeService.get().destroyNode(id);
+         addToPool(1);
+      }
+   }
+
+   @Override
+   public int currentSize() {
+      return getBackendNodes().size();
+   }
+
+   @Override
+   public int idleNodes() {
+      Set<NodeMetadata> backendNodes = getBackendNodes();
+      Set<NodeMetadata> frontendNodes = metadataStore.loadAll(backendNodes);
+      return backendNodes.size() - frontendNodes.size();
+   }
+
+   @Override
+   public int maxNodes() {
+      return maxSize;
+   }
+
+   @Override
+   public int minNodes() {
+      return minSize;
+   }
+
+   @Override
+   public int usedNodes() {
+      return metadataStore.loadAll(getBackendNodes()).size();
+   }
+
+}
diff --git a/labs/nodepool/src/main/java/org/jclouds/nodepool/internal/JsonNodeMetadataStore.java b/labs/nodepool/src/main/java/org/jclouds/nodepool/internal/JsonNodeMetadataStore.java
new file mode 100644
index 0000000..80716d7
--- /dev/null
+++ b/labs/nodepool/src/main/java/org/jclouds/nodepool/internal/JsonNodeMetadataStore.java
@@ -0,0 +1,116 @@
+package org.jclouds.nodepool.internal;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Map;
+import java.util.Set;
+
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.domain.NodeMetadataBuilder;
+import org.jclouds.compute.options.TemplateOptions;
+import org.jclouds.domain.LoginCredentials;
+import org.jclouds.json.Json;
+import org.jclouds.util.Strings2;
+
+import com.google.common.base.Function;
+import com.google.common.base.Supplier;
+import com.google.common.base.Throwables;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
+/**
+ * An implementation of {@link NodeMetadataStore} that stores all that is needed by building a json
+ * string.
+ * 
+ * @author David Alves
+ * 
+ */
+@Singleton
+public class JsonNodeMetadataStore implements NodeMetadataStore {
+
+   private final Supplier<Map<String, InputStream>> storage;
+   private final Json json;
+
+   private static class JsonUserNodeMetadata {
+      private String userGroup;
+      private Set<String> userTags;
+      private Map<String, String> userMetadata;
+      private String user;
+      private String password;
+      private String privateKey;
+      private Boolean authenticateSudo;
+   }
+
+   @Inject
+   public JsonNodeMetadataStore(Supplier<Map<String, InputStream>> storage, Json json) {
+      this.storage = storage;
+      this.json = json;
+   }
+
+   @Override
+   public NodeMetadata store(NodeMetadata backendNodeMetadata, TemplateOptions userOptions, String userGroup) {
+      checkNotNull(backendNodeMetadata);
+      checkNotNull(userGroup);
+      checkNotNull(userOptions);
+      JsonUserNodeMetadata jsonMetadata = new JsonUserNodeMetadata();
+      jsonMetadata.user = userOptions.getLoginUser();
+      jsonMetadata.password = userOptions.getLoginPassword();
+      jsonMetadata.privateKey = userOptions.getLoginPrivateKey();
+      jsonMetadata.authenticateSudo = userOptions.shouldAuthenticateSudo();
+      jsonMetadata.userMetadata = userOptions.getUserMetadata();
+      jsonMetadata.userTags = userOptions.getTags();
+      jsonMetadata.userGroup = userGroup;
+      storage.get().put(backendNodeMetadata.getId(), Strings2.toInputStream(json.toJson(jsonMetadata)));
+      return buildFromJsonAndBackendMetadata(backendNodeMetadata, jsonMetadata);
+   }
+
+   @Override
+   public NodeMetadata load(NodeMetadata backendNodeMetadata) {
+      try {
+         InputStream storedMetadata = storage.get().get(checkNotNull(backendNodeMetadata).getId());
+         if (storedMetadata == null) {
+            return null;
+         }
+         String jsonMetadataAsString = Strings2.toStringAndClose(storedMetadata);
+         JsonUserNodeMetadata jsonMetadata = json.fromJson(jsonMetadataAsString, JsonUserNodeMetadata.class);
+         return buildFromJsonAndBackendMetadata(backendNodeMetadata, jsonMetadata);
+      } catch (IOException e) {
+         throw Throwables.propagate(e);
+      }
+   }
+
+   private NodeMetadata buildFromJsonAndBackendMetadata(NodeMetadata backendNodeMetadata,
+            JsonUserNodeMetadata jsonMetadata) {
+      return NodeMetadataBuilder
+               .fromNodeMetadata(backendNodeMetadata)
+               .tags(jsonMetadata.userTags)
+               .group(jsonMetadata.userGroup)
+               .userMetadata(jsonMetadata.userMetadata)
+               .credentials(
+                        new LoginCredentials(jsonMetadata.user, jsonMetadata.password, jsonMetadata.privateKey,
+                                 jsonMetadata.authenticateSudo)).build();
+   }
+
+   @Override
+   public void deleteAllMappings() {
+      storage.get().clear();
+   }
+
+   @Override
+   public void deleteMapping(String backendNodeId) {
+      storage.get().remove(backendNodeId);
+   }
+
+   public Set<NodeMetadata> loadAll(Set<NodeMetadata> backendNodes) {
+      return ImmutableSet.copyOf(Iterables.transform(backendNodes, new Function<NodeMetadata, NodeMetadata>() {
+         @Override
+         public NodeMetadata apply(NodeMetadata input) {
+            return load(input);
+         }
+      }));
+   }
+}
diff --git a/labs/nodepool/src/main/java/org/jclouds/nodepool/internal/NodeMetadataStore.java b/labs/nodepool/src/main/java/org/jclouds/nodepool/internal/NodeMetadataStore.java
new file mode 100644
index 0000000..364072c
--- /dev/null
+++ b/labs/nodepool/src/main/java/org/jclouds/nodepool/internal/NodeMetadataStore.java
@@ -0,0 +1,80 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.nodepool.internal;
+
+import java.util.Set;
+
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.options.TemplateOptions;
+
+/**
+ * Stores/Loads frontend {@link NodeMetadata} mappings.
+ * 
+ * @author David Alves
+ * 
+ */
+public interface NodeMetadataStore {
+   
+   /**
+    * Associates the provided user options and group with the provided backend {@link NodeMetadata},
+    * then build a frontend version of node metadata that has some fields from the backend node such
+    * as id, name or location, and some fields from the provided userOptions, such as userMetadata
+    * or tags.
+    * 
+    * @param backendNode
+    *           the backend node's {@link NodeMetadata}
+    * @param userOptions
+    *           the user provided options
+    * @param userGroup
+    *           the user selected group
+    * @return a version of NodeMetadata that includes information from the backend node and form the
+    *         user provided options and group.
+    */
+   public NodeMetadata store(NodeMetadata backendNode, TemplateOptions userOptions, String userGroup);
+
+   /**
+    * Removes the mapping from storage.
+    * 
+    * @param backendNodeId
+    */
+   public void deleteMapping(String backendNodeId);
+
+   /**
+    * Clears all mappings.
+    */
+   public void deleteAllMappings();
+
+   /**
+    * Loads the previously stored user {@link NodeMetadata} corresponding to the provided backend
+    * {@link NodeMetadata}.
+    * 
+    * @param backendNode
+    * 
+    * @return the frontend {@link NodeMetadata} or null of this backend node has no mapping
+    */
+   public NodeMetadata load(NodeMetadata backendNode);
+
+   /**
+    * Loads frontend {@link NodeMetadata} for all provided backend nodes.
+    * 
+    * @param backendNodes
+    * @return
+    */
+   public Set<NodeMetadata> loadAll(Set<NodeMetadata> backendNodes);
+}
diff --git a/labs/nodepool/src/main/java/org/jclouds/nodepool/internal/NodeMetadataStoreCache.java b/labs/nodepool/src/main/java/org/jclouds/nodepool/internal/NodeMetadataStoreCache.java
new file mode 100644
index 0000000..99db916
--- /dev/null
+++ b/labs/nodepool/src/main/java/org/jclouds/nodepool/internal/NodeMetadataStoreCache.java
@@ -0,0 +1,70 @@
+package org.jclouds.nodepool.internal;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.options.TemplateOptions;
+
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+
+@Singleton
+public class NodeMetadataStoreCache implements NodeMetadataStore {
+
+   private Map<String, NodeMetadata> frontendMetadataCache = new HashMap<String, NodeMetadata>();
+   private NodeMetadataStore backend;
+
+   @Inject
+   public NodeMetadataStoreCache(NodeMetadataStore backend) {
+      this.backend = backend;
+   }
+
+   @Override
+   public synchronized NodeMetadata store(NodeMetadata backendNode, TemplateOptions userOptions, String userGroup) {
+      NodeMetadata frontEndNode = backend.store(backendNode, userOptions, userGroup);
+      frontendMetadataCache.put(backendNode.getId(), frontEndNode);
+      return frontEndNode;
+   }
+
+   @Override
+   public synchronized void deleteMapping(String backendNodeId) {
+      frontendMetadataCache.remove(backendNodeId);
+      backend.deleteMapping(backendNodeId);
+
+   }
+
+   @Override
+   public synchronized void deleteAllMappings() {
+      frontendMetadataCache.clear();
+      backend.deleteAllMappings();
+   }
+
+   @Override
+   public synchronized NodeMetadata load(NodeMetadata backendNode) {
+      NodeMetadata frontendNode = frontendMetadataCache.get(backendNode.getId());
+      if (frontendNode == null) {
+         frontendNode = backend.load(backendNode);
+         if (frontendNode != null) {
+            frontendMetadataCache.put(backendNode.getId(), frontendNode);
+         }
+      }
+      return frontendNode;
+   }
+
+   @Override
+   public synchronized Set<NodeMetadata> loadAll(Set<NodeMetadata> backendNodes) {
+      return ImmutableSet.copyOf(Iterables.transform(backendNodes, new Function<NodeMetadata, NodeMetadata>() {
+         @Override
+         public NodeMetadata apply(NodeMetadata input) {
+            return load(input);
+         }
+      }));
+   }
+
+}
diff --git a/labs/nodepool/src/test/java/org/jclouds/nodepool/BYONBackendLiveTest.java b/labs/nodepool/src/test/java/org/jclouds/nodepool/BYONBackendLiveTest.java
new file mode 100644
index 0000000..f1e7ff7
--- /dev/null
+++ b/labs/nodepool/src/test/java/org/jclouds/nodepool/BYONBackendLiveTest.java
@@ -0,0 +1,104 @@
+/*
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.nodepool;
+
+import static org.jclouds.compute.options.RunScriptOptions.Builder.wrapInInitScript;
+import static org.jclouds.nodepool.config.NodePoolProperties.BACKEND_PROVIDER;
+import static org.jclouds.nodepool.config.NodePoolProperties.BASEDIR;
+import static org.jclouds.nodepool.config.NodePoolProperties.MAX_SIZE;
+import static org.jclouds.nodepool.config.NodePoolProperties.MIN_SIZE;
+import static org.jclouds.scriptbuilder.domain.Statements.exec;
+
+import java.io.File;
+import java.util.Properties;
+import java.util.Set;
+
+import org.jclouds.compute.domain.ExecResponse;
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.internal.BaseComputeServiceContextLiveTest;
+import org.jclouds.scriptbuilder.domain.OsFamily;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.Iterables;
+
+/**
+ * 
+ * @author Adrian Cole
+ */
+@Test(groups = "live", testName ="BYONBackendLiveTest")
+public class BYONBackendLiveTest extends BaseComputeServiceContextLiveTest {
+
+   final String basedir = "target/" + this.getClass().getSimpleName();
+
+   public BYONBackendLiveTest() {
+      provider = "nodepool";
+   }
+   
+   @Override
+   protected Properties setupProperties() {
+
+      Properties contextProperties = super.setupProperties();
+      
+      contextProperties.setProperty(BACKEND_PROVIDER, "byon");
+      contextProperties.setProperty(BASEDIR, basedir);
+      contextProperties.setProperty(MAX_SIZE, 1 + "");
+      contextProperties.setProperty(MIN_SIZE, 1 + "");
+      
+      StringBuilder nodes = new StringBuilder();
+      nodes.append("nodes:\n");
+      nodes.append("    - id: mymachine\n");
+      nodes.append("      location_id: localhost\n");
+      nodes.append("      name: my local machine\n");
+      nodes.append("      hostname: localhost\n");
+      nodes.append("      os_arch: ").append(System.getProperty("os.arch")).append("\n");
+      nodes.append("      os_family: ").append(OsFamily.UNIX).append("\n");
+      nodes.append("      os_description: ").append(System.getProperty("os.name")).append("\n");
+      nodes.append("      os_version: ").append(System.getProperty("os.version")).append("\n");
+      nodes.append("      group: ").append("ssh").append("\n");
+      nodes.append("      tags:\n");
+      nodes.append("          - local\n");
+      nodes.append("      username: ").append(System.getProperty("user.name")).append("\n");
+      nodes.append("      credential_url: file://").append(System.getProperty("user.home")).append("/.ssh/id_rsa")
+               .append("\n");
+
+      contextProperties.setProperty("byon.nodes", nodes.toString());
+      contextProperties.setProperty("byon.template", "locationId=localhost");
+      return contextProperties;
+   }
+
+   public void testCanRunCommandAsCurrentUser() throws Exception {
+      Set<? extends NodeMetadata> nodes = view.getComputeService().createNodesInGroup("goo", 1);
+      NodeMetadata node = Iterables.get(nodes, 0);
+
+      try {
+         ExecResponse response = view.getComputeService().runScriptOnNode(node.getId(), exec("id"),
+               wrapInInitScript(false).runAsRoot(false));
+
+         assert response.getOutput().trim().contains(System.getProperty("user.name")) : node + ": " + response;
+      } finally {
+         view.getComputeService().destroyNode(node.getId());
+      }
+   }
+   
+   @Override
+   protected void tearDownContext() {
+      super.tearDownContext();
+      new File(basedir).delete();
+   }
+}
diff --git a/labs/nodepool/src/test/java/org/jclouds/nodepool/NodePoolComputeServiceContextTest.java b/labs/nodepool/src/test/java/org/jclouds/nodepool/NodePoolComputeServiceContextTest.java
new file mode 100644
index 0000000..2e24430
--- /dev/null
+++ b/labs/nodepool/src/test/java/org/jclouds/nodepool/NodePoolComputeServiceContextTest.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.nodepool;
+
+import static org.testng.Assert.assertEquals;
+
+import java.io.File;
+import java.util.Properties;
+
+import org.jclouds.ContextBuilder;
+import org.jclouds.compute.ComputeService;
+import org.jclouds.logging.slf4j.config.SLF4JLoggingModule;
+import org.jclouds.nodepool.Backend;
+import org.jclouds.nodepool.config.NodePoolProperties;
+import org.jclouds.rest.annotations.Credential;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Supplier;
+import com.google.inject.Key;
+import com.google.inject.TypeLiteral;
+
+/**
+ * 
+ */
+@Test(groups = "unit", testName = "NodePoolComputeServiceContextTest")
+public class NodePoolComputeServiceContextTest {
+
+
+   @Test
+   public void testBinds() {
+      final String basedir = "target/" + this.getClass().getSimpleName();
+      new File(basedir).delete();
+      
+      Properties overrides = new Properties();
+      overrides.setProperty(NodePoolProperties.BACKEND_PROVIDER, "stub");
+      overrides.setProperty(NodePoolProperties.BASEDIR, basedir);
+      // note no ssh module since we are stub and not trying ssh, yet
+      overrides.setProperty(NodePoolProperties.BACKEND_MODULES, SLF4JLoggingModule.class.getName());
+      
+      ComputeService stub = ContextBuilder.newBuilder("nodepool")
+                                          .credentials("foo", "bar")
+                                          .endpoint("gooend")
+                                          .apiVersion("1.1")
+                                          .buildVersion("1.1-2")
+                                          .overrides(overrides)
+                                          .buildInjector().getInstance(Key.get(new TypeLiteral<Supplier<ComputeService>>(){}, Backend.class)).get();
+      
+      assertEquals(stub.getContext().unwrap().getIdentity(), "foo");
+      assertEquals(stub.getContext().utils().injector().getInstance(Key.get(String.class, Credential.class)), "bar");
+      assertEquals(stub.getContext().unwrap().getProviderMetadata().getEndpoint(), "gooend");
+      assertEquals(stub.getContext().unwrap().getProviderMetadata().getApiMetadata().getVersion(), "1.1");
+      assertEquals(stub.getContext().unwrap().getProviderMetadata().getApiMetadata().getBuildVersion().get(), "1.1-2");
+
+      stub.getContext().close();
+      new File(basedir).delete();
+
+   }
+
+
+}
\ No newline at end of file
diff --git a/labs/nodepool/src/test/java/org/jclouds/nodepool/NodePoolComputeServiceTest.java b/labs/nodepool/src/test/java/org/jclouds/nodepool/NodePoolComputeServiceTest.java
deleted file mode 100644
index 2ac202a..0000000
--- a/labs/nodepool/src/test/java/org/jclouds/nodepool/NodePoolComputeServiceTest.java
+++ /dev/null
@@ -1,72 +0,0 @@
-package org.jclouds.nodepool;
-
-import static org.testng.Assert.assertEquals;
-
-import java.io.IOException;
-import java.util.concurrent.ExecutionException;
-
-import org.jclouds.ContextBuilder;
-import org.jclouds.compute.ComputeServiceContext;
-import org.jclouds.compute.RunNodesException;
-import org.jclouds.compute.predicates.NodePredicates;
-import org.jclouds.nodepool.internal.EagerNodePoolComputeService;
-import org.testng.annotations.BeforeClass;
-import org.testng.annotations.Test;
-
-import com.google.common.util.concurrent.ListenableFuture;
-
-@Test(groups = "unit", testName = "NodePoolComputeServiceTest")
-public class NodePoolComputeServiceTest {
-
-   private EagerNodePoolComputeService pooledComputeService;
-
-   @BeforeClass
-   public void setUp() {
-      ComputeServiceContext stubCtx = ContextBuilder.newBuilder("stub").buildView(ComputeServiceContext.class);
-      this.pooledComputeService = new EagerNodePoolComputeService(stubCtx, "pool", 10, 5, true, stubCtx
-               .getComputeService().templateBuilder().build(), stubCtx.utils().getUserExecutor());
-   }
-
-   public void testStartPool() throws InterruptedException, ExecutionException {
-      ListenableFuture<Void> future = this.pooledComputeService.startPool();
-      future.get();
-      assertEquals(pooledComputeService.ready(), 5);
-      assertEquals(pooledComputeService.size(), 5);
-      assertEquals(pooledComputeService.maxSize(), 10);
-   }
-
-   @Test(dependsOnMethods = "testStartPool", groups = { "unit", "poolStarted" })
-   public void testAllocateMinNodes() throws RunNodesException {
-      this.pooledComputeService.createNodesInGroup("1", 5);
-      // this pool is not supposed to add nodes past min until we request them
-      assertEquals(pooledComputeService.ready(), 0);
-      assertEquals(pooledComputeService.size(), 5);
-   }
-
-   @Test(dependsOnMethods = "testAllocateMinNodes", groups = { "unit", "poolStarted" })
-   public void testAllocateUpToMaxNodes() throws RunNodesException {
-      this.pooledComputeService.createNodesInGroup("2", 5);
-      assertEquals(pooledComputeService.ready(), 0);
-      assertEquals(pooledComputeService.size(), 10);
-   }
-
-   @Test(dependsOnMethods = "testAllocateUpToMaxNodes", groups = { "unit", "poolStarted" }, expectedExceptions = RunNodesException.class)
-   public void testAllocateMoreNodesFails() throws RunNodesException {
-      this.pooledComputeService.createNodesInGroup("3", 5);
-      System.out.println(this.pooledComputeService.size());
-   }
-
-   @Test(dependsOnMethods = "testAllocateUpToMaxNodes", groups = { "unit", "poolStarted" })
-   public void testDeallocatingNodesAndReallocating() throws RunNodesException {
-      this.pooledComputeService.destroyNodesMatching(NodePredicates.inGroup("2"));
-      assertEquals(pooledComputeService.ready(), 5);
-      this.pooledComputeService.createNodesInGroup("2", 5);
-   }
-
-   @Test(dependsOnGroups = "poolStarted")
-   public void testClose() throws IOException {
-      this.pooledComputeService.close();
-      assertEquals(0, pooledComputeService.ready());
-   }
-
-}
diff --git a/labs/nodepool/src/test/java/org/jclouds/nodepool/config/BindBackendComputeServiceTest.java b/labs/nodepool/src/test/java/org/jclouds/nodepool/config/BindBackendComputeServiceTest.java
new file mode 100644
index 0000000..0565c2f
--- /dev/null
+++ b/labs/nodepool/src/test/java/org/jclouds/nodepool/config/BindBackendComputeServiceTest.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.nodepool.config;
+
+import static org.testng.Assert.assertEquals;
+
+import java.io.File;
+import java.util.Properties;
+
+import org.jclouds.ContextBuilder;
+import org.jclouds.compute.ComputeService;
+import org.jclouds.logging.slf4j.config.SLF4JLoggingModule;
+import org.jclouds.nodepool.Backend;
+import org.jclouds.rest.annotations.Credential;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Supplier;
+import com.google.inject.Key;
+import com.google.inject.TypeLiteral;
+
+/**
+ * 
+ */
+@Test(groups = "unit", testName = "BindBackendComputeServiceTest")
+public class BindBackendComputeServiceTest {
+
+
+   @Test
+   public void testBinds() {
+      final String basedir = "target/" + this.getClass().getSimpleName();
+      new File(basedir).delete();
+      
+      Properties overrides = new Properties();
+      overrides.setProperty(NodePoolProperties.BACKEND_PROVIDER, "stub");
+      overrides.setProperty(NodePoolProperties.BASEDIR, basedir);
+      // note no ssh module since we are stub and not trying ssh, yet
+      overrides.setProperty(NodePoolProperties.BACKEND_MODULES, SLF4JLoggingModule.class.getName());
+
+      ComputeService stub = ContextBuilder.newBuilder("nodepool")
+                                          .credentials("foo", "bar")
+                                          .endpoint("gooend")
+                                          .apiVersion("1.1")
+                                          .buildVersion("1.1-2")
+                                          .overrides(overrides)
+                                          .buildInjector().getInstance(Key.get(new TypeLiteral<Supplier<ComputeService>>(){}, Backend.class)).get();
+      
+      assertEquals(stub.getContext().unwrap().getIdentity(), "foo");
+      assertEquals(stub.getContext().utils().injector().getInstance(Key.get(String.class, Credential.class)), "bar");
+      assertEquals(stub.getContext().unwrap().getProviderMetadata().getEndpoint(), "gooend");
+      assertEquals(stub.getContext().unwrap().getProviderMetadata().getApiMetadata().getVersion(), "1.1");
+      assertEquals(stub.getContext().unwrap().getProviderMetadata().getApiMetadata().getBuildVersion().get(), "1.1-2");
+
+      stub.getContext().close();
+      new File(basedir).delete();
+
+   }
+
+
+}
\ No newline at end of file
diff --git a/labs/nodepool/src/test/java/org/jclouds/nodepool/config/BindInputStreamToFilesystemBlobStoreTest.java b/labs/nodepool/src/test/java/org/jclouds/nodepool/config/BindInputStreamToFilesystemBlobStoreTest.java
new file mode 100644
index 0000000..b6a0ea3
--- /dev/null
+++ b/labs/nodepool/src/test/java/org/jclouds/nodepool/config/BindInputStreamToFilesystemBlobStoreTest.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.nodepool.config;
+
+import static org.testng.Assert.assertEquals;
+
+import java.io.File;
+import java.io.InputStream;
+import java.util.Map;
+
+import org.jclouds.logging.slf4j.config.SLF4JLoggingModule;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Supplier;
+import com.google.common.collect.ImmutableSet;
+import com.google.inject.AbstractModule;
+import com.google.inject.Guice;
+import com.google.inject.Key;
+import com.google.inject.TypeLiteral;
+import com.google.inject.name.Names;
+
+/**
+ * 
+ */
+@Test(groups = "unit", testName = "BindInputStreamToFilesystemBlobStoreTest")
+public class BindInputStreamToFilesystemBlobStoreTest {
+
+   @Test
+   public void testCreatesDir() {
+      final String basedir = "target/" + this.getClass().getSimpleName();
+      new File(basedir).delete();
+      Map<String, InputStream> file = Guice.createInjector(new AbstractModule() {
+
+         @Override
+         protected void configure() {
+            bindConstant().annotatedWith(Names.named(NodePoolProperties.BASEDIR)).to(basedir);
+            bindConstant().annotatedWith(Names.named(NodePoolProperties.METADATA_CONTAINER)).to("barr");
+            bindConstant().annotatedWith(Names.named(NodePoolProperties.BACKEND_MODULES)).to(
+                  SLF4JLoggingModule.class.getName());
+         }
+
+      }, new BindInputStreamToFilesystemBlobStore()).getInstance(
+            Key.get(new TypeLiteral<Supplier<Map<String, InputStream>>>() {
+            })).get();
+
+      assert (new File(basedir + "/barr").exists());
+      assertEquals(file.keySet(), ImmutableSet.of());
+      new File(basedir).delete();
+   }
+
+}
\ No newline at end of file
diff --git a/labs/nodepool/src/test/resources/logback.xml b/labs/nodepool/src/test/resources/logback.xml
deleted file mode 100644
index fc22c87..0000000
--- a/labs/nodepool/src/test/resources/logback.xml
+++ /dev/null
@@ -1,71 +0,0 @@
-<?xml version="1.0"?>

-<configuration scan="false">

-  <appender name="FILE" class="ch.qos.logback.core.FileAppender">

-    <file>target/test-data/jclouds.log</file>

-

-    <encoder>

-      <Pattern>%d %-5p [%c] [%thread] %m%n</Pattern>

-    </encoder>

-  </appender>

-

-  <appender name="WIREFILE" class="ch.qos.logback.core.FileAppender">

-    <file>target/test-data/jclouds-wire.log</file>

-

-    <encoder>

-      <Pattern>%d %-5p [%c] [%thread] %m%n</Pattern>

-    </encoder>

-  </appender>

-

-  <appender name="COMPUTEFILE" class="ch.qos.logback.core.FileAppender">

-    <file>target/test-data/jclouds-compute.log</file>

-

-    <encoder>

-      <Pattern>%d %-5p [%c] [%thread] %m%n</Pattern>

-    </encoder>

-  </appender>

-

-  <appender name="SSHFILE" class="ch.qos.logback.core.FileAppender">

-    <file>target/test-data/jclouds-ssh.log</file>

-

-    <encoder>

-      <Pattern>%d %-5p [%c] [%thread] %m%n</Pattern>

-    </encoder>

-  </appender>

-

-  <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">

-    <encoder>

-      <Pattern>%-4r [%thread] %-5level - %msg%n</Pattern>

-    </encoder>

-  </appender>

-

-  <root>

-    <level value="DEBUG" />

-    <appender-ref ref="CONSOLE" />

-  </root>

-

-  <logger name="org.jclouds">

-    <level value="DEBUG" />

-    <appender-ref ref="FILE" />

-  </logger>

-

-  <logger name="jclouds.wire">

-    <level value="DEBUG" />

-    <appender-ref ref="WIREFILE" />

-  </logger>

-

-  <logger name="jclouds.headers">

-    <level value="DEBUG" />

-    <appender-ref ref="WIREFILE" />

-  </logger>

-

-  <logger name="jclouds.compute">

-    <level value="DEBUG" />

-    <appender-ref ref="COMPUTEFILE" />

-  </logger>

-

-  <logger name="jclouds.ssh">

-    <level value="DEBUG" />

-    <appender-ref ref="SSHFILE" />

-  </logger>

-

-</configuration>

diff --git a/labs/openstack-glance/src/main/java/org/jclouds/openstack/glance/v1_0/GlanceApiMetadata.java b/labs/openstack-glance/src/main/java/org/jclouds/openstack/glance/v1_0/GlanceApiMetadata.java
index f5a63d0..4ed6123 100644
--- a/labs/openstack-glance/src/main/java/org/jclouds/openstack/glance/v1_0/GlanceApiMetadata.java
+++ b/labs/openstack-glance/src/main/java/org/jclouds/openstack/glance/v1_0/GlanceApiMetadata.java
@@ -27,8 +27,9 @@
 import org.jclouds.apis.ApiMetadata;
 import org.jclouds.openstack.glance.v1_0.config.GlanceRestClientModule;
 import org.jclouds.openstack.keystone.v2_0.config.CredentialTypes;
+import org.jclouds.openstack.keystone.v2_0.config.KeystoneAuthenticationModule;
+import org.jclouds.openstack.keystone.v2_0.config.KeystoneAuthenticationModule.RegionModule;
 import org.jclouds.openstack.keystone.v2_0.config.KeystoneProperties;
-import org.jclouds.openstack.keystone.v2_0.config.KeystoneAuthenticationModule.KeystoneAuthenticationModuleForRegions;
 import org.jclouds.openstack.v2_0.ServiceType;
 import org.jclouds.rest.RestContext;
 import org.jclouds.rest.internal.BaseRestApiMetadata;
@@ -86,7 +87,10 @@
          .version("1.0")
          .defaultEndpoint("http://localhost:5000")
          .defaultProperties(GlanceApiMetadata.defaultProperties())
-         .defaultModules(ImmutableSet.<Class<? extends Module>>of(KeystoneAuthenticationModuleForRegions.class, GlanceRestClientModule.class));
+         .defaultModules(ImmutableSet.<Class<? extends Module>>builder()
+                                     .add(KeystoneAuthenticationModule.class)
+                                     .add(RegionModule.class)
+                                     .add(GlanceRestClientModule.class).build());
       }
       
       @Override
diff --git a/labs/openstack-glance/src/main/java/org/jclouds/openstack/glance/v1_0/config/GlanceRestClientModule.java b/labs/openstack-glance/src/main/java/org/jclouds/openstack/glance/v1_0/config/GlanceRestClientModule.java
index 9dbe6f5..4925694 100644
--- a/labs/openstack-glance/src/main/java/org/jclouds/openstack/glance/v1_0/config/GlanceRestClientModule.java
+++ b/labs/openstack-glance/src/main/java/org/jclouds/openstack/glance/v1_0/config/GlanceRestClientModule.java
@@ -57,7 +57,7 @@
       bind(DateAdapter.class).to(Iso8601DateAdapter.class);
       super.configure();
    }
-
+   
    @Override
    protected void bindErrorHandlers() {
       bind(HttpErrorHandler.class).annotatedWith(Redirection.class).to(GlanceErrorHandler.class);
diff --git a/labs/openstack-glance/src/main/java/org/jclouds/openstack/glance/v1_0/domain/Image.java b/labs/openstack-glance/src/main/java/org/jclouds/openstack/glance/v1_0/domain/Image.java
index 7b1d413..f390b42 100644
--- a/labs/openstack-glance/src/main/java/org/jclouds/openstack/glance/v1_0/domain/Image.java
+++ b/labs/openstack-glance/src/main/java/org/jclouds/openstack/glance/v1_0/domain/Image.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -18,20 +18,30 @@
  */
 package org.jclouds.openstack.glance.v1_0.domain;
 
+import java.beans.ConstructorProperties;
+import java.util.Set;
+
+import javax.inject.Named;
+
+import org.jclouds.javax.annotation.Nullable;
+import org.jclouds.openstack.v2_0.domain.Link;
 import org.jclouds.openstack.v2_0.domain.Resource;
 
 import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
 import com.google.common.base.Optional;
-import com.google.gson.annotations.SerializedName;
 
 /**
  * An image the Glance server knows about
- * 
+ *
  * @author Adrian Cole
  * @see <a href= "http://glance.openstack.org/glanceapi.html" />
  * @see <a href= "https://github.com/openstack/glance/blob/master/glance/api/v1/images.py" />
  */
 public class Image extends Resource {
+
+   /**
+    */
    public static enum Status {
 
       UNRECOGNIZED, ACTIVE, SAVING, QUEUED, KILLED, PENDING_DELETE, DELETED;
@@ -49,7 +59,7 @@
       }
 
    }
-   
+
    public static Builder<?> builder() {
       return new ConcreteBuilder();
    }
@@ -58,16 +68,16 @@
       return new ConcreteBuilder().fromImage(this);
    }
 
-   public static abstract class Builder<T extends Builder<T>> extends Resource.Builder<T> {
-      private Optional<ContainerFormat> containerFormat = Optional.absent();
-      private Optional<DiskFormat> diskFormat = Optional.absent();
-      private Optional<Long> size = Optional.absent();
-      private Optional<String> checksum = Optional.absent();
+   public static abstract class Builder<T extends Builder<T>> extends Resource.Builder<T>  {
+      protected ContainerFormat containerFormat;
+      protected DiskFormat diskFormat;
+      protected Long size;
+      protected String checksum;
 
       /**
        * @see Image#getContainerFormat()
        */
-      public T containerFormat(Optional<ContainerFormat> containerFormat) {
+      public T containerFormat(ContainerFormat containerFormat) {
          this.containerFormat = containerFormat;
          return self();
       }
@@ -75,7 +85,7 @@
       /**
        * @see Image#getDiskFormat()
        */
-      public T diskFormat(Optional<DiskFormat> diskFormat) {
+      public T diskFormat(DiskFormat diskFormat) {
          this.diskFormat = diskFormat;
          return self();
       }
@@ -83,56 +93,30 @@
       /**
        * @see Image#getSize()
        */
-      public T size(Optional<Long> size) {
+      public T size(Long size) {
          this.size = size;
          return self();
       }
 
       /**
-       * @see Image#getSize()
+       * @see Image#getChecksum()
        */
-      public T checksum(Optional<String> checksum) {
+      public T checksum(String checksum) {
          this.checksum = checksum;
          return self();
       }
 
-      /**
-       * @see Image#getContainerFormat()
-       */
-      public T containerFormat(ContainerFormat containerFormat) {
-         return containerFormat(Optional.of(containerFormat));
-      }
-
-      /**
-       * @see Image#getDiskFormat()
-       */
-      public T diskFormat(DiskFormat diskFormat) {
-         return diskFormat(Optional.of(diskFormat));
-      }
-
-      /**
-       * @see Image#getSize()
-       */
-      public T size(long size) {
-         return size(Optional.of(size));
-      }
-
-      /**
-       * @see Image#getSize()
-       */
-      public T checksum(String checksum) {
-         return checksum(Optional.of(checksum));
-      }
-
       public Image build() {
-         return new Image(this);
+         return new Image(id, name, links, containerFormat, diskFormat, size, checksum);
       }
 
       public T fromImage(Image in) {
-         return super.fromResource(in).containerFormat(in.getContainerFormat()).diskFormat(in.getDiskFormat()).size(
-                  in.getSize()).checksum(in.getChecksum());
+         return super.fromResource(in)
+               .containerFormat(in.getContainerFormat().orNull())
+               .diskFormat(in.getDiskFormat().orNull())
+               .size(in.getSize().orNull())
+               .checksum(in.getChecksum().orNull());
       }
-
    }
 
    private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
@@ -142,29 +126,23 @@
       }
    }
 
-   protected Image() {
-      // we want serializers like Gson to work w/o using sun.misc.Unsafe,
-      // prohibited in GAE. This also implies fields are not final.
-      // see http://code.google.com/p/jclouds/issues/detail?id=925
-   }
+   @Named("container_format")
+   private final Optional<ContainerFormat> containerFormat;
+   @Named("disk_format")
+   private final Optional<DiskFormat> diskFormat;
+   private final Optional<Long> size;
+   private final Optional<String> checksum;
 
-   // | container_format | varchar(20) | YES | | NULL | |
-   @SerializedName("container_format")
-   private Optional<ContainerFormat> containerFormat = Optional.absent();
-   // | disk_format | varchar(20) | YES | | NULL | |
-   @SerializedName("disk_format")
-   private Optional<DiskFormat> diskFormat = Optional.absent();
-   // | size | bigint(20) | YES | | NULL | |
-   private Optional<Long> size = Optional.absent();
-   // | checksum | varchar(32) | YES | | NULL | |
-   private Optional<String> checksum = Optional.absent();
-
-   protected Image(Builder<?> builder) {
-      super(builder);
-      this.containerFormat = builder.containerFormat;
-      this.diskFormat = builder.diskFormat;
-      this.size = builder.size;
-      this.checksum = builder.checksum;
+   @ConstructorProperties({
+         "id", "name", "links", "container_format", "disk_format", "size", "checksum"
+   })
+   protected Image(String id, @Nullable String name, Set<Link> links, @Nullable ContainerFormat containerFormat,
+                   @Nullable DiskFormat diskFormat, @Nullable Long size, @Nullable String checksum) {
+      super(id, name, links);
+      this.containerFormat = Optional.fromNullable(containerFormat);
+      this.diskFormat = Optional.fromNullable(diskFormat);
+      this.size = Optional.fromNullable(size);
+      this.checksum = Optional.fromNullable(checksum);
    }
 
    public Optional<ContainerFormat> getContainerFormat() {
@@ -180,12 +158,28 @@
    }
 
    public Optional<String> getChecksum() {
-      return checksum;
+      return this.checksum;
    }
 
    @Override
-   protected Objects.ToStringHelper string() {
-      return super.string().add("containerFormat", containerFormat).add("diskFormat", diskFormat).add("size", size)
-               .add("checksum", checksum);
+   public int hashCode() {
+      return Objects.hashCode(containerFormat, diskFormat, size, checksum);
    }
-}
\ No newline at end of file
+
+   @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      Image that = Image.class.cast(obj);
+      return super.equals(that) && Objects.equal(this.containerFormat, that.containerFormat)
+            && Objects.equal(this.diskFormat, that.diskFormat)
+            && Objects.equal(this.size, that.size)
+            && Objects.equal(this.checksum, that.checksum);
+   }
+
+   protected ToStringHelper string() {
+      return super.string()
+            .add("containerFormat", containerFormat).add("diskFormat", diskFormat).add("size", size).add("checksum", checksum);
+   }
+
+}
diff --git a/labs/openstack-glance/src/main/java/org/jclouds/openstack/glance/v1_0/domain/ImageDetails.java b/labs/openstack-glance/src/main/java/org/jclouds/openstack/glance/v1_0/domain/ImageDetails.java
index 343143c..4f440c9 100644
--- a/labs/openstack-glance/src/main/java/org/jclouds/openstack/glance/v1_0/domain/ImageDetails.java
+++ b/labs/openstack-glance/src/main/java/org/jclouds/openstack/glance/v1_0/domain/ImageDetails.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -20,19 +20,24 @@
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import java.beans.ConstructorProperties;
 import java.util.Date;
 import java.util.Map;
+import java.util.Set;
 
-import com.google.common.base.Optional;
-import com.google.common.base.Predicates;
+import javax.inject.Named;
+
+import org.jclouds.javax.annotation.Nullable;
+import org.jclouds.openstack.v2_0.domain.Link;
+
+import com.google.common.base.Objects;
 import com.google.common.base.Objects.ToStringHelper;
+import com.google.common.base.Optional;
 import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Maps;
-import com.google.gson.annotations.SerializedName;
 
 /**
  * Detailed listing of an Image
- * 
+ *
  * @author Adrian Cole
  * @see <a href= "http://glance.openstack.org/glanceapi.html" />
  * @see <a href= "https://github.com/openstack/glance/blob/master/glance/api/v1/images.py" />
@@ -48,17 +53,17 @@
       return new ConcreteBuilder().fromImageDetails(this);
    }
 
-   public static abstract class Builder<T extends Builder<T>> extends Image.Builder<T> {
-      private long minDisk;
-      private long minRam;
-      private Optional<String> location = Optional.absent();
-      private Optional<String> owner = Optional.absent();
-      private Date updatedAt;
-      private Date createdAt;
-      private Optional<Date> deletedAt = Optional.absent();
-      private Status status = Status.UNRECOGNIZED;
-      private boolean isPublic;
-      private Map<String, String> properties = ImmutableMap.of();
+   public static abstract class Builder<T extends Builder<T>> extends Image.Builder<T>  {
+      protected long minDisk;
+      protected long minRam;
+      protected String location;
+      protected String owner;
+      protected Date updatedAt;
+      protected Date createdAt;
+      protected Date deletedAt;
+      protected Image.Status status;
+      protected boolean isPublic;
+      protected Map<String, String> properties = ImmutableMap.of();
 
       /**
        * @see ImageDetails#getMinDisk()
@@ -79,23 +84,8 @@
       /**
        * @see ImageDetails#getLocation()
        */
-      public T location(Optional<String> location) {
-         this.location = location;
-         return self();
-      }
-
-      /**
-       * @see ImageDetails#getLocation()
-       */
       public T location(String location) {
-         return location(Optional.of(location));
-      }
-
-      /**
-       * @see ImageDetails#getOwner()
-       */
-      public T owner(Optional<String> owner) {
-         this.owner = owner;
+         this.location = location;
          return self();
       }
 
@@ -103,7 +93,8 @@
        * @see ImageDetails#getOwner()
        */
       public T owner(String owner) {
-         return owner(Optional.of(owner));
+         this.owner = owner;
+         return self();
       }
 
       /**
@@ -125,22 +116,15 @@
       /**
        * @see ImageDetails#getDeletedAt()
        */
-      public T deletedAt(Optional<Date> deletedAt) {
+      public T deletedAt(Date deletedAt) {
          this.deletedAt = deletedAt;
          return self();
       }
 
       /**
-       * @see ImageDetails#getDeletedAt()
-       */
-      public T deletedAt(Date deletedAt) {
-         return deletedAt(Optional.of(deletedAt));
-      }
-
-      /**
        * @see ImageDetails#getStatus()
        */
-      public T status(Status status) {
+      public T status(Image.Status status) {
          this.status = status;
          return self();
       }
@@ -157,18 +141,26 @@
        * @see ImageDetails#getProperties()
        */
       public T properties(Map<String, String> properties) {
-         this.properties = properties;
+         this.properties = ImmutableMap.copyOf(checkNotNull(properties, "properties"));
          return self();
       }
 
       public ImageDetails build() {
-         return new ImageDetails(this);
+         return new ImageDetails(id, name, links, containerFormat, diskFormat, size, checksum, minDisk, minRam, location, owner, updatedAt, createdAt, deletedAt, status, isPublic, properties);
       }
 
       public T fromImageDetails(ImageDetails in) {
-         return super.fromImage(in).minDisk(in.getMinDisk()).minRam(in.getMinRam()).location(in.getLocation())
-                  .updatedAt(in.getUpdatedAt()).createdAt(in.getCreatedAt()).deletedAt(in.getDeletedAt()).status(
-                           in.getStatus()).isPublic(in.isPublic()).properties(in.getProperties());
+         return super.fromImage(in)
+               .minDisk(in.getMinDisk())
+               .minRam(in.getMinRam())
+               .location(in.getLocation().orNull())
+               .owner(in.getOwner().orNull())
+               .updatedAt(in.getUpdatedAt())
+               .createdAt(in.getCreatedAt())
+               .deletedAt(in.getDeletedAt().orNull())
+               .status(in.getStatus())
+               .isPublic(in.isPublic())
+               .properties(in.getProperties());
       }
    }
 
@@ -179,49 +171,42 @@
       }
    }
 
-   protected ImageDetails() {
-      // we want serializers like Gson to work w/o using sun.misc.Unsafe,
-      // prohibited in GAE. This also implies fields are not final.
-      // see http://code.google.com/p/jclouds/issues/detail?id=925
-   }
+   @Named("min_disk")
+   private final long minDisk;
+   @Named("min_ram")
+   private final long minRam;
+   private final Optional<String> location;
+   private final Optional<String> owner;
+   @Named("updated_at")
+   private final Date updatedAt;
+   @Named("created_at")
+   private final Date createdAt;
+   @Named("deleted_at")
+   private final Optional<Date> deletedAt;
+   private final Image.Status status;
+   @Named("is_public")
+   private final boolean isPublic;
+   private final Map<String, String> properties;
 
-   // | min_disk | int(11) | YES | | NULL | |
-   @SerializedName("min_disk")
-   private long minDisk;
-   // | min_ram | int(11) | YES | | NULL | |
-   @SerializedName("min_ram")
-   private long minRam;
-   // | location | text | YES | | NULL | |
-   private Optional<String> location = Optional.absent();
-   // | owner | varchar(255) | YES | | NULL | |
-   private Optional<String> owner = Optional.absent();
-   // | updated_at | datetime | YES | | NULL | |
-   @SerializedName("updated_at")
-   private Date updatedAt;
-   // | created_at | datetime | NO | | NULL | |
-   @SerializedName("created_at")
-   private Date createdAt;
-   @SerializedName("deleted_at")
-   private Optional<Date> deletedAt = Optional.absent();
-   // | status | varchar(30) | NO | | NULL | |
-   private Status status = Status.UNRECOGNIZED;
-   // | is_public | tinyint(1) | NO | | NULL | |
-   @SerializedName("is_public")
-   private boolean isPublic;
-   private Map<String, String> properties = ImmutableMap.of();
-
-   protected ImageDetails(Builder<?> builder) {
-      super(builder);
-      this.minDisk = builder.minDisk;
-      this.minRam = checkNotNull(builder.minRam, "minRam");
-      this.location = checkNotNull(builder.location, "location");
-      this.owner = checkNotNull(builder.owner, "owner");
-      this.updatedAt = checkNotNull(builder.updatedAt, "updatedAt");
-      this.createdAt = checkNotNull(builder.createdAt, "createdAt");
-      this.deletedAt = checkNotNull(builder.deletedAt, "deletedAt");
-      this.status = checkNotNull(builder.status, "status");
-      this.isPublic = checkNotNull(builder.isPublic, "isPublic");
-      this.properties = ImmutableMap.copyOf(builder.properties);
+   @ConstructorProperties({
+         "id", "name", "links", "container_format", "disk_format", "size", "checksum", "min_disk", "min_ram", "location", "owner", "updated_at", "created_at", "deleted_at", "status", "is_public", "properties"
+   })
+   protected ImageDetails(String id, @Nullable String name, Set<Link> links, @Nullable ContainerFormat containerFormat,
+                          @Nullable DiskFormat diskFormat, @Nullable Long size, @Nullable String checksum, long minDisk,
+                          long minRam, @Nullable String location, @Nullable String owner, Date updatedAt, 
+                          Date createdAt, @Nullable Date deletedAt, Image.Status status, boolean isPublic, 
+                          Map<String, String> properties) {
+      super(id, name, links, containerFormat, diskFormat, size, checksum);
+      this.minDisk = minDisk;
+      this.minRam = minRam;
+      this.location = Optional.fromNullable(location);
+      this.owner = Optional.fromNullable(owner);
+      this.updatedAt = checkNotNull(updatedAt, "updatedAt");
+      this.createdAt = checkNotNull(createdAt, "createdAt");
+      this.deletedAt = Optional.fromNullable(deletedAt);
+      this.status = checkNotNull(status, "status");
+      this.isPublic = isPublic;
+      this.properties = ImmutableMap.copyOf(checkNotNull(properties, "properties"));
    }
 
    /**
@@ -238,18 +223,14 @@
       return this.minRam;
    }
 
-   public Status getStatus() {
-      return this.status;
-   }
-
    public Optional<String> getLocation() {
       return this.location;
    }
 
    public Optional<String> getOwner() {
-      return owner;
+      return this.owner;
    }
-   
+
    public Date getUpdatedAt() {
       return this.updatedAt;
    }
@@ -262,22 +243,43 @@
       return this.deletedAt;
    }
 
+   public Image.Status getStatus() {
+      return this.status;
+   }
+
    public boolean isPublic() {
       return this.isPublic;
    }
 
    public Map<String, String> getProperties() {
-      // in case this was assigned in gson
-      return ImmutableMap.copyOf(Maps.filterValues(this.properties, Predicates.notNull()));
+      return this.properties;
    }
 
-   // hashCode/equals from super is ok
-
    @Override
-   protected ToStringHelper string() {
-      return super.string().add("minDisk", minDisk).add("minRam", minRam).add("location", location).add("deletedAt",
-               getDeletedAt()).add("updatedAt", updatedAt).add("createdAt", createdAt).add("status", status).add(
-               "location", location).add("owner", owner).add("isPublic", isPublic).add("properties", properties);
+   public int hashCode() {
+      return Objects.hashCode(minDisk, minRam, location, owner, updatedAt, createdAt, deletedAt, status, isPublic, properties);
    }
 
-}
\ No newline at end of file
+   @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      ImageDetails that = ImageDetails.class.cast(obj);
+      return super.equals(that) && Objects.equal(this.minDisk, that.minDisk)
+            && Objects.equal(this.minRam, that.minRam)
+            && Objects.equal(this.location, that.location)
+            && Objects.equal(this.owner, that.owner)
+            && Objects.equal(this.updatedAt, that.updatedAt)
+            && Objects.equal(this.createdAt, that.createdAt)
+            && Objects.equal(this.deletedAt, that.deletedAt)
+            && Objects.equal(this.status, that.status)
+            && Objects.equal(this.isPublic, that.isPublic)
+            && Objects.equal(this.properties, that.properties);
+   }
+
+   protected ToStringHelper string() {
+      return super.string()
+            .add("minDisk", minDisk).add("minRam", minRam).add("location", location).add("owner", owner).add("updatedAt", updatedAt).add("createdAt", createdAt).add("deletedAt", deletedAt).add("status", status).add("isPublic", isPublic).add("properties", properties);
+   }
+
+}
diff --git a/labs/openstack-glance/src/main/java/org/jclouds/openstack/glance/v1_0/functions/ParseImageDetailsFromHeaders.java b/labs/openstack-glance/src/main/java/org/jclouds/openstack/glance/v1_0/functions/ParseImageDetailsFromHeaders.java
index b2c374e..44ecc33 100644
--- a/labs/openstack-glance/src/main/java/org/jclouds/openstack/glance/v1_0/functions/ParseImageDetailsFromHeaders.java
+++ b/labs/openstack-glance/src/main/java/org/jclouds/openstack/glance/v1_0/functions/ParseImageDetailsFromHeaders.java
@@ -18,21 +18,7 @@
  */
 package org.jclouds.openstack.glance.v1_0.functions;
 
-import static org.jclouds.openstack.glance.v1_0.options.ImageField.CHECKSUM;
-import static org.jclouds.openstack.glance.v1_0.options.ImageField.CONTAINER_FORMAT;
-import static org.jclouds.openstack.glance.v1_0.options.ImageField.CREATED_AT;
-import static org.jclouds.openstack.glance.v1_0.options.ImageField.DELETED_AT;
-import static org.jclouds.openstack.glance.v1_0.options.ImageField.DISK_FORMAT;
-import static org.jclouds.openstack.glance.v1_0.options.ImageField.ID;
-import static org.jclouds.openstack.glance.v1_0.options.ImageField.IS_PUBLIC;
-import static org.jclouds.openstack.glance.v1_0.options.ImageField.LOCATION;
-import static org.jclouds.openstack.glance.v1_0.options.ImageField.MIN_DISK;
-import static org.jclouds.openstack.glance.v1_0.options.ImageField.MIN_RAM;
-import static org.jclouds.openstack.glance.v1_0.options.ImageField.NAME;
-import static org.jclouds.openstack.glance.v1_0.options.ImageField.OWNER;
-import static org.jclouds.openstack.glance.v1_0.options.ImageField.SIZE;
-import static org.jclouds.openstack.glance.v1_0.options.ImageField.STATUS;
-import static org.jclouds.openstack.glance.v1_0.options.ImageField.UPDATED_AT;
+import static org.jclouds.openstack.glance.v1_0.options.ImageField.*;
 
 import javax.inject.Inject;
 
@@ -40,11 +26,10 @@
 import org.jclouds.http.HttpResponse;
 import org.jclouds.openstack.glance.v1_0.domain.ContainerFormat;
 import org.jclouds.openstack.glance.v1_0.domain.DiskFormat;
-import org.jclouds.openstack.glance.v1_0.domain.ImageDetails;
 import org.jclouds.openstack.glance.v1_0.domain.Image.Status;
+import org.jclouds.openstack.glance.v1_0.domain.ImageDetails;
 
 import com.google.common.base.Function;
-import com.google.common.base.Optional;
 
 /**
  * This parses {@link ImageDetails} from HTTP headers.
@@ -63,14 +48,14 @@
       ImageDetails.Builder<?> builder = ImageDetails.builder()
                 .id(from.getFirstHeaderOrNull(ID.asHeader()))
                 .name(from.getFirstHeaderOrNull(NAME.asHeader()))
-                .checksum(Optional.fromNullable(from.getFirstHeaderOrNull(CHECKSUM.asHeader())))
+                .checksum(from.getFirstHeaderOrNull(CHECKSUM.asHeader()))
                 .minDisk(Long.parseLong(from.getFirstHeaderOrNull(MIN_DISK.asHeader())))
                 .minRam(Long.parseLong(from.getFirstHeaderOrNull(MIN_RAM.asHeader())))
                 .isPublic(Boolean.parseBoolean(from.getFirstHeaderOrNull(IS_PUBLIC.asHeader())))
                 .createdAt(dateService.iso8601SecondsDateParse(from.getFirstHeaderOrNull(CREATED_AT.asHeader())))
                 .updatedAt(dateService.iso8601SecondsDateParse(from.getFirstHeaderOrNull(UPDATED_AT.asHeader())))
-                .owner(Optional.fromNullable(from.getFirstHeaderOrNull(OWNER.asHeader())))
-                .location(Optional.fromNullable(from.getFirstHeaderOrNull(LOCATION.asHeader())))
+                .owner(from.getFirstHeaderOrNull(OWNER.asHeader()))
+                .location(from.getFirstHeaderOrNull(LOCATION.asHeader()))
                 .status(Status.fromValue(from.getFirstHeaderOrNull(STATUS.asHeader())));
 
       String containerFormat = from.getFirstHeaderOrNull(CONTAINER_FORMAT.asHeader());
diff --git a/labs/openstack-glance/src/test/java/org/jclouds/openstack/glance/v1_0/parse/ParseImageDetailsTest.java b/labs/openstack-glance/src/test/java/org/jclouds/openstack/glance/v1_0/parse/ParseImageDetailsTest.java
index 68bc45b..90b7b54 100644
--- a/labs/openstack-glance/src/test/java/org/jclouds/openstack/glance/v1_0/parse/ParseImageDetailsTest.java
+++ b/labs/openstack-glance/src/test/java/org/jclouds/openstack/glance/v1_0/parse/ParseImageDetailsTest.java
@@ -53,7 +53,7 @@
                   .containerFormat(ContainerFormat.BARE)
                   .diskFormat(DiskFormat.RAW)
                   .checksum("6ae4e0fdc3c108a1bfe10ef5e436f4f4")
-                  .size(27)
+                  .size(27L)
                   .status(Image.Status.ACTIVE)
                   .owner("68a7c7abb7bf45ada1536dfa28ec2115")
                   .isPublic(false)
diff --git a/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/QuantumApiMetadata.java b/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/QuantumApiMetadata.java
index cb99c31..026c500 100644
--- a/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/QuantumApiMetadata.java
+++ b/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/QuantumApiMetadata.java
@@ -26,8 +26,9 @@
 
 import org.jclouds.apis.ApiMetadata;
 import org.jclouds.openstack.keystone.v2_0.config.CredentialTypes;
+import org.jclouds.openstack.keystone.v2_0.config.KeystoneAuthenticationModule;
+import org.jclouds.openstack.keystone.v2_0.config.KeystoneAuthenticationModule.RegionModule;
 import org.jclouds.openstack.keystone.v2_0.config.KeystoneProperties;
-import org.jclouds.openstack.keystone.v2_0.config.KeystoneAuthenticationModule.KeystoneAuthenticationModuleForRegions;
 import org.jclouds.openstack.quantum.v1_0.config.QuantumRestClientModule;
 import org.jclouds.openstack.v2_0.ServiceType;
 import org.jclouds.rest.RestContext;
@@ -85,7 +86,10 @@
          .version("1.0")
          .defaultEndpoint("http://localhost:5000")
          .defaultProperties(QuantumApiMetadata.defaultProperties())
-         .defaultModules(ImmutableSet.<Class<? extends Module>>of(KeystoneAuthenticationModuleForRegions.class, QuantumRestClientModule.class));
+         .defaultModules(ImmutableSet.<Class<? extends Module>>builder()
+                                     .add(KeystoneAuthenticationModule.class)
+                                     .add(RegionModule.class)
+                                     .add(QuantumRestClientModule.class).build());
       }
       
       @Override
diff --git a/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/config/QuantumRestClientModule.java b/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/config/QuantumRestClientModule.java
index 8850d98..623b59d 100644
--- a/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/config/QuantumRestClientModule.java
+++ b/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/config/QuantumRestClientModule.java
@@ -60,7 +60,7 @@
       bind(DateAdapter.class).to(Iso8601DateAdapter.class);
       super.configure();
    }
-
+   
    @Override
    protected void bindErrorHandlers() {
       bind(HttpErrorHandler.class).annotatedWith(Redirection.class).to(QuantumErrorHandler.class);
diff --git a/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/domain/Attachment.java b/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/domain/Attachment.java
index 9ff5920..6a59cb3 100644
--- a/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/domain/Attachment.java
+++ b/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/domain/Attachment.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -18,29 +18,30 @@
  */
 package org.jclouds.openstack.quantum.v1_0.domain;
 
+import java.beans.ConstructorProperties;
+
 /**
  * A Quantum attachment
- *
+ * 
  * @author Adam Lowe
  * @see <a href="http://docs.openstack.org/api/openstack-network/1.0/content/Attachments.html">api doc</a>
- */
+*/
 public class Attachment extends Reference {
 
-   public static Builder<?> builder() {
+   public static Builder<?> builder() { 
       return new ConcreteBuilder();
    }
-
-   public Builder<?> toBuilder() {
+   
+   public Builder<?> toBuilder() { 
       return new ConcreteBuilder().fromAttachment(this);
    }
 
-   public static abstract class Builder<T extends Builder<T>> extends Reference.Builder<T> {
-      protected abstract T self();
-
+   public static abstract class Builder<T extends Builder<T>> extends Reference.Builder<T>  {
+   
       public Attachment build() {
-         return new Attachment(this);
+         return new Attachment(id);
       }
-
+      
       public T fromAttachment(Attachment in) {
          return super.fromReference(in);
       }
@@ -53,11 +54,12 @@
       }
    }
 
-   protected Attachment(Builder<?> builder) {
-      super(builder);
+
+   @ConstructorProperties({
+      "id"
+   })
+   protected Attachment(String id) {
+      super(id);
    }
 
-   protected Attachment() {
-      // for GSON
-   }
-}
\ No newline at end of file
+}
diff --git a/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/domain/Network.java b/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/domain/Network.java
index 6ad19f7..366b3d1 100644
--- a/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/domain/Network.java
+++ b/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/domain/Network.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -20,31 +20,40 @@
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import java.beans.ConstructorProperties;
+
+import javax.inject.Named;
+
+import org.jclouds.javax.annotation.Nullable;
+
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Sets;
+import com.google.common.collect.ImmutableSet;
 import com.google.common.base.Objects;
 import com.google.common.base.Objects.ToStringHelper;
 
 /**
  * A Quantum network
- *
+ * 
  * @author Adam Lowe
  * @see <a href="http://docs.openstack.org/api/openstack-network/1.0/content/Networks.html">api doc</a>
- */
+*/
 public class Network extends Reference {
 
-   public static Builder<?> builder() {
+   public static Builder<?> builder() { 
       return new ConcreteBuilder();
    }
-
-   public Builder<?> toBuilder() {
+   
+   public Builder<?> toBuilder() { 
       return new ConcreteBuilder().fromNetwork(this);
    }
 
-   public static abstract class Builder<T extends Builder<T>> extends Reference.Builder<T> {
-      protected abstract T self();
-
-      private String name;
-
-      /**
+   public static abstract class Builder<T extends Builder<T>> extends Reference.Builder<T>  {
+      protected String name;
+   
+      /** 
        * @see Network#getName()
        */
       public T name(String name) {
@@ -53,11 +62,12 @@
       }
 
       public Network build() {
-         return new Network(this);
+         return new Network(id, name);
       }
-
+      
       public T fromNetwork(Network in) {
-         return super.fromReference(in).name(in.getName());
+         return super.fromReference(in)
+                  .name(in.getName());
       }
    }
 
@@ -70,25 +80,21 @@
 
    private final String name;
 
-   protected Network(Builder<?> builder) {
-      super(builder);
-      this.name = checkNotNull(builder.name, "name");
+   @ConstructorProperties({
+      "id", "name"
+   })
+   protected Network(String id, String name) {
+      super(id);
+      this.name = checkNotNull(name, "name");
    }
 
-   protected Network() {
-      // for GSON
-      this.name = null;
-   }
-
-   /**
-    */
    public String getName() {
       return this.name;
    }
 
    @Override
    public int hashCode() {
-      return Objects.hashCode(super.hashCode(), name);
+      return Objects.hashCode(name);
    }
 
    @Override
@@ -98,8 +104,10 @@
       Network that = Network.class.cast(obj);
       return super.equals(that) && Objects.equal(this.name, that.name);
    }
-
+   
    protected ToStringHelper string() {
-      return super.string().add("name", name);
+      return super.string()
+            .add("name", name);
    }
-}
\ No newline at end of file
+   
+}
diff --git a/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/domain/NetworkDetails.java b/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/domain/NetworkDetails.java
index 8d76a0b..3515838 100644
--- a/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/domain/NetworkDetails.java
+++ b/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/domain/NetworkDetails.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -20,7 +20,7 @@
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
-import java.util.Collections;
+import java.beans.ConstructorProperties;
 import java.util.Set;
 
 import com.google.common.base.Objects;
@@ -29,28 +29,28 @@
 
 /**
  * Details of a Quantum network
- *
+ * 
  * @author Adam Lowe
  * @see <a href="http://docs.openstack.org/api/openstack-network/1.0/content/Networks.html">api doc</a>
- */
+*/
 public class NetworkDetails extends Network {
 
-   public static Builder<?> builder() {
+   public static Builder<?> builder() { 
       return new ConcreteBuilder();
    }
-
-   public Builder<?> toBuilder() {
+   
+   public Builder<?> toBuilder() { 
       return new ConcreteBuilder().fromNetworkDetails(this);
    }
 
    public static abstract class Builder<T extends Builder<T>> extends Network.Builder<T>  {
-      private Set<Port> ports = ImmutableSet.of();
-
-      /**
+      protected Set<Port> ports = ImmutableSet.of();
+   
+      /** 
        * @see NetworkDetails#getPorts()
        */
       public T ports(Set<Port> ports) {
-         this.ports = ports;
+         this.ports = ImmutableSet.copyOf(checkNotNull(ports, "ports"));      
          return self();
       }
 
@@ -59,11 +59,12 @@
       }
 
       public NetworkDetails build() {
-         return new NetworkDetails(this);
+         return new NetworkDetails(id, name, ports);
       }
-
+      
       public T fromNetworkDetails(NetworkDetails in) {
-         return super.fromNetwork(in).ports(in.getPorts());
+         return super.fromNetwork(in)
+                  .ports(in.getPorts());
       }
    }
 
@@ -76,20 +77,16 @@
 
    private final Set<Port> ports;
 
-   protected NetworkDetails(Builder<?> builder) {
-      super(builder);
-      this.ports = ImmutableSet.copyOf(checkNotNull(builder.ports, "ports"));
+   @ConstructorProperties({
+      "id", "name", "ports"
+   })
+   protected NetworkDetails(String id, String name, Set<Port> ports) {
+      super(id, name);
+      this.ports = ImmutableSet.copyOf(checkNotNull(ports, "ports"));      
    }
 
-   protected NetworkDetails() {
-      // for GSON
-      this.ports = ImmutableSet.of();
-   }
-
-   /**
-    */
    public Set<Port> getPorts() {
-      return Collections.unmodifiableSet(this.ports);
+      return this.ports;
    }
 
    @Override
@@ -104,9 +101,10 @@
       NetworkDetails that = NetworkDetails.class.cast(obj);
       return super.equals(that) && Objects.equal(this.ports, that.ports);
    }
-
+   
    protected ToStringHelper string() {
-      return super.string().add("ports", ports);
+      return super.string()
+            .add("ports", ports);
    }
-
-}
\ No newline at end of file
+   
+}
diff --git a/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/domain/Port.java b/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/domain/Port.java
index 41b1566..035e3de 100644
--- a/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/domain/Port.java
+++ b/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/domain/Port.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -20,35 +20,37 @@
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import java.beans.ConstructorProperties;
+
 import com.google.common.base.Objects;
 import com.google.common.base.Objects.ToStringHelper;
 
 /**
  * A Quantum port
- *
+ * 
  * @author Adam Lowe
  * @see <a href="http://docs.openstack.org/api/openstack-network/1.0/content/Ports.html">api doc</a>
- */
+*/
 public class Port extends Reference {
 
+   /**
+    */
    public static enum State {
       ACTIVE, DOWN
    }
 
-   public static Builder<?> builder() {
+   public static Builder<?> builder() { 
       return new ConcreteBuilder();
    }
-
-   public Builder<?> toBuilder() {
+   
+   public Builder<?> toBuilder() { 
       return new ConcreteBuilder().fromPort(this);
    }
 
-   public static abstract class Builder<T extends Builder<T>> extends Reference.Builder<T> {
-      protected abstract T self();
-
-      private Port.State state;
-
-      /**
+   public static abstract class Builder<T extends Builder<T>> extends Reference.Builder<T>  {
+      protected Port.State state;
+   
+      /** 
        * @see Port#getState()
        */
       public T state(Port.State state) {
@@ -57,11 +59,12 @@
       }
 
       public Port build() {
-         return new Port(this);
+         return new Port(id, state);
       }
-
+      
       public T fromPort(Port in) {
-         return fromReference(in).state(in.getState());
+         return super.fromReference(in)
+                  .state(in.getState());
       }
    }
 
@@ -74,14 +77,12 @@
 
    private final Port.State state;
 
-   protected Port(Builder<?> builder) {
-      super(builder);
-      this.state = checkNotNull(builder.state, "state");
-   }
-
-   protected Port() {
-      // for GSON
-      this.state = null;
+   @ConstructorProperties({
+      "id", "state"
+   })
+   protected Port(String id, Port.State state) {
+      super(id);
+      this.state = checkNotNull(state, "state");
    }
 
    public Port.State getState() {
@@ -90,7 +91,7 @@
 
    @Override
    public int hashCode() {
-      return Objects.hashCode(super.hashCode(), state);
+      return Objects.hashCode(state);
    }
 
    @Override
@@ -100,8 +101,10 @@
       Port that = Port.class.cast(obj);
       return super.equals(that) && Objects.equal(this.state, that.state);
    }
-
+   
    protected ToStringHelper string() {
-      return super.string().add("state", state);
+      return super.string()
+            .add("state", state);
    }
-}
\ No newline at end of file
+   
+}
diff --git a/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/domain/PortDetails.java b/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/domain/PortDetails.java
index df5474f..c5ed414 100644
--- a/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/domain/PortDetails.java
+++ b/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/domain/PortDetails.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -18,6 +18,8 @@
  */
 package org.jclouds.openstack.quantum.v1_0.domain;
 
+import java.beans.ConstructorProperties;
+
 import org.jclouds.javax.annotation.Nullable;
 
 import com.google.common.base.Objects;
@@ -25,24 +27,24 @@
 
 /**
  * Details of a Quantum Port
- *
+ * 
  * @author Adam Lowe
  * @see <a href="http://docs.openstack.org/api/openstack-network/1.0/content/Ports.html">api doc</a>
- */
+*/
 public class PortDetails extends Port {
 
-   public static Builder<?> builder() {
+   public static Builder<?> builder() { 
       return new ConcreteBuilder();
    }
-
-   public Builder<?> toBuilder() {
+   
+   public Builder<?> toBuilder() { 
       return new ConcreteBuilder().fromPortDetails(this);
    }
 
-   public static abstract class Builder<T extends Builder<T>> extends Port.Builder<T> {
-      private Attachment attachment;
-
-      /**
+   public static abstract class Builder<T extends Builder<T>> extends Port.Builder<T>  {
+      protected Attachment attachment;
+   
+      /** 
        * @see PortDetails#getAttachment()
        */
       public T attachment(Attachment attachment) {
@@ -51,11 +53,12 @@
       }
 
       public PortDetails build() {
-         return new PortDetails(this);
+         return new PortDetails(id, state, attachment);
       }
-
+      
       public T fromPortDetails(PortDetails in) {
-         return super.fromPort(in).attachment(in.getAttachment());
+         return super.fromPort(in)
+                  .attachment(in.getAttachment());
       }
    }
 
@@ -66,17 +69,14 @@
       }
    }
 
-   @Nullable
    private final Attachment attachment;
 
-   protected PortDetails(Builder<?> builder) {
-      super(builder);
-      this.attachment = builder.attachment;
-   }
-
-   protected PortDetails() {
-      // for GSON
-      this.attachment = null;
+   @ConstructorProperties({
+      "id", "state", "attachment"
+   })
+   protected PortDetails(String id, Port.State state, @Nullable Attachment attachment) {
+      super(id, state);
+      this.attachment = attachment;
    }
 
    @Nullable
@@ -96,9 +96,10 @@
       PortDetails that = PortDetails.class.cast(obj);
       return super.equals(that) && Objects.equal(this.attachment, that.attachment);
    }
-
+   
    protected ToStringHelper string() {
-      return super.string().add("attachment", attachment);
+      return super.string()
+            .add("attachment", attachment);
    }
-
-}
\ No newline at end of file
+   
+}
diff --git a/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/domain/Reference.java b/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/domain/Reference.java
index 1f6e8a2..c6faf9e 100644
--- a/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/domain/Reference.java
+++ b/labs/openstack-quantum/src/main/java/org/jclouds/openstack/quantum/v1_0/domain/Reference.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to jclouds, Inc. (jclouds) under one or more
  * contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -20,32 +20,34 @@
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import java.beans.ConstructorProperties;
+
 import com.google.common.base.Objects;
 import com.google.common.base.Objects.ToStringHelper;
 
 /**
  * A wrapper around an id in the quantum api
- *
+ * 
  * @author Adam Lowe
  * @see <a href="http://docs.openstack.org/api/openstack-network/1.0/content/Networks.html">api doc</a>
- */
+*/
 public class Reference {
 
-   public static Builder<?> builder() {
+   public static Builder<?> builder() { 
       return new ConcreteBuilder();
    }
-
-   public Builder<?> toBuilder() {
+   
+   public Builder<?> toBuilder() { 
       return new ConcreteBuilder().fromReference(this);
    }
 
    public static abstract class Builder<T extends Builder<T>>  {
       protected abstract T self();
 
-      private String id;
-
-      /**
-       * @see org.jclouds.openstack.quantum.v1_0.domain.Reference#getId()
+      protected String id;
+   
+      /** 
+       * @see Reference#getId()
        */
       public T id(String id) {
          this.id = id;
@@ -53,11 +55,12 @@
       }
 
       public Reference build() {
-         return new Reference(this);
+         return new Reference(id);
       }
-
+      
       public T fromReference(Reference in) {
-         return this.id(in.getId());
+         return this
+                  .id(in.getId());
       }
    }
 
@@ -70,22 +73,17 @@
 
    private final String id;
 
-   protected Reference(Builder<?> builder) {
-      this.id = checkNotNull(builder.id, "id");
+   @ConstructorProperties({
+      "id"
+   })
+   protected Reference(String id) {
+      this.id = checkNotNull(id, "id");
    }
 
-   protected Reference() {
-      // for GSON
-      this.id = null;
-   }
-
-   /**
-    */
    public String getId() {
       return this.id;
    }
 
-
    @Override
    public int hashCode() {
       return Objects.hashCode(id);
@@ -98,14 +96,15 @@
       Reference that = Reference.class.cast(obj);
       return Objects.equal(this.id, that.id);
    }
-
+   
    protected ToStringHelper string() {
-      return Objects.toStringHelper("").add("id", id);
+      return Objects.toStringHelper(this)
+            .add("id", id);
    }
-
+   
    @Override
    public String toString() {
       return string().toString();
    }
 
-}
\ No newline at end of file
+}
diff --git a/labs/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/SwiftApiMetadata.java b/labs/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/SwiftApiMetadata.java
index 8270363..fa7d0b2 100644
--- a/labs/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/SwiftApiMetadata.java
+++ b/labs/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/SwiftApiMetadata.java
@@ -26,8 +26,9 @@
 
 import org.jclouds.apis.ApiMetadata;
 import org.jclouds.openstack.keystone.v2_0.config.CredentialTypes;
+import org.jclouds.openstack.keystone.v2_0.config.KeystoneAuthenticationModule;
+import org.jclouds.openstack.keystone.v2_0.config.KeystoneAuthenticationModule.RegionModule;
 import org.jclouds.openstack.keystone.v2_0.config.KeystoneProperties;
-import org.jclouds.openstack.keystone.v2_0.config.KeystoneAuthenticationModule.KeystoneAuthenticationModuleForRegions;
 import org.jclouds.openstack.swift.v1.config.SwiftRestClientModule;
 import org.jclouds.openstack.v2_0.ServiceType;
 import org.jclouds.rest.RestContext;
@@ -86,7 +87,10 @@
          .version("1.0")
          .defaultEndpoint("http://localhost:5000")
          .defaultProperties(SwiftApiMetadata.defaultProperties())
-         .defaultModules(ImmutableSet.<Class<? extends Module>>of(KeystoneAuthenticationModuleForRegions.class, SwiftRestClientModule.class));
+         .defaultModules(ImmutableSet.<Class<? extends Module>>builder()
+                                     .add(KeystoneAuthenticationModule.class)
+                                     .add(RegionModule.class)
+                                     .add(SwiftRestClientModule.class).build());
       }
       
       @Override
diff --git a/labs/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/domain/AccountMetadata.java b/labs/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/domain/AccountMetadata.java
index 18c1801..b3e70da 100644
--- a/labs/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/domain/AccountMetadata.java
+++ b/labs/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/domain/AccountMetadata.java
@@ -3,6 +3,8 @@
 import static com.google.common.base.Objects.equal;
 import static com.google.common.base.Objects.toStringHelper;
 
+import java.beans.ConstructorProperties;
+
 import com.google.common.base.Objects;
 import com.google.common.base.Objects.ToStringHelper;
 
@@ -48,17 +50,12 @@
          return containerCount(from.getContainerCount()).bytesUsed(from.getBytesUsed());
       }
    }
-
-   protected AccountMetadata() {
-      // we want serializers like Gson to work w/o using sun.misc.Unsafe,
-      // prohibited in GAE. This also implies fields are not final.
-      // see http://code.google.com/p/jclouds/issues/detail?id=925
-   }
   
    protected int containerCount;
    protected long bytesUsed;
 
-   public AccountMetadata(int containerCount, long bytesUsed) {
+   @ConstructorProperties({"containerCount", "bytesUsed"})
+   protected AccountMetadata(int containerCount, long bytesUsed) {
       this.containerCount = containerCount;
       this.bytesUsed = bytesUsed;
    }
diff --git a/labs/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/domain/ContainerMetadata.java b/labs/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/domain/ContainerMetadata.java
index 18e0018..1cbff85 100644
--- a/labs/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/domain/ContainerMetadata.java
+++ b/labs/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/domain/ContainerMetadata.java
@@ -4,6 +4,8 @@
 import static com.google.common.base.Objects.toStringHelper;
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import java.beans.ConstructorProperties;
+
 import com.google.common.base.Objects;
 import com.google.common.base.Objects.ToStringHelper;
 
@@ -64,18 +66,13 @@
          return name(from.getName()).count(from.getCount()).bytes(from.getBytes());
       }
    }
-
-   protected ContainerMetadata() {
-      // we want serializers like Gson to work w/o using sun.misc.Unsafe,
-      // prohibited in GAE. This also implies fields are not final.
-      // see http://code.google.com/p/jclouds/issues/detail?id=925
-   }
   
    protected String name;
    protected int count;
    protected int bytes;
 
-   public ContainerMetadata(String name, int count, int bytes) {
+   @ConstructorProperties({"name", "count", "bytes"})
+   protected ContainerMetadata(String name, int count, int bytes) {
       this.name = checkNotNull(name, "name");
       this.count = count;
       this.bytes = bytes;
diff --git a/labs/pom.xml b/labs/pom.xml
index b223764..878c602 100644
--- a/labs/pom.xml
+++ b/labs/pom.xml
@@ -48,9 +48,10 @@
        <module>joyent-sdc</module>
        <module>joyentcloud</module>
        <module>openstack-quantum</module>
-       <module>rackspace-cloudidentity</module>
-       <module>rackspace-cloudservers-us</module>
        <module>greenqloud-compute</module>
        <module>greenqloud-storage</module>
+       <module>iam</module>
+       <module>aws-iam</module>
+       <module>nodepool</module>
     </modules>
 </project>
diff --git a/labs/rackspace-cloudidentity/src/main/java/org/jclouds/rackspace/cloudidentity/v2_0/config/CloudIdentityRestClientModule.java b/labs/rackspace-cloudidentity/src/main/java/org/jclouds/rackspace/cloudidentity/v2_0/config/CloudIdentityRestClientModule.java
deleted file mode 100644
index a881197..0000000
--- a/labs/rackspace-cloudidentity/src/main/java/org/jclouds/rackspace/cloudidentity/v2_0/config/CloudIdentityRestClientModule.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package org.jclouds.rackspace.cloudidentity.v2_0.config;
-
-import org.jclouds.location.config.LocationModule;
-import org.jclouds.openstack.keystone.v2_0.KeystoneAsyncClient;
-import org.jclouds.openstack.keystone.v2_0.KeystoneClient;
-import org.jclouds.openstack.keystone.v2_0.config.KeystoneRestClientModule;
-import org.jclouds.rest.ConfiguresRestClient;
-
-/**
- * Configures the Keystone connection.
- * 
- * @author Adam Lowe
- */
-@ConfiguresRestClient
-public class CloudIdentityRestClientModule extends KeystoneRestClientModule<KeystoneClient, KeystoneAsyncClient> {
-
-   @Override
-   protected void installLocations() {
-      // TODO: select this from KeystoneProperties.VERSION; note you select from
-      // a guice provided property, so it will have to come from somewhere else, maybe we move
-      // this to the the ContextBuilder
-      install(CloudIdentityAuthenticationModule.forRegions());
-      install(new LocationModule());
-   }
-}
diff --git a/labs/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/config/VPDCRestClientModule.java b/labs/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/config/VPDCRestClientModule.java
index f1434b9..d2bdd6c 100644
--- a/labs/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/config/VPDCRestClientModule.java
+++ b/labs/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/config/VPDCRestClientModule.java
@@ -27,6 +27,7 @@
 import java.io.IOException;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.TimeUnit;
 
 import javax.inject.Named;
 import javax.inject.Singleton;
@@ -62,11 +63,12 @@
 import org.jclouds.savvis.vpdc.location.FirstNetwork;
 import org.jclouds.savvis.vpdc.predicates.TaskSuccess;
 import org.jclouds.util.Strings2;
+import org.jclouds.util.Suppliers2;
 
 import com.google.common.base.Function;
+import com.google.common.base.Objects;
 import com.google.common.base.Predicate;
 import com.google.common.base.Supplier;
-import com.google.common.base.Suppliers;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Iterables;
 import com.google.inject.Injector;
@@ -91,7 +93,7 @@
    @Provides
    @Singleton
    protected Supplier<String> provideVCloudToken(Supplier<VCloudSession> cache) {
-      return Suppliers.compose(new Function<VCloudSession, String>() {
+      return Suppliers2.compose(new Function<VCloudSession, String>() {
 
          @Override
          public String apply(VCloudSession input) {
@@ -106,7 +108,7 @@
    @Singleton
    protected Supplier<Set<org.jclouds.savvis.vpdc.domain.Resource>> provideOrgs(Supplier<VCloudSession> cache,
             @Identity final String user) {
-      return Suppliers.compose(new Function<VCloudSession, Set<org.jclouds.savvis.vpdc.domain.Resource>>() {
+      return Suppliers2.compose(new Function<VCloudSession, Set<org.jclouds.savvis.vpdc.domain.Resource>>() {
 
          @Override
          public Set<org.jclouds.savvis.vpdc.domain.Resource> apply(VCloudSession input) {
@@ -122,7 +124,7 @@
    @Singleton
    protected Supplier<String> provideDefaultOrgId(
             @org.jclouds.savvis.vpdc.internal.Org Supplier<Set<org.jclouds.savvis.vpdc.domain.Resource>> orgs) {
-      return Suppliers.compose(new Function<Set<org.jclouds.savvis.vpdc.domain.Resource>, String>() {
+      return Suppliers2.compose(new Function<Set<org.jclouds.savvis.vpdc.domain.Resource>, String>() {
 
          @Override
          public String apply(Set<org.jclouds.savvis.vpdc.domain.Resource> input) {
@@ -163,15 +165,18 @@
    @Singleton
    protected Supplier<VCloudSession> provideVCloudTokenCache(@Named(PROPERTY_SESSION_INTERVAL) long seconds,
             final LoginClient login) {
-      return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier<VCloudSession>(authException, seconds,
+      return MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(authException,
                new Supplier<VCloudSession>() {
 
                   @Override
                   public VCloudSession get() {
                      return login.login();
                   }
-
-               });
+                  @Override
+                  public String toString() {
+                     return Objects.toStringHelper(login).add("method", "login").toString();
+                  }
+               }, seconds, TimeUnit.SECONDS);
    }
 
    @Override
diff --git a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/config/VCloudDirectorRestClientModule.java b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/config/VCloudDirectorRestClientModule.java
index d8af02a..8c93b29 100644
--- a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/config/VCloudDirectorRestClientModule.java
+++ b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/config/VCloudDirectorRestClientModule.java
@@ -40,6 +40,7 @@
 import org.jclouds.rest.config.BinderUtils;
 import org.jclouds.rest.config.RestClientModule;
 import org.jclouds.rest.internal.RestContextImpl;
+import org.jclouds.util.Suppliers2;
 import org.jclouds.vcloud.director.v1_5.admin.VCloudDirectorAdminAsyncClient;
 import org.jclouds.vcloud.director.v1_5.admin.VCloudDirectorAdminClient;
 import org.jclouds.vcloud.director.v1_5.annotations.Login;
@@ -93,7 +94,6 @@
 
 import com.google.common.base.Function;
 import com.google.common.base.Supplier;
-import com.google.common.base.Suppliers;
 import com.google.common.cache.CacheBuilder;
 import com.google.common.cache.CacheLoader;
 import com.google.common.cache.LoadingCache;
@@ -187,7 +187,7 @@
    @Login
    protected Supplier<URI> loginUrl(@Provider Supplier<URI> provider) {
       // TODO: technically, we should implement version client, but this will work
-      return Suppliers.compose(new Function<URI, URI>() {
+      return Suppliers2.compose(new Function<URI, URI>() {
          
          @Override
          public URI apply(URI arg0) {
@@ -199,7 +199,7 @@
    
    @Provides
    protected Supplier<Session> currentSession(Supplier<SessionWithToken> in) {
-      return Suppliers.compose(new Function<SessionWithToken, Session>() {
+      return Suppliers2.compose(new Function<SessionWithToken, Session>() {
          
          @Override
          public Session apply(SessionWithToken arg0) {
@@ -214,7 +214,7 @@
    @Singleton
    @org.jclouds.vcloud.director.v1_5.annotations.Session
    protected Supplier<String> sessionToken(Supplier<SessionWithToken> in) {
-      return Suppliers.compose(new Function<SessionWithToken, String>() {
+      return Suppliers2.compose(new Function<SessionWithToken, String>() {
          
          @Override
          public String apply(SessionWithToken arg0) {
diff --git a/loadbalancer/src/test/java/org/jclouds/loadbalancer/BaseLoadBalancerServiceLiveTest.java b/loadbalancer/src/test/java/org/jclouds/loadbalancer/BaseLoadBalancerServiceLiveTest.java
index 5ef28f1..4914f39 100644
--- a/loadbalancer/src/test/java/org/jclouds/loadbalancer/BaseLoadBalancerServiceLiveTest.java
+++ b/loadbalancer/src/test/java/org/jclouds/loadbalancer/BaseLoadBalancerServiceLiveTest.java
@@ -43,7 +43,7 @@
 import org.jclouds.predicates.SocketOpen;
 import org.jclouds.ssh.SshClient;
 import org.testng.annotations.AfterTest;
-import org.testng.annotations.BeforeGroups;
+import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
 import com.google.common.base.Splitter;
@@ -111,7 +111,7 @@
    protected String computeBuildversion;
    protected ComputeServiceContext computeContext;
 
-   @BeforeGroups(groups = { "integration", "live" })
+   @BeforeClass(groups = { "integration", "live" })
    @Override
    public void setupContext() {
       setServiceDefaults();
@@ -144,7 +144,7 @@
 
    abstract protected Module getSshModule();
 
-   @BeforeGroups(groups = { "integration", "live" }, dependsOnMethods = "setupClient")
+   @BeforeClass(groups = { "integration", "live" }, dependsOnMethods = "setupContext")
    public void createNodes() throws RunNodesException {
       try {
          nodes = computeContext.getComputeService().createNodesInGroup(group, 2);
diff --git a/pom.xml b/pom.xml
index 2092ec5..ecde0de 100644
--- a/pom.xml
+++ b/pom.xml
@@ -55,38 +55,6 @@
     <build>
         <plugins>
             <plugin>
-                <artifactId>maven-javadoc-plugin</artifactId>
-                <executions>
-                    <execution>
-                        <id>javadoc</id>
-                        <phase>package</phase>
-                        <goals>
-                            <goal>aggregate-jar</goal>
-                        </goals>
-                    </execution>
-                </executions>                
-                <configuration>
-                    <additionalJOption>-J-Xmx512m</additionalJOption>
-                    <encoding>${project.build.sourceEncoding}</encoding>
-                    <quiet>true</quiet>
-                    <links>
-                        <link>http://download.oracle.com/javase/6/docs/api/</link>
-                    </links>
-                    <footer><![CDATA[
-                     <!-- Google Analytics -->
-<script type='text/javascript'>
-var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
-document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
-</script>
-<script type='text/javascript'>
-try {
-var pageTracker = _gat._getTracker("UA-8638379-1");
-pageTracker._trackPageview();
-} catch(err) {}</script>
-                     ]]></footer>
-                </configuration>
-            </plugin>
-            <plugin>
                 <groupId>com.mycila.maven-license-plugin</groupId>
                 <artifactId>maven-license-plugin</artifactId>
                 <version>1.9.0</version>
@@ -144,5 +112,24 @@
                 </plugins>
             </build>
         </profile>
+        <profile>
+          <id>doc</id>
+          <build>
+            <plugins>
+              <plugin>
+                <artifactId>maven-javadoc-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>javadoc</id>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>aggregate-jar</goal>
+                        </goals>
+                    </execution>
+                </executions>                
+              </plugin>
+            </plugins>
+          </build>
+        </profile>
     </profiles>
 </project>
diff --git a/project/pom.xml b/project/pom.xml
index c8c2cf0..e84ef06 100644
--- a/project/pom.xml
+++ b/project/pom.xml
@@ -430,52 +430,6 @@
                     </systemPropertyVariables>
                 </configuration>
             </plugin>
-            <!-- make sure we generate src jars too -->
-            <plugin>
-                <artifactId>maven-source-plugin</artifactId>
-                <version>2.1.2</version>
-                <executions>
-                    <execution>
-                        <id>attach-sources</id>
-                        <goals>
-                            <goal>jar-no-fork</goal>
-                        </goals>
-                    </execution>
-                </executions>
-            </plugin>
-            <plugin>
-                <artifactId>maven-javadoc-plugin</artifactId>
-                <version>2.8.1</version>
-                <executions>
-                    <execution>
-                        <id>javadoc</id>
-                        <phase>package</phase>
-                        <goals>
-                            <goal>jar</goal>
-                        </goals>
-                    </execution>
-                </executions>
-                <configuration>
-                    <additionalJOption>-J-Xmx256m</additionalJOption>
-                    <encoding>${project.build.sourceEncoding}</encoding>
-                    <quiet>true</quiet>
-                    <links>
-                        <link>http://download.oracle.com/javase/6/docs/api/</link>
-                    </links>
-                    <footer><![CDATA[
-                     <!-- Google Analytics -->
-<script type='text/javascript'>
-var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
-document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
-</script>
-<script type='text/javascript'>
-try {
-var pageTracker = _gat._getTracker("UA-8638379-1");
-pageTracker._trackPageview();
-} catch(err) {}</script>
-                     ]]></footer>
-                </configuration>
-            </plugin>
             <plugin>
                 <artifactId>maven-enforcer-plugin</artifactId>
                 <version>1.1</version>
@@ -613,6 +567,9 @@
                 <plugin>
                     <artifactId>maven-release-plugin</artifactId>
                     <version>2.3.1</version>
+                    <configuration>
+                      <releaseProfiles>doc</releaseProfiles>
+                    </configuration>
                 </plugin>
                 <plugin>
                     <artifactId>maven-pmd-plugin</artifactId>
@@ -747,6 +704,32 @@
                         </execution>
                     </executions>
                 </plugin>
+                <plugin>
+                  <groupId>org.apache.maven.plugins</groupId>
+                  <artifactId>maven-javadoc-plugin</artifactId>
+                  <version>2.8.1</version>
+                  <configuration>
+                    <additionalJOption>-J-Xmx256m</additionalJOption>
+                    <encoding>${project.build.sourceEncoding}</encoding>
+                    <quiet>true</quiet>
+                    <links>
+                      <link>http://download.oracle.com/javase/6/docs/api/</link>
+                    </links>
+                    <footer><![CDATA[
+                    <!-- Google Analytics -->
+<script type='text/javascript'>
+var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
+document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
+</script>
+<script type='text/javascript'>
+try {
+var pageTracker = _gat._getTracker("UA-8638379-1");
+pageTracker._trackPageview();
+} catch(err) {}</script>
+]]>
+                    </footer>
+                  </configuration>
+                </plugin>
             </plugins>
         </pluginManagement>
     </build>
@@ -915,5 +898,45 @@
                 </plugins>            
             </build>
         </profile>
+        <profile>
+          <id>doc</id>
+          <build>
+            <plugins>
+              <plugin>
+                <artifactId>maven-javadoc-plugin</artifactId>
+                <version>2.8.1</version>
+                <executions>
+                  <execution>
+                    <id>javadoc</id>
+                    <phase>package</phase>
+                    <goals>
+                      <goal>jar</goal>
+                    </goals>
+                  </execution>
+                </executions>
+              </plugin>
+            </plugins>
+          </build>
+        </profile>
+        <profile>
+          <id>src</id>
+          <build>
+            <plugins>
+              <!-- make sure we generate src jars too -->
+              <plugin>
+                <artifactId>maven-source-plugin</artifactId>
+                <version>2.1.2</version>
+                <executions>
+                  <execution>
+                    <id>attach-sources</id>
+                    <goals>
+                      <goal>jar-no-fork</goal>
+                    </goals>
+                  </execution>
+                </executions>
+              </plugin>
+            </plugins>
+          </build>
+        </profile>
     </profiles>
 </project>
diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/AWSEC2TemplateBuilderLiveTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/AWSEC2TemplateBuilderLiveTest.java
index 1f56d26..82ff6c5 100644
--- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/AWSEC2TemplateBuilderLiveTest.java
+++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/AWSEC2TemplateBuilderLiveTest.java
@@ -78,9 +78,9 @@
             case DEBIAN:
                return true;
             case RHEL:
-               return input.version.matches("5.[6]") || input.version.equals("");
+               return input.version.matches("5.[06]") || input.version.equals("");
             case CENTOS:
-               return input.version.matches("5.[246]") || (input.version.equals("5.0") && !input.is64Bit)
+               return input.version.matches("5.[0246]") || (input.version.equals("6.0") && input.is64Bit)
                      || input.version.equals("");
             case WINDOWS:
                return input.version.matches("200[38]") || (input.version.equals("7") && !input.is64Bit)
@@ -170,7 +170,7 @@
 
       Template defaultTemplate = view.getComputeService().templateBuilder().build();
       assert (defaultTemplate.getImage().getProviderId().startsWith("ami-")) : defaultTemplate;
-      assertEquals(defaultTemplate.getImage().getOperatingSystem().getVersion(), "pv-2012.03.1");
+      assertEquals(defaultTemplate.getImage().getOperatingSystem().getVersion(), "pv-2012.03.3");
       assertEquals(defaultTemplate.getImage().getOperatingSystem().is64Bit(), true);
       assertEquals(defaultTemplate.getImage().getOperatingSystem().getFamily(), OsFamily.AMZN_LINUX);
       assertEquals(defaultTemplate.getImage().getUserMetadata().get("rootDeviceType"), "ebs");
@@ -185,7 +185,7 @@
       Template defaultTemplate = view.getComputeService().templateBuilder().osFamily(OsFamily.AMZN_LINUX)
             .imageMatches(EC2ImagePredicates.rootDeviceType(RootDeviceType.INSTANCE_STORE)).build();
       assert (defaultTemplate.getImage().getProviderId().startsWith("ami-")) : defaultTemplate;
-      assertEquals(defaultTemplate.getImage().getOperatingSystem().getVersion(), "pv-2012.03.1");
+      assertEquals(defaultTemplate.getImage().getOperatingSystem().getVersion(), "pv-2012.03.3");
       assertEquals(defaultTemplate.getImage().getOperatingSystem().is64Bit(), true);
       assertEquals(defaultTemplate.getImage().getOperatingSystem().getFamily(), OsFamily.AMZN_LINUX);
       assertEquals(defaultTemplate.getImage().getUserMetadata().get("rootDeviceType"), "instance-store");
diff --git a/providers/aws-s3/src/main/java/org/jclouds/aws/s3/AWSS3AsyncClient.java b/providers/aws-s3/src/main/java/org/jclouds/aws/s3/AWSS3AsyncClient.java
index 9f6e5d9..ac0ec71 100644
--- a/providers/aws-s3/src/main/java/org/jclouds/aws/s3/AWSS3AsyncClient.java
+++ b/providers/aws-s3/src/main/java/org/jclouds/aws/s3/AWSS3AsyncClient.java
@@ -21,7 +21,6 @@
 import static org.jclouds.blobstore.attr.BlobScopes.CONTAINER;
 
 import java.util.Map;
-
 import javax.ws.rs.DELETE;
 import javax.ws.rs.GET;
 import javax.ws.rs.POST;
@@ -68,7 +67,7 @@
 /**
  * Provides access to amazon-specific S3 features
  * 
- * @author Adrian Cole
+ * @author Adrian Cole, Jeremy Whitlock
  */
 @SkipEncoding('/')
 @RequestFilters(RequestAuthorizeSignature.class)
diff --git a/providers/aws-s3/src/main/java/org/jclouds/aws/s3/config/AWSS3RestClientModule.java b/providers/aws-s3/src/main/java/org/jclouds/aws/s3/config/AWSS3RestClientModule.java
index 93e4538..aab19e9 100644
--- a/providers/aws-s3/src/main/java/org/jclouds/aws/s3/config/AWSS3RestClientModule.java
+++ b/providers/aws-s3/src/main/java/org/jclouds/aws/s3/config/AWSS3RestClientModule.java
@@ -23,13 +23,13 @@
 import static org.jclouds.location.reference.LocationConstants.PROPERTY_REGION;
 
 import java.net.URI;
-
 import javax.inject.Named;
 import javax.inject.Singleton;
 
 import org.jclouds.aws.s3.AWSS3AsyncClient;
 import org.jclouds.aws.s3.AWSS3Client;
 import org.jclouds.aws.s3.binders.AssignCorrectHostnameAndBindAsHostPrefixIfConfigured;
+import org.jclouds.aws.s3.predicates.validators.AWSS3BucketNameValidator;
 import org.jclouds.location.Region;
 import org.jclouds.rest.ConfiguresRestClient;
 import org.jclouds.rest.RestContext;
@@ -38,6 +38,7 @@
 import org.jclouds.s3.S3Client;
 import org.jclouds.s3.binders.BindAsHostPrefixIfConfigured;
 import org.jclouds.s3.config.S3RestClientModule;
+import org.jclouds.s3.predicates.validators.BucketNameValidator;
 
 import com.google.common.base.Supplier;
 import com.google.common.base.Suppliers;
@@ -64,6 +65,7 @@
    @Override
    protected void configure() {
       bind(BindAsHostPrefixIfConfigured.class).to(AssignCorrectHostnameAndBindAsHostPrefixIfConfigured.class);
+      bind(BucketNameValidator.class).to(AWSS3BucketNameValidator.class);
       super.configure();
    }
    
diff --git a/providers/aws-s3/src/main/java/org/jclouds/aws/s3/predicates/validators/AWSS3BucketNameValidator.java b/providers/aws-s3/src/main/java/org/jclouds/aws/s3/predicates/validators/AWSS3BucketNameValidator.java
new file mode 100644
index 0000000..d2e7617
--- /dev/null
+++ b/providers/aws-s3/src/main/java/org/jclouds/aws/s3/predicates/validators/AWSS3BucketNameValidator.java
@@ -0,0 +1,57 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.aws.s3.predicates.validators;
+
+import javax.inject.Inject;
+
+import org.jclouds.s3.predicates.validators.BucketNameValidator;
+
+import com.google.inject.Singleton;
+
+/**
+ * Validates name for AWS S3 buckets. The complete requirements are listed at:
+ * http://docs.amazonwebservices.com/AmazonS3/latest/index.html?BucketRestrictions.html
+ * 
+ * @see org.jclouds.rest.InputParamValidator
+ * @see org.jclouds.predicates.Validator
+ * 
+ * @author Adrian Cole, Jeremy Whitlock
+ */
+@Singleton
+public class AWSS3BucketNameValidator extends BucketNameValidator {
+
+   @Inject
+   AWSS3BucketNameValidator() {
+      super();
+   }
+
+   public void validate(String containerName) {
+      // AWS S3 allows for upper case characters in bucket names (US Standard region only) and behind the scenes will
+      // use the lower-cased version of the bucket name for its DNS name.  So for AWS S3, we will lowercase the bucket
+      // name prior to validation.  For all other regions than US Standard region, we will let AWS throw handle the
+      // error.
+      //
+      // http://code.google.com/p/jclouds/issues/detail?id=992
+      //
+      // It would be nice to scope this more lax validator to only the us regions, since based on AWS S3 documentation,
+      // this is only necessary for the us regions.
+      super.validate(containerName.toLowerCase());
+   }
+
+}
diff --git a/providers/aws-s3/src/test/java/org/jclouds/aws/s3/AWSS3AsyncClientTest.java b/providers/aws-s3/src/test/java/org/jclouds/aws/s3/AWSS3AsyncClientTest.java
index 6a2aa03..57c8407 100644
--- a/providers/aws-s3/src/test/java/org/jclouds/aws/s3/AWSS3AsyncClientTest.java
+++ b/providers/aws-s3/src/test/java/org/jclouds/aws/s3/AWSS3AsyncClientTest.java
@@ -19,10 +19,16 @@
 package org.jclouds.aws.s3;
 
 import java.io.IOException;
+import java.lang.reflect.Array;
 import java.lang.reflect.Method;
 import java.util.Map;
 import java.util.concurrent.ConcurrentMap;
 
+import com.google.common.base.Supplier;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+import com.google.inject.Module;
+import com.google.inject.TypeLiteral;
 import org.jclouds.aws.s3.config.AWSS3RestClientModule;
 import org.jclouds.aws.s3.functions.ETagFromHttpResponseViaRegex;
 import org.jclouds.aws.s3.functions.UploadIdFromHttpResponseViaRegex;
@@ -39,22 +45,18 @@
 import org.jclouds.rest.functions.MapHttp4xxCodesToExceptions;
 import org.jclouds.rest.functions.ReturnVoidOnNotFoundOr404;
 import org.jclouds.rest.internal.RestAnnotationProcessor;
+import org.jclouds.s3.S3AsyncClient;
 import org.jclouds.s3.S3AsyncClientTest;
 import org.jclouds.s3.domain.ObjectMetadata;
 import org.jclouds.s3.domain.ObjectMetadataBuilder;
 import org.jclouds.s3.domain.S3Object;
 import org.jclouds.s3.functions.ReturnFalseIfBucketAlreadyOwnedByYouOrIllegalState;
+import org.jclouds.s3.options.CopyObjectOptions;
 import org.jclouds.s3.options.PutBucketOptions;
 import org.jclouds.s3.options.PutObjectOptions;
 import org.jclouds.s3.xml.LocationConstraintHandler;
 import org.testng.annotations.Test;
 
-import com.google.common.base.Supplier;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Maps;
-import com.google.inject.Module;
-import com.google.inject.TypeLiteral;
-
 /**
  * @author Adrian Cole
  */
@@ -63,6 +65,16 @@
 @Test(groups = "unit", testName = "AWSS3AsyncClientTest")
 public class AWSS3AsyncClientTest extends S3AsyncClientTest<AWSS3AsyncClient> {
 
+   @Override
+   public void testCopyObjectInvalidName() throws ArrayIndexOutOfBoundsException, SecurityException,
+                                                  IllegalArgumentException, NoSuchMethodException, IOException {
+      // For AWS S3, S3AsyncClientTest#testCopyObjectInvalidName() will not throw an exception
+      Method method = S3AsyncClient.class.getMethod("copyObject", String.class, String.class, String.class,
+                                                    String.class,
+                                                    Array.newInstance(CopyObjectOptions.class, 0).getClass());
+      processor.createRequest(method, "sourceBucket", "sourceObject", "destinationBucket", "destinationObject");
+   }
+
    public void testGetBucketLocationEU() throws SecurityException, NoSuchMethodException, IOException {
       Method method = AWSS3AsyncClient.class.getMethod("getBucketLocation", String.class);
       HttpRequest request = processor.createRequest(method, "eubucket");
diff --git a/providers/aws-s3/src/test/java/org/jclouds/aws/s3/AWSS3ClientLiveTest.java b/providers/aws-s3/src/test/java/org/jclouds/aws/s3/AWSS3ClientLiveTest.java
index 61339bc..568a7f8 100644
--- a/providers/aws-s3/src/test/java/org/jclouds/aws/s3/AWSS3ClientLiveTest.java
+++ b/providers/aws-s3/src/test/java/org/jclouds/aws/s3/AWSS3ClientLiveTest.java
@@ -26,6 +26,8 @@
 import static org.jclouds.io.Payloads.newByteArrayPayload;
 import static org.jclouds.s3.options.ListBucketOptions.Builder.withPrefix;
 import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.fail;
 
 import java.io.ByteArrayInputStream;
 import java.io.File;
@@ -34,11 +36,15 @@
 import java.io.InputStream;
 import java.util.zip.GZIPInputStream;
 
+import org.jclouds.aws.AWSResponseException;
+import org.jclouds.aws.domain.Region;
 import org.jclouds.blobstore.AsyncBlobStore;
 import org.jclouds.blobstore.BlobStore;
 import org.jclouds.blobstore.KeyNotFoundException;
 import org.jclouds.blobstore.domain.Blob;
+import org.jclouds.blobstore.domain.StorageMetadata;
 import org.jclouds.blobstore.options.PutOptions;
+import org.jclouds.domain.Location;
 import org.jclouds.http.BaseJettyTest;
 import org.jclouds.io.Payload;
 import org.jclouds.s3.S3Client;
@@ -48,14 +54,16 @@
 import org.jclouds.s3.domain.ObjectMetadata.StorageClass;
 import org.jclouds.s3.domain.ObjectMetadataBuilder;
 import org.jclouds.s3.domain.S3Object;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.io.ByteStreams;
+import com.google.common.io.InputSupplier;
+
 import org.testng.ITestContext;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
-import com.google.common.collect.ImmutableMap;
-import com.google.common.io.ByteStreams;
-import com.google.common.io.InputSupplier;
-
 /**
  * Tests behavior of {@code S3Client}
  * 
@@ -197,4 +205,59 @@
          returnContainer(containerName);
       }
    }
+
+   /**
+    * http://code.google.com/p/jclouds/issues/detail?id=992
+    */
+   public void testUseBucketWithUpperCaseName() throws Exception {
+      String bucketName = CONTAINER_PREFIX + "-TestBucket";
+      String blobName = "TestBlob.txt";
+      StorageMetadata container = null;
+      BlobStore store = view.getBlobStore();
+
+      // Create and use a valid bucket name with uppercase characters in the bucket name (US regions only)
+      try {
+         store.createContainerInLocation(null, bucketName);
+
+         for (StorageMetadata metadata : store.list()) {
+            if (metadata.getName().equals(bucketName)) {
+               container = metadata;
+               break;
+            }
+         }
+
+         assertNotNull(container);
+
+         store.putBlob(bucketName, store.blobBuilder(blobName)
+                                          .payload("This is a test!")
+                                          .contentType("text/plain")
+                                          .build());
+
+         assertNotNull(store.getBlob(bucketName, blobName));
+      } finally {
+         if (container != null) {
+            store.deleteContainer(bucketName);
+         }
+      }
+
+      // Try to create the same bucket successfully created above in one of the non-US regions to ensure an error is
+      // encountered as expected.
+      Location location = null;
+
+      for (Location pLocation : store.listAssignableLocations()) {
+         if (!ImmutableSet.of(Region.US_STANDARD, Region.US_EAST_1, Region.US_WEST_1, Region.US_WEST_2)
+            .contains(pLocation.getId())) {
+            location = pLocation;
+            break;
+         }
+      }
+
+      try {
+         store.createContainerInLocation(location, bucketName);
+         fail("Should had failed because in non-US regions, mixed-case bucket names are invalid.");
+      } catch (AWSResponseException e) {
+         assertEquals("InvalidBucketName", e.getError().getCode());
+      }
+   }
+
 }
diff --git a/providers/gogrid/src/test/java/org/jclouds/gogrid/functions/ParseJobsFromJsonResponseTest.java b/providers/gogrid/src/test/java/org/jclouds/gogrid/functions/ParseJobsFromJsonResponseTest.java
index a5f5d7d..d725ee6 100644
--- a/providers/gogrid/src/test/java/org/jclouds/gogrid/functions/ParseJobsFromJsonResponseTest.java
+++ b/providers/gogrid/src/test/java/org/jclouds/gogrid/functions/ParseJobsFromJsonResponseTest.java
@@ -40,6 +40,7 @@
 import org.jclouds.json.config.GsonModule;
 import org.testng.annotations.Test;
 
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSortedSet;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Maps;
@@ -61,18 +62,17 @@
       ParseJobListFromJsonResponse parser = i.getInstance(ParseJobListFromJsonResponse.class);
       SortedSet<Job> response = parser.apply(new HttpResponse(200, "ok", Payloads.newInputStreamPayload(is)));
 
-      Map<String, String> details = Maps.newTreeMap();
-      details.put("description", null);
-      details.put("image", "GSI-f8979644-e646-4711-ad58-d98a5fa3612c");
-      details.put("ip", "204.51.240.189");
-      details.put("name", "ServerCreated40562");
-      details.put("type", "virtual_server");
+      Map<String, String> details = ImmutableMap.of(
+            "image", "GSI-f8979644-e646-4711-ad58-d98a5fa3612c",
+            "ip", "204.51.240.189",
+            "name", "ServerCreated40562",
+            "type", "virtual_server");
 
       Job job = new Job(250628L, new Option(7L, "DeleteVirtualServer", "Delete Virtual Server"),
             ObjectType.VIRTUAL_SERVER, new Date(1267404528895L), new Date(1267404538592L), JobState.SUCCEEDED, 1,
             "3116784158f0af2d-24076@api.gogrid.com", ImmutableSortedSet.of(new JobProperties(940263L, new Date(
-                  1267404528897L), JobState.CREATED, null), new JobProperties(940264L, new Date(1267404528967L),
-                  JobState.QUEUED, null)), details);
+            1267404528897L), JobState.CREATED, null), new JobProperties(940264L, new Date(1267404528967L),
+            JobState.QUEUED, null)), details);
       assertEquals(job, Iterables.getOnlyElement(response));
    }
 
diff --git a/providers/hpcloud-compute/src/main/java/org/jclouds/hpcloud/compute/HPCloudComputeProviderMetadata.java b/providers/hpcloud-compute/src/main/java/org/jclouds/hpcloud/compute/HPCloudComputeProviderMetadata.java
index 8863b9b..f29019e 100644
--- a/providers/hpcloud-compute/src/main/java/org/jclouds/hpcloud/compute/HPCloudComputeProviderMetadata.java
+++ b/providers/hpcloud-compute/src/main/java/org/jclouds/hpcloud/compute/HPCloudComputeProviderMetadata.java
@@ -30,7 +30,8 @@
 import java.util.Properties;
 
 import org.jclouds.hpcloud.compute.config.HPCloudComputeServiceContextModule;
-import org.jclouds.openstack.keystone.v2_0.config.KeystoneAuthenticationModule.KeystoneAuthenticationModuleForZones;
+import org.jclouds.openstack.keystone.v2_0.config.KeystoneAuthenticationModule;
+import org.jclouds.openstack.keystone.v2_0.config.KeystoneAuthenticationModule.ZoneModule;
 import org.jclouds.openstack.nova.v2_0.NovaApiMetadata;
 import org.jclouds.openstack.nova.v2_0.config.NovaRestClientModule;
 import org.jclouds.providers.ProviderMetadata;
@@ -85,9 +86,13 @@
          id("hpcloud-compute")
          .name("HP Cloud Compute Services")
          .apiMetadata(new NovaApiMetadata().toBuilder()
-                  .identityName("tenantName:accessKey or accessKey")
+                  .identityName("yourTenantName:yourAccessKey")
                   .credentialName("secretKey")
-                  .defaultModules(ImmutableSet.<Class<? extends Module>>of(KeystoneAuthenticationModuleForZones.class,NovaRestClientModule.class, HPCloudComputeServiceContextModule.class))
+                  .defaultModules(ImmutableSet.<Class<? extends Module>>builder()
+                                              .add(KeystoneAuthenticationModule.class)
+                                              .add(ZoneModule.class)
+                                              .add(NovaRestClientModule.class)
+                                              .add(HPCloudComputeServiceContextModule.class).build())
                   .build())
          .homepage(URI.create("http://hpcloud.com"))
          .console(URI.create("https://manage.hpcloud.com/compute"))
diff --git a/providers/hpcloud-objectstorage/src/main/java/org/jclouds/hpcloud/objectstorage/HPCloudObjectStorageApiMetadata.java b/providers/hpcloud-objectstorage/src/main/java/org/jclouds/hpcloud/objectstorage/HPCloudObjectStorageApiMetadata.java
index ea31832..465b780 100644
--- a/providers/hpcloud-objectstorage/src/main/java/org/jclouds/hpcloud/objectstorage/HPCloudObjectStorageApiMetadata.java
+++ b/providers/hpcloud-objectstorage/src/main/java/org/jclouds/hpcloud/objectstorage/HPCloudObjectStorageApiMetadata.java
@@ -29,6 +29,7 @@
 import org.jclouds.hpcloud.objectstorage.blobstore.config.HPCloudObjectStorageBlobStoreContextModule;
 import org.jclouds.hpcloud.objectstorage.config.HPCloudObjectStorageRestClientModule;
 import org.jclouds.openstack.keystone.v2_0.config.CredentialTypes;
+import org.jclouds.openstack.keystone.v2_0.config.KeystoneAuthenticationModule.RegionModule;
 import org.jclouds.openstack.keystone.v2_0.config.KeystoneProperties;
 import org.jclouds.openstack.services.ServiceType;
 import org.jclouds.openstack.swift.SwiftApiMetadata;
@@ -84,12 +85,16 @@
          super(HPCloudObjectStorageClient.class, HPCloudObjectStorageAsyncClient.class);
          id("hpcloud-objectstorage")
          .name("HP Cloud Services Object Storage API")
-         .identityName("tenantName:accessKey or accessKey")
+         .identityName("yourTenantName:yourAccessKey")
          .credentialName("secretKey")
          .documentation(URI.create("https://build.hpcloud.com/object-storage/api"))
          .defaultProperties(HPCloudObjectStorageApiMetadata.defaultProperties())
          .context(CONTEXT_TOKEN)
-         .defaultModules(ImmutableSet.<Class<? extends Module>>of(KeystoneStorageEndpointModule.class, HPCloudObjectStorageRestClientModule.class, HPCloudObjectStorageBlobStoreContextModule.class));
+         .defaultModules(ImmutableSet.<Class<? extends Module>>builder()
+                                     .add(KeystoneStorageEndpointModule.class)
+                                     .add(RegionModule.class)
+                                     .add(HPCloudObjectStorageRestClientModule.class)
+                                     .add(HPCloudObjectStorageBlobStoreContextModule.class).build());
       }
 
       @Override
diff --git a/providers/hpcloud-objectstorage/src/main/java/org/jclouds/hpcloud/objectstorage/config/HPCloudObjectStorageRestClientModule.java b/providers/hpcloud-objectstorage/src/main/java/org/jclouds/hpcloud/objectstorage/config/HPCloudObjectStorageRestClientModule.java
index cf64278..38fc722 100644
--- a/providers/hpcloud-objectstorage/src/main/java/org/jclouds/hpcloud/objectstorage/config/HPCloudObjectStorageRestClientModule.java
+++ b/providers/hpcloud-objectstorage/src/main/java/org/jclouds/hpcloud/objectstorage/config/HPCloudObjectStorageRestClientModule.java
@@ -63,7 +63,7 @@
       bind(CommonSwiftClient.class).to(HPCloudObjectStorageClient.class).in(Scopes.SINGLETON);
       bind(CommonSwiftAsyncClient.class).to(HPCloudObjectStorageAsyncClient.class).in(Scopes.SINGLETON);
    }
-
+   
    @Provides
    @Singleton
    @HPExtensionCDN
diff --git a/providers/pom.xml b/providers/pom.xml
index 88c4d0c..85723f0 100644
--- a/providers/pom.xml
+++ b/providers/pom.xml
@@ -71,5 +71,6 @@
         <module>hpcloud-compute</module>
         <module>hpcloud-objectstorage</module>
         <module>trystack-nova</module>
+        <module>rackspace-cloudservers-us</module>
     </modules>
 </project>
diff --git a/labs/rackspace-cloudservers-us/pom.xml b/providers/rackspace-cloudservers-us/pom.xml
similarity index 97%
rename from labs/rackspace-cloudservers-us/pom.xml
rename to providers/rackspace-cloudservers-us/pom.xml
index ac966aa..41dd0f3 100644
--- a/labs/rackspace-cloudservers-us/pom.xml
+++ b/providers/rackspace-cloudservers-us/pom.xml
@@ -27,7 +27,7 @@
     <version>1.5.0-SNAPSHOT</version>
     <relativePath>../../project/pom.xml</relativePath>
   </parent>
-  <groupId>org.jclouds.labs</groupId>
+  <groupId>org.jclouds.provider</groupId>
   <artifactId>rackspace-cloudservers-us</artifactId>
   <name>jclouds Rackspace Next Generation Cloud Servers provider</name>
   <description>OpenStack Nova implementation targeted to Rackspace Next Generation Cloud Servers</description>
@@ -51,7 +51,7 @@
 
   <dependencies>
     <dependency>
-      <groupId>org.jclouds.labs</groupId>
+      <groupId>org.jclouds.api</groupId>
       <artifactId>rackspace-cloudidentity</artifactId>
       <version>${project.version}</version>
     </dependency>
@@ -75,7 +75,7 @@
       <scope>test</scope>
     </dependency>
     <dependency>
-      <groupId>org.jclouds.labs</groupId>
+      <groupId>org.jclouds.api</groupId>
       <artifactId>rackspace-cloudidentity</artifactId>
       <version>${project.version}</version>
       <type>test-jar</type>
diff --git a/labs/rackspace-cloudservers-us/src/main/java/org/jclouds/rackspace/cloudservers/us/CloudServersUSProviderMetadata.java b/providers/rackspace-cloudservers-us/src/main/java/org/jclouds/rackspace/cloudservers/us/CloudServersUSProviderMetadata.java
similarity index 86%
rename from labs/rackspace-cloudservers-us/src/main/java/org/jclouds/rackspace/cloudservers/us/CloudServersUSProviderMetadata.java
rename to providers/rackspace-cloudservers-us/src/main/java/org/jclouds/rackspace/cloudservers/us/CloudServersUSProviderMetadata.java
index 0d872e4..4096ff8 100644
--- a/labs/rackspace-cloudservers-us/src/main/java/org/jclouds/rackspace/cloudservers/us/CloudServersUSProviderMetadata.java
+++ b/providers/rackspace-cloudservers-us/src/main/java/org/jclouds/rackspace/cloudservers/us/CloudServersUSProviderMetadata.java
@@ -18,19 +18,19 @@
  */
 package org.jclouds.rackspace.cloudservers.us;
 
-import static org.jclouds.compute.config.ComputeServiceProperties.TEMPLATE;
 import static org.jclouds.openstack.keystone.v2_0.config.KeystoneProperties.CREDENTIAL_TYPE;
 
 import java.net.URI;
 import java.util.Properties;
 
+import org.jclouds.openstack.keystone.v2_0.config.KeystoneAuthenticationModule.ZoneModule;
 import org.jclouds.openstack.nova.v2_0.NovaApiMetadata;
-import org.jclouds.openstack.nova.v2_0.compute.config.NovaComputeServiceContextModule;
 import org.jclouds.openstack.nova.v2_0.config.NovaRestClientModule;
 import org.jclouds.providers.ProviderMetadata;
 import org.jclouds.providers.internal.BaseProviderMetadata;
+import org.jclouds.rackspace.cloudidentity.v2_0.config.CloudIdentityAuthenticationModule;
 import org.jclouds.rackspace.cloudidentity.v2_0.config.CloudIdentityCredentialTypes;
-import org.jclouds.rackspace.cloudidentity.v2_0.config.CloudIdentityAuthenticationModule.CloudIdentityAuthenticationModuleForZones;
+import org.jclouds.rackspace.cloudservers.us.config.CloudServersUSComputeServiceContextModule;
 
 import com.google.common.collect.ImmutableSet;
 import com.google.inject.Module;
@@ -65,8 +65,6 @@
    public static Properties defaultProperties() {
       Properties properties = new Properties();
       properties.setProperty(CREDENTIAL_TYPE, CloudIdentityCredentialTypes.API_KEY_CREDENTIALS);
-      properties.setProperty(TEMPLATE, "imageId=DFW/3afe97b2-26dc-49c5-a2cc-a2fc8d80c001,loginUser=root");
-
       return properties;
    }
    
@@ -80,7 +78,11 @@
                   .credentialName("API Key")
                   .version("2")
                   .documentation(URI.create("http://docs.rackspace.com/servers/api/v2/cs-devguide/content/ch_preface.html#webhelp-currentid"))
-                  .defaultModules(ImmutableSet.<Class<? extends Module>>of(CloudIdentityAuthenticationModuleForZones.class, NovaRestClientModule.class, NovaComputeServiceContextModule.class))
+                  .defaultModules(ImmutableSet.<Class<? extends Module>>builder()
+                                              .add(CloudIdentityAuthenticationModule.class)
+                                              .add(ZoneModule.class)
+                                              .add(NovaRestClientModule.class)
+                                              .add(CloudServersUSComputeServiceContextModule.class).build())
                   .build())
          .homepage(URI.create("http://www.rackspace.com/cloud/nextgen"))
          .console(URI.create("https://mycloud.rackspace.com"))
diff --git a/providers/rackspace-cloudservers-us/src/main/java/org/jclouds/rackspace/cloudservers/us/config/CloudServersUSComputeServiceContextModule.java b/providers/rackspace-cloudservers-us/src/main/java/org/jclouds/rackspace/cloudservers/us/config/CloudServersUSComputeServiceContextModule.java
new file mode 100644
index 0000000..c284337
--- /dev/null
+++ b/providers/rackspace-cloudservers-us/src/main/java/org/jclouds/rackspace/cloudservers/us/config/CloudServersUSComputeServiceContextModule.java
@@ -0,0 +1,45 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.rackspace.cloudservers.us.config;
+
+import java.util.Map;
+
+import org.jclouds.compute.domain.OsFamily;
+import org.jclouds.domain.LoginCredentials;
+import org.jclouds.openstack.nova.v2_0.compute.config.NovaComputeServiceContextModule;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.inject.Injector;
+
+/**
+ * 
+ * @author Adrian Cole
+ */
+public class CloudServersUSComputeServiceContextModule extends NovaComputeServiceContextModule {
+
+   /**
+    * CloudServers images are accessible via the root user, not ubuntu
+    */
+   @Override
+   protected Map<OsFamily, LoginCredentials> osFamilyToCredentials(Injector injector) {
+      return ImmutableMap.of(OsFamily.WINDOWS, LoginCredentials.builder().user("Administrator").build(),
+               OsFamily.UBUNTU, LoginCredentials.builder().user("root").build());
+   }
+
+}
\ No newline at end of file
diff --git a/labs/rackspace-cloudservers-us/src/main/resources/META-INF/services/org.jclouds.providers.ProviderMetadata b/providers/rackspace-cloudservers-us/src/main/resources/META-INF/services/org.jclouds.providers.ProviderMetadata
similarity index 100%
rename from labs/rackspace-cloudservers-us/src/main/resources/META-INF/services/org.jclouds.providers.ProviderMetadata
rename to providers/rackspace-cloudservers-us/src/main/resources/META-INF/services/org.jclouds.providers.ProviderMetadata
diff --git a/labs/rackspace-cloudservers-us/src/test/java/org/jclouds/rackspace/cloudservers/us/CloudServersUSProviderTest.java b/providers/rackspace-cloudservers-us/src/test/java/org/jclouds/rackspace/cloudservers/us/CloudServersUSProviderTest.java
similarity index 100%
rename from labs/rackspace-cloudservers-us/src/test/java/org/jclouds/rackspace/cloudservers/us/CloudServersUSProviderTest.java
rename to providers/rackspace-cloudservers-us/src/test/java/org/jclouds/rackspace/cloudservers/us/CloudServersUSProviderTest.java
diff --git a/labs/rackspace-cloudservers-us/src/test/java/org/jclouds/rackspace/cloudservers/us/compute/CloudServersUSComputeServiceLiveTest.java b/providers/rackspace-cloudservers-us/src/test/java/org/jclouds/rackspace/cloudservers/us/compute/CloudServersUSComputeServiceLiveTest.java
similarity index 100%
rename from labs/rackspace-cloudservers-us/src/test/java/org/jclouds/rackspace/cloudservers/us/compute/CloudServersUSComputeServiceLiveTest.java
rename to providers/rackspace-cloudservers-us/src/test/java/org/jclouds/rackspace/cloudservers/us/compute/CloudServersUSComputeServiceLiveTest.java
diff --git a/labs/rackspace-cloudservers-us/src/test/java/org/jclouds/rackspace/cloudservers/us/compute/CloudServersUSTemplateBuilderLiveTest.java b/providers/rackspace-cloudservers-us/src/test/java/org/jclouds/rackspace/cloudservers/us/compute/CloudServersUSTemplateBuilderLiveTest.java
similarity index 90%
rename from labs/rackspace-cloudservers-us/src/test/java/org/jclouds/rackspace/cloudservers/us/compute/CloudServersUSTemplateBuilderLiveTest.java
rename to providers/rackspace-cloudservers-us/src/test/java/org/jclouds/rackspace/cloudservers/us/compute/CloudServersUSTemplateBuilderLiveTest.java
index f60ce66..a200418 100644
--- a/labs/rackspace-cloudservers-us/src/test/java/org/jclouds/rackspace/cloudservers/us/compute/CloudServersUSTemplateBuilderLiveTest.java
+++ b/providers/rackspace-cloudservers-us/src/test/java/org/jclouds/rackspace/cloudservers/us/compute/CloudServersUSTemplateBuilderLiveTest.java
@@ -53,14 +53,16 @@
          public boolean apply(OsFamilyVersion64Bit input) {
             switch (input.family) {
                case UBUNTU:
-                  return (input.version.equals("") || (input.version.matches("^1[01].*") && !input.version
+                  return (input.version.equals("") || (input.version.matches("^1[012].*") && !input.version
                            .equals("10.10")))
                            && input.is64Bit;
                case DEBIAN:
                   return input.is64Bit && !input.version.equals("5.0");
                case CENTOS:
-                  return (input.version.equals("") || input.version.equals("5.6") || input.version.equals("6.0"))
+                  return (input.version.equals("") || input.version.equals("5.0")|| input.version.equals("5.6") || input.version.equals("6.0"))
                            && input.is64Bit;
+               case WINDOWS:
+                  return input.is64Bit && input.version.equals("");
                default:
                   return false;
             }
@@ -73,15 +75,15 @@
    public void testTemplateBuilder() {
       Template defaultTemplate = this.view.getComputeService().templateBuilder().build();
       assertEquals(defaultTemplate.getImage().getOperatingSystem().is64Bit(), true);
-      assertEquals(defaultTemplate.getImage().getOperatingSystem().getVersion(), "11.10");
+      assertEquals(defaultTemplate.getImage().getOperatingSystem().getVersion(), "12.04");
       assertEquals(defaultTemplate.getImage().getOperatingSystem().getFamily(), OsFamily.UBUNTU);
-      assertEquals(defaultTemplate.getImage().getName(), "Ubuntu 11.10");
+      assertEquals(defaultTemplate.getImage().getName(), "Ubuntu 12.04 LTS");
       assertEquals(defaultTemplate.getImage().getDefaultCredentials().getUser(), "root");
       assertEquals(defaultTemplate.getLocation().getId(), "DFW");
       assertEquals(defaultTemplate.getImage().getLocation().getId(), "DFW");
       assertEquals(defaultTemplate.getHardware().getLocation().getId(), "DFW");
-      assertEquals(defaultTemplate.getOptions().as(NovaTemplateOptions.class).shouldAutoAssignFloatingIp(), true);
-      assertEquals(getCores(defaultTemplate.getHardware()), 4.0d);
+      assertEquals(defaultTemplate.getOptions().as(NovaTemplateOptions.class).shouldAutoAssignFloatingIp(), false);
+      assertEquals(getCores(defaultTemplate.getHardware()), 1.0d);
    }
 
    @Override
diff --git a/labs/rackspace-cloudservers-us/src/test/java/org/jclouds/rackspace/cloudservers/us/compute/extensions/CloudServersUSImageExtensionLivetest.java b/providers/rackspace-cloudservers-us/src/test/java/org/jclouds/rackspace/cloudservers/us/compute/extensions/CloudServersUSImageExtensionLivetest.java
similarity index 100%
rename from labs/rackspace-cloudservers-us/src/test/java/org/jclouds/rackspace/cloudservers/us/compute/extensions/CloudServersUSImageExtensionLivetest.java
rename to providers/rackspace-cloudservers-us/src/test/java/org/jclouds/rackspace/cloudservers/us/compute/extensions/CloudServersUSImageExtensionLivetest.java
diff --git a/labs/rackspace-cloudservers-us/src/test/java/org/jclouds/rackspace/cloudservers/us/features/CloudServersUSFlavorClientLiveTest.java b/providers/rackspace-cloudservers-us/src/test/java/org/jclouds/rackspace/cloudservers/us/features/CloudServersUSFlavorClientLiveTest.java
similarity index 100%
rename from labs/rackspace-cloudservers-us/src/test/java/org/jclouds/rackspace/cloudservers/us/features/CloudServersUSFlavorClientLiveTest.java
rename to providers/rackspace-cloudservers-us/src/test/java/org/jclouds/rackspace/cloudservers/us/features/CloudServersUSFlavorClientLiveTest.java
diff --git a/labs/rackspace-cloudservers-us/src/test/java/org/jclouds/rackspace/cloudservers/us/features/CloudServersUSImageClientLiveTest.java b/providers/rackspace-cloudservers-us/src/test/java/org/jclouds/rackspace/cloudservers/us/features/CloudServersUSImageClientLiveTest.java
similarity index 100%
rename from labs/rackspace-cloudservers-us/src/test/java/org/jclouds/rackspace/cloudservers/us/features/CloudServersUSImageClientLiveTest.java
rename to providers/rackspace-cloudservers-us/src/test/java/org/jclouds/rackspace/cloudservers/us/features/CloudServersUSImageClientLiveTest.java
diff --git a/labs/rackspace-cloudservers-us/src/test/java/org/jclouds/rackspace/cloudservers/us/features/CloudServersUSServerClientLiveTest.java b/providers/rackspace-cloudservers-us/src/test/java/org/jclouds/rackspace/cloudservers/us/features/CloudServersUSServerClientLiveTest.java
similarity index 100%
rename from labs/rackspace-cloudservers-us/src/test/java/org/jclouds/rackspace/cloudservers/us/features/CloudServersUSServerClientLiveTest.java
rename to providers/rackspace-cloudservers-us/src/test/java/org/jclouds/rackspace/cloudservers/us/features/CloudServersUSServerClientLiveTest.java
diff --git a/labs/rackspace-cloudservers-us/src/test/resources/raxImages.json b/providers/rackspace-cloudservers-us/src/test/resources/raxImages.json
similarity index 100%
rename from labs/rackspace-cloudservers-us/src/test/resources/raxImages.json
rename to providers/rackspace-cloudservers-us/src/test/resources/raxImages.json
diff --git a/providers/softlayer/src/main/java/org/jclouds/softlayer/compute/config/SoftLayerComputeServiceContextModule.java b/providers/softlayer/src/main/java/org/jclouds/softlayer/compute/config/SoftLayerComputeServiceContextModule.java
index 2726f0b..0c7f805 100644
--- a/providers/softlayer/src/main/java/org/jclouds/softlayer/compute/config/SoftLayerComputeServiceContextModule.java
+++ b/providers/softlayer/src/main/java/org/jclouds/softlayer/compute/config/SoftLayerComputeServiceContextModule.java
@@ -25,6 +25,7 @@
 import static org.jclouds.softlayer.reference.SoftLayerConstants.PROPERTY_SOFTLAYER_VIRTUALGUEST_PACKAGE_NAME;
 import static org.jclouds.softlayer.reference.SoftLayerConstants.PROPERTY_SOFTLAYER_VIRTUALGUEST_PRICES;
 
+import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicReference;
 
 import javax.inject.Named;
@@ -54,6 +55,7 @@
 import org.jclouds.softlayer.features.ProductPackageClient;
 
 import com.google.common.base.Function;
+import com.google.common.base.Objects;
 import com.google.common.base.Splitter;
 import com.google.common.base.Supplier;
 import com.google.common.collect.Iterables;
@@ -95,7 +97,7 @@
    public Supplier<ProductPackage> getProductPackage(AtomicReference<AuthorizationException> authException,
             @Named(PROPERTY_SESSION_INTERVAL) long seconds, final SoftLayerClient client,
             @Named(PROPERTY_SOFTLAYER_VIRTUALGUEST_PACKAGE_NAME) final String virtualGuestPackageName) {
-      return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier<ProductPackage>(authException, seconds,
+      return MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(authException,
                new Supplier<ProductPackage>() {
                   @Override
                   public ProductPackage get() {
@@ -104,7 +106,13 @@
                      ProductPackage p = find(accountClient.getActivePackages(), named(virtualGuestPackageName));
                      return productPackageClient.getProductPackage(p.getId());
                   }
-               });
+                  
+                  @Override
+                  public String toString() {
+                     return Objects.toStringHelper(client).add("method", "accountClient.getActivePackages")
+                                                          .add("method", "productPackageClient.getProductPackage").toString();
+                  }
+               }, seconds, TimeUnit.SECONDS);
    }
 
    // TODO: check the prices really do exist
diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/ssh/SshdConfig.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/ssh/SshdConfig.java
index 2264f89..ec89d8f 100644
--- a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/ssh/SshdConfig.java
+++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/ssh/SshdConfig.java
@@ -49,7 +49,7 @@
       Statement prependSshdConfig = exec(String.format(
                "exec 3<> %1$s && awk -v TEXT=\"%2$s\n\" 'BEGIN {print TEXT}{print}' %1$s >&3", sshdConfig,
                linesToPrepend));
-      Statement reloadSshdConfig = exec("hash service 2>/dev/null && service ssh reload || /etc/init.d/ssh* reload");
+      Statement reloadSshdConfig = exec("hash service 2>&- && service ssh reload 2>&- || /etc/init.d/ssh* reload");
       return newStatementList(prependSshdConfig, reloadSshdConfig).render(family);
    }
 
diff --git a/scriptbuilder/src/main/resources/functions/installOpenJDK.sh b/scriptbuilder/src/main/resources/functions/installOpenJDK.sh
index 5634d5c..fd32aca 100644
--- a/scriptbuilder/src/main/resources/functions/installOpenJDK.sh
+++ b/scriptbuilder/src/main/resources/functions/installOpenJDK.sh
@@ -1,36 +1,53 @@
+# adds JAVA_HOME and into PATH in global and user-specific profiles
 function setupJavaHomeInProfile() {
-  test -n \"$SUDO_USER\" && cat >> `getent passwd $SUDO_USER| cut -f6 -d:`/.bashrc <<-'END_OF_JCLOUDS_FILE'
+  PROFILES=(/etc/bashrc $HOME/.bashrc /etc/skel/.bashrc)
+  test -n "$SUDO_USER" &&
+    PROFILES=(${PROFILES[*]} `getent passwd $SUDO_USER| cut -f6 -d:`/.bashrc)
+  for PROFILE in ${PROFILES[*]}; do
+    cat >> $PROFILE <<-'END_OF_JCLOUDS_FILE'
 	export JAVA_HOME=/usr/local/jdk
 	export PATH=$JAVA_HOME/bin:$PATH
 END_OF_JCLOUDS_FILE
-  cat >> /etc/bashrc <<-'END_OF_JCLOUDS_FILE'
-	export JAVA_HOME=/usr/local/jdk
-	export PATH=$JAVA_HOME/bin:$PATH
-END_OF_JCLOUDS_FILE
-  cat >> $HOME/.bashrc <<-'END_OF_JCLOUDS_FILE'
-	export JAVA_HOME=/usr/local/jdk
-	export PATH=$JAVA_HOME/bin:$PATH
-END_OF_JCLOUDS_FILE
-  cat >> /etc/skel/.bashrc <<-'END_OF_JCLOUDS_FILE'
-	export JAVA_HOME=/usr/local/jdk
-	export PATH=$JAVA_HOME/bin:$PATH
-END_OF_JCLOUDS_FILE
+  done
 }
 
+# resets JAVA_HOME to what an openjdk installer created
+function findOpenJDK() {
+  local oldJavaHome=$JAVA_HOME
+  unset JAVA_HOME
+  for CANDIDATE in $oldJavaHome `ls -d /usr/lib/jvm/java-1.6.0-openjdk-* /usr/lib/jvm/java-6-openjdk-* /usr/lib/jvm/java-6-openjdk 2>&-`; do
+    if [ -n "$CANDIDATE" -a -x "$CANDIDATE/bin/java" ]; then
+      export JAVA_HOME=$CANDIDATE
+      break
+    fi
+  done
+}
+
+# assures JDK installed and JAVA_HOME to a link at /usr/local/jdk
 function installOpenJDK() {
-  if hash apt-get 2>/dev/null; then
-    export JAVA_HOME=${JAVA_HOME:-/usr/lib/jvm/java-6-openjdk}
-    test -d $JAVA_HOME || ( apt-get-update && apt-get-install openjdk-6-jdk )
-  elif hash yum 2>/dev/null; then
-    export pkg=java-1.6.0-openjdk-devel
-    yum --nogpgcheck -y install $pkg &&
-    export JAVA_HOME=`ls -d /usr/lib/jvm/java-1.6.0-openjdk-*`
-  else
-    abort "we only support apt-get and yum right now... please contribute!"
-    return 1
+  if [ "$JAVA_HOME" == "/usr/local/jdk" ]; then
+    echo skipping as JAVA_HOME is already set to /usr/local/jdk
+    return 0
   fi
-  test -n "$JAVA_HOME" || abort "JDK installation failed!"
-  ln -Fs $JAVA_HOME /usr/local/jdk 
+  if [ -n "$JAVA_HOME" -a -x "$JAVA_HOME/bin/java" ]; then
+    echo reusing JAVA_HOME $JAVA_HOME
+  else
+    if hash apt-get 2>/dev/null; then
+      apt-get-update && apt-get-install openjdk-6-jdk
+    elif hash yum 2>/dev/null; then
+      yum-install java-1.6.0-openjdk-devel
+    else
+      abort "we only support apt-get and yum right now... please contribute"
+    fi
+    findOpenJDK
+    if [ -n "$JAVA_HOME" ]; then
+      echo installed JAVA_HOME $JAVA_HOME
+    else
+      abort "JDK installation failed"
+    fi
+  fi
+  rm -rf /usr/local/jdk
+  ln -Fs $JAVA_HOME /usr/local/jdk
   /usr/local/jdk/bin/java -version || abort "cannot run java"
   setupJavaHomeInProfile
 }
\ No newline at end of file
diff --git a/scriptbuilder/src/main/resources/functions/setupPublicCurl.sh b/scriptbuilder/src/main/resources/functions/setupPublicCurl.sh
index 25a6c6d..60b1b54 100644
--- a/scriptbuilder/src/main/resources/functions/setupPublicCurl.sh
+++ b/scriptbuilder/src/main/resources/functions/setupPublicCurl.sh
@@ -1,5 +1,6 @@
-alias apt-get-install="apt-get install -f -y -qq --force-yes"
 alias apt-get-update="apt-get update -qq"
+alias apt-get-install="apt-get install -f -y -qq --force-yes"
+alias yum-install="yum --quiet --nogpgcheck -y install"
 
 function ensure_cmd_or_install_package_apt(){
   local cmd=$1
@@ -11,7 +12,7 @@
 function ensure_cmd_or_install_package_yum(){
   local cmd=$1
   local pkg=$2
-  hash $cmd 2>/dev/null || yum --nogpgcheck -y install $pkg
+  hash $cmd 2>/dev/null || yum-install $pkg
 }
 
 function ensure_netutils_apt() {
diff --git a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/login/AdminAccessTest.java b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/login/AdminAccessTest.java
index 2aad925..5c95ed3 100644
--- a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/login/AdminAccessTest.java
+++ b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/login/AdminAccessTest.java
@@ -33,7 +33,7 @@
 /**
  * @author Adrian Cole
  */
-@Test(groups = "unit", singleThreaded = true)
+@Test(groups = "unit", singleThreaded = true, testName = "AdminAccessTest")
 public class AdminAccessTest {
 
    public void testStandardUNIX() throws IOException {
diff --git a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/ssh/SshStatementsTest.java b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/ssh/SshStatementsTest.java
index 7e2ddb9..3a3be55 100644
--- a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/ssh/SshStatementsTest.java
+++ b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/ssh/SshStatementsTest.java
@@ -37,7 +37,7 @@
                .append("PasswordAuthentication no").append("\n")//
                .append("PermitRootLogin no").append("\n")//
                .append("\" 'BEGIN {print TEXT}{print}' /etc/ssh/sshd_config >&3").append("\n")//
-               .append("hash service 2>/dev/null && service ssh reload || /etc/init.d/ssh* reload").append("\n").toString());
+               .append("hash service 2>&- && service ssh reload 2>&- || /etc/init.d/ssh* reload").append("\n").toString());
    }
 
    public void testSshdConfigUNIX() {
@@ -45,7 +45,7 @@
                new StringBuilder().append("exec 3<> /etc/ssh/sshd_config && awk -v TEXT=\"")//
                         .append("AddressFamily inet6").append("\n")//
                         .append("\" 'BEGIN {print TEXT}{print}' /etc/ssh/sshd_config >&3").append("\n")//
-                        .append("hash service 2>/dev/null && service ssh reload || /etc/init.d/ssh* reload").append("\n").toString());
+                        .append("hash service 2>&- && service ssh reload 2>&- || /etc/init.d/ssh* reload").append("\n").toString());
    }
 
 }
diff --git a/scriptbuilder/src/test/resources/test_adminaccess_params.sh b/scriptbuilder/src/test/resources/test_adminaccess_params.sh
index 3c33878..2eed6fa 100644
--- a/scriptbuilder/src/test/resources/test_adminaccess_params.sh
+++ b/scriptbuilder/src/test/resources/test_adminaccess_params.sh
@@ -15,6 +15,6 @@
 exec 3<> /etc/ssh/sshd_config && awk -v TEXT="PasswordAuthentication no
 PermitRootLogin no
 " 'BEGIN {print TEXT}{print}' /etc/ssh/sshd_config >&3
-hash service 2>/dev/null && service ssh reload || /etc/init.d/ssh* reload
+hash service 2>&- && service ssh reload 2>&- || /etc/init.d/ssh* reload
 awk -v user=^${SUDO_USER:=${USER}}: -v password='crypt(0)' 'BEGIN { FS=OFS=":" } $0 ~ user { $2 = password } 1' /etc/shadow >/etc/shadow.${SUDO_USER:=${USER}}
 test -f /etc/shadow.${SUDO_USER:=${USER}} && mv /etc/shadow.${SUDO_USER:=${USER}} /etc/shadow
diff --git a/scriptbuilder/src/test/resources/test_adminaccess_standard.sh b/scriptbuilder/src/test/resources/test_adminaccess_standard.sh
index 28d1849..b567afc 100644
--- a/scriptbuilder/src/test/resources/test_adminaccess_standard.sh
+++ b/scriptbuilder/src/test/resources/test_adminaccess_standard.sh
@@ -15,6 +15,6 @@
 exec 3<> /etc/ssh/sshd_config && awk -v TEXT="PasswordAuthentication no
 PermitRootLogin no
 " 'BEGIN {print TEXT}{print}' /etc/ssh/sshd_config >&3
-hash service 2>/dev/null && service ssh reload || /etc/init.d/ssh* reload
+hash service 2>&- && service ssh reload 2>&- || /etc/init.d/ssh* reload
 awk -v user=^${SUDO_USER:=${USER}}: -v password='crypt(1)' 'BEGIN { FS=OFS=":" } $0 ~ user { $2 = password } 1' /etc/shadow >/etc/shadow.${SUDO_USER:=${USER}}
 test -f /etc/shadow.${SUDO_USER:=${USER}} && mv /etc/shadow.${SUDO_USER:=${USER}} /etc/shadow
diff --git a/scriptbuilder/src/test/resources/test_install_jdk_scriptbuilder.sh b/scriptbuilder/src/test/resources/test_install_jdk_scriptbuilder.sh
index 5a3efe0..e3d1872 100644
--- a/scriptbuilder/src/test/resources/test_install_jdk_scriptbuilder.sh
+++ b/scriptbuilder/src/test/resources/test_install_jdk_scriptbuilder.sh
@@ -83,8 +83,9 @@
    echo "aborting: $@" 1>&2
    exit 1
 }
-alias apt-get-install="apt-get install -f -y -qq --force-yes"
 alias apt-get-update="apt-get update -qq"
+alias apt-get-install="apt-get install -f -y -qq --force-yes"
+alias yum-install="yum --quiet --nogpgcheck -y install"
 
 function ensure_cmd_or_install_package_apt(){
   local cmd=$1
@@ -96,7 +97,7 @@
 function ensure_cmd_or_install_package_yum(){
   local cmd=$1
   local pkg=$2
-  hash $cmd 2>/dev/null || yum --nogpgcheck -y install $pkg
+  hash $cmd 2>/dev/null || yum-install $pkg
 }
 
 function ensure_netutils_apt() {
@@ -133,39 +134,56 @@
   ensure_can_resolve_public_dns
   return 0  
 }
+# adds JAVA_HOME and into PATH in global and user-specific profiles
 function setupJavaHomeInProfile() {
-  test -n \"$SUDO_USER\" && cat >> `getent passwd $SUDO_USER| cut -f6 -d:`/.bashrc <<-'END_OF_JCLOUDS_FILE'
+  PROFILES=(/etc/bashrc $HOME/.bashrc /etc/skel/.bashrc)
+  test -n "$SUDO_USER" &&
+    PROFILES=(${PROFILES[*]} `getent passwd $SUDO_USER| cut -f6 -d:`/.bashrc)
+  for PROFILE in ${PROFILES[*]}; do
+    cat >> $PROFILE <<-'END_OF_JCLOUDS_FILE'
 	export JAVA_HOME=/usr/local/jdk
 	export PATH=$JAVA_HOME/bin:$PATH
 END_OF_JCLOUDS_FILE
-  cat >> /etc/bashrc <<-'END_OF_JCLOUDS_FILE'
-	export JAVA_HOME=/usr/local/jdk
-	export PATH=$JAVA_HOME/bin:$PATH
-END_OF_JCLOUDS_FILE
-  cat >> $HOME/.bashrc <<-'END_OF_JCLOUDS_FILE'
-	export JAVA_HOME=/usr/local/jdk
-	export PATH=$JAVA_HOME/bin:$PATH
-END_OF_JCLOUDS_FILE
-  cat >> /etc/skel/.bashrc <<-'END_OF_JCLOUDS_FILE'
-	export JAVA_HOME=/usr/local/jdk
-	export PATH=$JAVA_HOME/bin:$PATH
-END_OF_JCLOUDS_FILE
+  done
 }
 
+# resets JAVA_HOME to what an openjdk installer created
+function findOpenJDK() {
+  local oldJavaHome=$JAVA_HOME
+  unset JAVA_HOME
+  for CANDIDATE in $oldJavaHome `ls -d /usr/lib/jvm/java-1.6.0-openjdk-* /usr/lib/jvm/java-6-openjdk-* /usr/lib/jvm/java-6-openjdk 2>&-`; do
+    if [ -n "$CANDIDATE" -a -x "$CANDIDATE/bin/java" ]; then
+      export JAVA_HOME=$CANDIDATE
+      break
+    fi
+  done
+}
+
+# assures JDK installed and JAVA_HOME to a link at /usr/local/jdk
 function installOpenJDK() {
-  if hash apt-get 2>/dev/null; then
-    export JAVA_HOME=${JAVA_HOME:-/usr/lib/jvm/java-6-openjdk}
-    test -d $JAVA_HOME || ( apt-get-update && apt-get-install openjdk-6-jdk )
-  elif hash yum 2>/dev/null; then
-    export pkg=java-1.6.0-openjdk-devel
-    yum --nogpgcheck -y install $pkg &&
-    export JAVA_HOME=`ls -d /usr/lib/jvm/java-1.6.0-openjdk-*`
-  else
-    abort "we only support apt-get and yum right now... please contribute!"
-    return 1
+  if [ "$JAVA_HOME" == "/usr/local/jdk" ]; then
+    echo skipping as JAVA_HOME is already set to /usr/local/jdk
+    return 0
   fi
-  test -n "$JAVA_HOME" || abort "JDK installation failed!"
-  ln -Fs $JAVA_HOME /usr/local/jdk 
+  if [ -n "$JAVA_HOME" -a -x "$JAVA_HOME/bin/java" ]; then
+    echo reusing JAVA_HOME $JAVA_HOME
+  else
+    if hash apt-get 2>/dev/null; then
+      apt-get-update && apt-get-install openjdk-6-jdk
+    elif hash yum 2>/dev/null; then
+      yum-install java-1.6.0-openjdk-devel
+    else
+      abort "we only support apt-get and yum right now... please contribute"
+    fi
+    findOpenJDK
+    if [ -n "$JAVA_HOME" ]; then
+      echo installed JAVA_HOME $JAVA_HOME
+    else
+      abort "JDK installation failed"
+    fi
+  fi
+  rm -rf /usr/local/jdk
+  ln -Fs $JAVA_HOME /usr/local/jdk
   /usr/local/jdk/bin/java -version || abort "cannot run java"
   setupJavaHomeInProfile
 }