http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/java/org/jclouds/chef/config/BaseChefHttpApiModule.java
----------------------------------------------------------------------
diff --git 
a/apis/chef/src/main/java/org/jclouds/chef/config/BaseChefHttpApiModule.java 
b/apis/chef/src/main/java/org/jclouds/chef/config/BaseChefHttpApiModule.java
new file mode 100644
index 0000000..2fc4201
--- /dev/null
+++ b/apis/chef/src/main/java/org/jclouds/chef/config/BaseChefHttpApiModule.java
@@ -0,0 +1,208 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.chef.config;
+
+import static com.google.common.base.Suppliers.compose;
+import static com.google.common.base.Suppliers.memoizeWithExpiration;
+import static com.google.common.base.Throwables.propagate;
+import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL;
+import static org.jclouds.chef.config.ChefProperties.CHEF_VALIDATOR_CREDENTIAL;
+import static org.jclouds.chef.config.ChefProperties.CHEF_VALIDATOR_NAME;
+import static org.jclouds.crypto.Pems.privateKeySpec;
+
+import java.io.IOException;
+import java.security.PrivateKey;
+import java.security.spec.InvalidKeySpecException;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import org.jclouds.chef.domain.Client;
+import org.jclouds.chef.functions.BootstrapConfigForGroup;
+import org.jclouds.chef.functions.ClientForGroup;
+import org.jclouds.chef.functions.RunListForGroup;
+import org.jclouds.chef.handlers.ChefApiErrorRetryHandler;
+import org.jclouds.chef.handlers.ChefErrorHandler;
+import org.jclouds.crypto.Crypto;
+import org.jclouds.crypto.Pems;
+import org.jclouds.date.DateService;
+import org.jclouds.date.TimeStamp;
+import org.jclouds.domain.Credentials;
+import org.jclouds.domain.JsonBall;
+import org.jclouds.http.HttpErrorHandler;
+import org.jclouds.http.HttpRetryHandler;
+import org.jclouds.http.annotation.ClientError;
+import org.jclouds.http.annotation.Redirection;
+import org.jclouds.http.annotation.ServerError;
+import org.jclouds.rest.ConfiguresHttpApi;
+import org.jclouds.rest.config.HttpApiModule;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Charsets;
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+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.io.ByteSource;
+import com.google.inject.ConfigurationException;
+import com.google.inject.Injector;
+import com.google.inject.Key;
+import com.google.inject.Provides;
+import com.google.inject.name.Names;
+
+/**
+ * Configures the Chef connection.
+ */
+@ConfiguresHttpApi
+public abstract class BaseChefHttpApiModule<S> extends HttpApiModule<S> {
+
+   @Provides
+   @TimeStamp
+   protected String provideTimeStamp(@TimeStamp Supplier<String> cache) {
+      return cache.get();
+   }
+
+   /**
+    * borrowing concurrency code to ensure that caching takes place properly
+    */
+   @Provides
+   @TimeStamp
+   Supplier<String> provideTimeStampCache(@Named(PROPERTY_SESSION_INTERVAL) 
long seconds, final DateService dateService) {
+      return memoizeWithExpiration(new Supplier<String>() {
+         @Override
+         public String get() {
+            return dateService.iso8601SecondsDateFormat();
+         }
+      }, seconds, TimeUnit.SECONDS);
+   }
+
+   // TODO: potentially change this
+   @Provides
+   @Singleton
+   public Supplier<PrivateKey> supplyKey(final LoadingCache<Credentials, 
PrivateKey> keyCache,
+         @org.jclouds.location.Provider final Supplier<Credentials> creds) {
+      return compose(new Function<Credentials, PrivateKey>() {
+         @Override
+         public PrivateKey apply(Credentials in) {
+            return keyCache.getUnchecked(in);
+         }
+      }, creds);
+   }
+
+   @Provides
+   @Singleton
+   LoadingCache<Credentials, PrivateKey> 
privateKeyCache(PrivateKeyForCredentials loader) {
+      // throw out the private key related to old credentials
+      return CacheBuilder.newBuilder().maximumSize(2).build(loader);
+   }
+
+   /**
+    * it is relatively expensive to extract a private key from a PEM. cache the
+    * relationship between current credentials so that the private key is only
+    * recalculated once.
+    */
+   @VisibleForTesting
+   @Singleton
+   private static class PrivateKeyForCredentials extends 
CacheLoader<Credentials, PrivateKey> {
+      private final Crypto crypto;
+
+      @Inject
+      private PrivateKeyForCredentials(Crypto crypto) {
+         this.crypto = crypto;
+      }
+
+      @Override
+      public PrivateKey load(Credentials in) {
+         try {
+            return crypto.rsaKeyFactory().generatePrivate(
+                  
privateKeySpec(ByteSource.wrap(in.credential.getBytes(Charsets.UTF_8))));
+         } catch (InvalidKeySpecException e) {
+            throw propagate(e);
+         } catch (IOException e) {
+            throw propagate(e);
+         }
+      }
+   }
+
+   @Provides
+   @Singleton
+   @Validator
+   public Optional<String> provideValidatorName(Injector injector) {
+      // Named properties can not be injected as optional here, so let's use 
the
+      // injector to bypass it
+      Key<String> key = Key.get(String.class, 
Names.named(CHEF_VALIDATOR_NAME));
+      try {
+         return Optional.<String> of(injector.getInstance(key));
+      } catch (ConfigurationException ex) {
+         return Optional.<String> absent();
+      }
+   }
+
+   @Provides
+   @Singleton
+   @Validator
+   public Optional<PrivateKey> provideValidatorCredential(Crypto crypto, 
Injector injector)
+         throws InvalidKeySpecException, IOException {
+      // Named properties can not be injected as optional here, so let's use 
the
+      // injector to bypass it
+      Key<String> key = Key.get(String.class, 
Names.named(CHEF_VALIDATOR_CREDENTIAL));
+      try {
+         String validatorCredential = injector.getInstance(key);
+         PrivateKey validatorKey = crypto.rsaKeyFactory().generatePrivate(
+               
Pems.privateKeySpec(ByteSource.wrap(validatorCredential.getBytes(Charsets.UTF_8))));
+         return Optional.<PrivateKey> of(validatorKey);
+      } catch (ConfigurationException ex) {
+         return Optional.<PrivateKey> absent();
+      }
+   }
+
+   @Provides
+   @Singleton
+   CacheLoader<String, List<String>> runListForGroup(RunListForGroup 
runListForGroup) {
+      return CacheLoader.from(runListForGroup);
+   }
+
+   @Provides
+   @Singleton
+   CacheLoader<String, ? extends JsonBall> 
bootstrapConfigForGroup(BootstrapConfigForGroup bootstrapConfigForGroup) {
+      return CacheLoader.from(bootstrapConfigForGroup);
+   }
+
+   @Provides
+   @Singleton
+   CacheLoader<String, Client> groupToClient(ClientForGroup clientForGroup) {
+      return CacheLoader.from(clientForGroup);
+   }
+
+   @Override
+   protected void bindErrorHandlers() {
+      
bind(HttpErrorHandler.class).annotatedWith(Redirection.class).to(ChefErrorHandler.class);
+      
bind(HttpErrorHandler.class).annotatedWith(ClientError.class).to(ChefErrorHandler.class);
+      
bind(HttpErrorHandler.class).annotatedWith(ServerError.class).to(ChefErrorHandler.class);
+   }
+
+   @Override
+   protected void bindRetryHandlers() {
+      
bind(HttpRetryHandler.class).annotatedWith(ClientError.class).to(ChefApiErrorRetryHandler.class);
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/java/org/jclouds/chef/config/ChefBootstrapModule.java
----------------------------------------------------------------------
diff --git 
a/apis/chef/src/main/java/org/jclouds/chef/config/ChefBootstrapModule.java 
b/apis/chef/src/main/java/org/jclouds/chef/config/ChefBootstrapModule.java
new file mode 100644
index 0000000..a5fd231
--- /dev/null
+++ b/apis/chef/src/main/java/org/jclouds/chef/config/ChefBootstrapModule.java
@@ -0,0 +1,121 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.chef.config;
+
+import static org.jclouds.chef.config.ChefProperties.CHEF_GEM_SYSTEM_VERSION;
+import static org.jclouds.chef.config.ChefProperties.CHEF_UPDATE_GEMS;
+import static org.jclouds.chef.config.ChefProperties.CHEF_UPDATE_GEM_SYSTEM;
+import static org.jclouds.chef.config.ChefProperties.CHEF_USE_OMNIBUS;
+import static org.jclouds.chef.config.ChefProperties.CHEF_VERSION;
+
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import org.jclouds.scriptbuilder.domain.Statement;
+import org.jclouds.scriptbuilder.domain.StatementList;
+import org.jclouds.scriptbuilder.statements.chef.InstallChefGems;
+import org.jclouds.scriptbuilder.statements.chef.InstallChefUsingOmnibus;
+import org.jclouds.scriptbuilder.statements.ruby.InstallRuby;
+import org.jclouds.scriptbuilder.statements.ruby.InstallRubyGems;
+
+import com.google.common.base.Optional;
+import com.google.inject.AbstractModule;
+import com.google.inject.Inject;
+import com.google.inject.Provides;
+
+/**
+ * Provides bootstrap configuration for nodes.
+ */
+public class ChefBootstrapModule extends AbstractModule {
+
+   @Provides
+   @Named("installChefGems")
+   @Singleton
+   Statement installChefGems(BootstrapProperties bootstrapProperties) {
+      InstallRubyGems installRubyGems = InstallRubyGems.builder()
+            .version(bootstrapProperties.gemSystemVersion().orNull())
+            .updateSystem(bootstrapProperties.updateGemSystem(), 
bootstrapProperties.gemSystemVersion().orNull())
+            .updateExistingGems(bootstrapProperties.updateGems()) //
+            .build();
+
+      Statement installChef = 
InstallChefGems.builder().version(bootstrapProperties.chefVersion().orNull()).build();
+
+      return new StatementList(InstallRuby.builder().build(), installRubyGems, 
installChef);
+   }
+
+   @Provides
+   @Named("installChefOmnibus")
+   @Singleton
+   Statement installChefUsingOmnibus() {
+      return new InstallChefUsingOmnibus();
+   }
+
+   @Provides
+   @InstallChef
+   @Singleton
+   Statement installChef(BootstrapProperties bootstrapProperties, 
@Named("installChefGems") Statement installChefGems,
+         @Named("installChefOmnibus") Statement installChefOmnibus) {
+      return bootstrapProperties.useOmnibus() ? installChefOmnibus : 
installChefGems;
+   }
+
+   @Singleton
+   private static class BootstrapProperties {
+      @Named(CHEF_VERSION)
+      @Inject(optional = true)
+      private String chefVersionProperty;
+
+      @Named(CHEF_GEM_SYSTEM_VERSION)
+      @Inject(optional = true)
+      private String gemSystemVersionProperty;
+
+      @Named(CHEF_UPDATE_GEM_SYSTEM)
+      @Inject
+      private String updateGemSystemProeprty;
+
+      @Named(CHEF_UPDATE_GEMS)
+      @Inject
+      private String updateGemsProperty;
+
+      @Named(CHEF_USE_OMNIBUS)
+      @Inject
+      private String useOmnibus;
+
+      public Optional<String> chefVersion() {
+         return Optional.fromNullable(chefVersionProperty);
+      }
+
+      public Optional<String> gemSystemVersion() {
+         return Optional.fromNullable(gemSystemVersionProperty);
+      }
+
+      public boolean updateGemSystem() {
+         return Boolean.parseBoolean(updateGemSystemProeprty);
+      }
+
+      public boolean updateGems() {
+         return Boolean.parseBoolean(updateGemsProperty);
+      }
+
+      public boolean useOmnibus() {
+         return Boolean.parseBoolean(useOmnibus);
+      }
+   }
+
+   @Override
+   protected void configure() {
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/java/org/jclouds/chef/config/ChefHttpApiModule.java
----------------------------------------------------------------------
diff --git 
a/apis/chef/src/main/java/org/jclouds/chef/config/ChefHttpApiModule.java 
b/apis/chef/src/main/java/org/jclouds/chef/config/ChefHttpApiModule.java
new file mode 100644
index 0000000..ffd3b0b
--- /dev/null
+++ b/apis/chef/src/main/java/org/jclouds/chef/config/ChefHttpApiModule.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.chef.config;
+
+import org.jclouds.chef.ChefApi;
+import org.jclouds.rest.ConfiguresHttpApi;
+
+/**
+ * Configures the Chef connection.
+ */
+@ConfiguresHttpApi
+public class ChefHttpApiModule extends BaseChefHttpApiModule<ChefApi> {
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/java/org/jclouds/chef/config/ChefParserModule.java
----------------------------------------------------------------------
diff --git 
a/apis/chef/src/main/java/org/jclouds/chef/config/ChefParserModule.java 
b/apis/chef/src/main/java/org/jclouds/chef/config/ChefParserModule.java
new file mode 100644
index 0000000..c214236
--- /dev/null
+++ b/apis/chef/src/main/java/org/jclouds/chef/config/ChefParserModule.java
@@ -0,0 +1,321 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.chef.config;
+
+import static com.google.common.base.Objects.equal;
+import static com.google.common.base.Objects.toStringHelper;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.lang.reflect.Type;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.security.spec.InvalidKeySpecException;
+import java.util.Map;
+import java.util.Set;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.jclouds.chef.domain.DatabagItem;
+import org.jclouds.chef.functions.ParseCookbookDefinitionFromJson;
+import org.jclouds.chef.functions.ParseCookbookVersionsV09FromJson;
+import org.jclouds.chef.functions.ParseCookbookVersionsV10FromJson;
+import org.jclouds.chef.functions.ParseKeySetFromJson;
+import org.jclouds.chef.suppliers.ChefVersionSupplier;
+import org.jclouds.crypto.Crypto;
+import org.jclouds.crypto.Pems;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.json.config.GsonModule.DateAdapter;
+import org.jclouds.json.config.GsonModule.Iso8601DateAdapter;
+import 
org.jclouds.json.internal.NullFilteringTypeAdapterFactories.MapTypeAdapterFactory;
+import org.jclouds.json.internal.NullHackJsonLiteralAdapter;
+
+import com.google.common.base.Charsets;
+import com.google.common.base.Function;
+import com.google.common.base.Objects;
+import com.google.common.base.Throwables;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+import com.google.common.io.ByteSource;
+import com.google.gson.Gson;
+import com.google.gson.JsonDeserializationContext;
+import com.google.gson.JsonDeserializer;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonParseException;
+import com.google.gson.JsonSyntaxException;
+import com.google.gson.TypeAdapter;
+import com.google.gson.internal.JsonReaderInternalAccess;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonWriter;
+import com.google.inject.AbstractModule;
+import com.google.inject.ImplementedBy;
+import com.google.inject.Provides;
+
+public class ChefParserModule extends AbstractModule {
+   @ImplementedBy(PrivateKeyAdapterImpl.class)
+   public interface PrivateKeyAdapter extends JsonDeserializer<PrivateKey> {
+
+   }
+
+   @Singleton
+   public static class PrivateKeyAdapterImpl implements PrivateKeyAdapter {
+      private final Crypto crypto;
+
+      @Inject
+      PrivateKeyAdapterImpl(Crypto crypto) {
+         this.crypto = crypto;
+      }
+
+      @Override
+      public PrivateKey deserialize(JsonElement json, Type typeOfT, 
JsonDeserializationContext context)
+            throws JsonParseException {
+         String keyText = json.getAsString().replaceAll("\\n", "\n");
+         try {
+            return crypto.rsaKeyFactory().generatePrivate(
+                  
Pems.privateKeySpec(ByteSource.wrap(keyText.getBytes(Charsets.UTF_8))));
+         } catch (UnsupportedEncodingException e) {
+            Throwables.propagate(e);
+            return null;
+         } catch (InvalidKeySpecException e) {
+            Throwables.propagate(e);
+            return null;
+         } catch (IOException e) {
+            Throwables.propagate(e);
+            return null;
+         }
+      }
+   }
+
+   @ImplementedBy(PublicKeyAdapterImpl.class)
+   public interface PublicKeyAdapter extends JsonDeserializer<PublicKey> {
+
+   }
+
+   @Singleton
+   public static class PublicKeyAdapterImpl implements PublicKeyAdapter {
+      private final Crypto crypto;
+
+      @Inject
+      PublicKeyAdapterImpl(Crypto crypto) {
+         this.crypto = crypto;
+      }
+
+      @Override
+      public PublicKey deserialize(JsonElement json, Type typeOfT, 
JsonDeserializationContext context)
+            throws JsonParseException {
+         String keyText = json.getAsString().replaceAll("\\n", "\n");
+         try {
+            return crypto.rsaKeyFactory().generatePublic(
+                  
Pems.publicKeySpec(ByteSource.wrap(keyText.getBytes(Charsets.UTF_8))));
+         } catch (UnsupportedEncodingException e) {
+            Throwables.propagate(e);
+            return null;
+         } catch (InvalidKeySpecException e) {
+            Throwables.propagate(e);
+            return null;
+         } catch (IOException e) {
+            Throwables.propagate(e);
+            return null;
+         }
+      }
+   }
+
+   @ImplementedBy(X509CertificateAdapterImpl.class)
+   public interface X509CertificateAdapter extends 
JsonDeserializer<X509Certificate> {
+
+   }
+
+   @Singleton
+   public static class X509CertificateAdapterImpl implements 
X509CertificateAdapter {
+      private final Crypto crypto;
+
+      @Inject
+      X509CertificateAdapterImpl(Crypto crypto) {
+         this.crypto = crypto;
+      }
+
+      @Override
+      public X509Certificate deserialize(JsonElement json, Type typeOfT, 
JsonDeserializationContext context)
+            throws JsonParseException {
+         String keyText = json.getAsString().replaceAll("\\n", "\n");
+         try {
+            return 
Pems.x509Certificate(ByteSource.wrap(keyText.getBytes(Charsets.UTF_8)),
+                  crypto.certFactory());
+         } catch (UnsupportedEncodingException e) {
+            Throwables.propagate(e);
+            return null;
+         } catch (IOException e) {
+            Throwables.propagate(e);
+            return null;
+         } catch (CertificateException e) {
+            Throwables.propagate(e);
+            return null;
+         }
+      }
+   }
+
+   /**
+    * writes or reads the literal directly
+    */
+   @Singleton
+   public static class DataBagItemAdapter extends 
NullHackJsonLiteralAdapter<DatabagItem> {
+      final Gson gson = new Gson();
+
+      @Override
+      protected DatabagItem createJsonLiteralFromRawJson(String text) {
+         IdHolder idHolder = gson.fromJson(text, IdHolder.class);
+         checkState(idHolder.id != null,
+               "databag item must be a json hash ex. 
{\"id\":\"item1\",\"my_key\":\"my_data\"}; was %s", text);
+         text = text.replaceFirst(String.format("\\{\"id\"[ ]?:\"%s\",", 
idHolder.id), "{");
+         return new DatabagItem(idHolder.id, text);
+      }
+
+      @Override
+      protected String toString(DatabagItem value) {
+         String text = value.toString();
+
+         try {
+            IdHolder idHolder = gson.fromJson(text, IdHolder.class);
+            if (idHolder.id == null) {
+               text = text.replaceFirst("\\{", 
String.format("{\"id\":\"%s\",", value.getId()));
+            } else {
+               checkArgument(value.getId().equals(idHolder.id),
+                     "incorrect id in databagItem text, should be %s: was %s", 
value.getId(), idHolder.id);
+            }
+         } catch (JsonSyntaxException e) {
+            throw new IllegalArgumentException(e);
+         }
+
+         return text;
+      }
+   }
+
+   private static class IdHolder {
+      private String id;
+   }
+
+   // The NullFilteringTypeAdapterFactories.MapTypeAdapter class is final. Do
+   // the same logic here
+   private static final class KeepLastRepeatedKeyMapTypeAdapter<K, V> extends 
TypeAdapter<Map<K, V>> {
+
+      protected final TypeAdapter<K> keyAdapter;
+      protected final TypeAdapter<V> valueAdapter;
+
+      protected KeepLastRepeatedKeyMapTypeAdapter(TypeAdapter<K> keyAdapter, 
TypeAdapter<V> valueAdapter) {
+         this.keyAdapter = keyAdapter;
+         this.valueAdapter = valueAdapter;
+         nullSafe();
+      }
+
+      public void write(JsonWriter out, Map<K, V> value) throws IOException {
+         if (value == null) {
+            out.nullValue();
+            return;
+         }
+         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.newHashMap();
+         in.beginObject();
+         while (in.hasNext()) {
+            JsonReaderInternalAccess.INSTANCE.promoteNameToValue(in);
+            K name = keyAdapter.read(in);
+            V value = valueAdapter.read(in);
+            if (value != null) {
+               // If there are repeated keys, overwrite them to only keep the 
last one
+               result.put(name, value);
+            }
+         }
+         in.endObject();
+         return ImmutableMap.copyOf(result);
+      }
+
+      @Override
+      public int hashCode() {
+         return Objects.hashCode(keyAdapter, valueAdapter);
+      }
+
+      @Override
+      public boolean equals(Object obj) {
+         if (this == obj)
+            return true;
+         if (obj == null || getClass() != obj.getClass())
+            return false;
+         KeepLastRepeatedKeyMapTypeAdapter<?, ?> that = 
KeepLastRepeatedKeyMapTypeAdapter.class.cast(obj);
+         return equal(this.keyAdapter, that.keyAdapter) && 
equal(this.valueAdapter, that.valueAdapter);
+      }
+
+      @Override
+      public String toString() {
+         return toStringHelper(this).add("keyAdapter", 
keyAdapter).add("valueAdapter", valueAdapter).toString();
+      }
+   }
+
+   public static class KeepLastRepeatedKeyMapTypeAdapterFactory extends 
MapTypeAdapterFactory {
+
+      public KeepLastRepeatedKeyMapTypeAdapterFactory() {
+         super(Map.class);
+      }
+
+      @SuppressWarnings("unchecked")
+      @Override
+      protected <K, V, T> TypeAdapter<T> newAdapter(TypeAdapter<K> keyAdapter, 
TypeAdapter<V> valueAdapter) {
+         return (TypeAdapter<T>) new KeepLastRepeatedKeyMapTypeAdapter<K, 
V>(keyAdapter, valueAdapter);
+      }
+   }
+
+   @Provides
+   @Singleton
+   public Map<Type, Object> provideCustomAdapterBindings(DataBagItemAdapter 
adapter, PrivateKeyAdapter privateAdapter,
+         PublicKeyAdapter publicAdapter, X509CertificateAdapter certAdapter) {
+      return ImmutableMap.<Type, Object> of(DatabagItem.class, adapter, 
PrivateKey.class, privateAdapter,
+            PublicKey.class, publicAdapter, X509Certificate.class, 
certAdapter);
+   }
+
+   @Provides
+   @Singleton
+   @CookbookParser
+   public Function<HttpResponse, Set<String>> 
provideCookbookDefinitionAdapter(ChefVersionSupplier chefVersionSupplier,
+         ParseCookbookDefinitionFromJson v10parser, ParseKeySetFromJson 
v09parser) {
+      return chefVersionSupplier.get() >= 10 ? v10parser : v09parser;
+   }
+
+   @Provides
+   @Singleton
+   @CookbookVersionsParser
+   public Function<HttpResponse, Set<String>> 
provideCookbookDefinitionAdapter(ChefVersionSupplier chefVersionSupplier,
+         ParseCookbookVersionsV10FromJson v10parser, 
ParseCookbookVersionsV09FromJson v09parser) {
+      return chefVersionSupplier.get() >= 10 ? v10parser : v09parser;
+   }
+
+   @Override
+   protected void configure() {
+      bind(DateAdapter.class).to(Iso8601DateAdapter.class);
+      
bind(MapTypeAdapterFactory.class).to(KeepLastRepeatedKeyMapTypeAdapterFactory.class);
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/java/org/jclouds/chef/config/ChefProperties.java
----------------------------------------------------------------------
diff --git 
a/apis/chef/src/main/java/org/jclouds/chef/config/ChefProperties.java 
b/apis/chef/src/main/java/org/jclouds/chef/config/ChefProperties.java
new file mode 100644
index 0000000..211b5d6
--- /dev/null
+++ b/apis/chef/src/main/java/org/jclouds/chef/config/ChefProperties.java
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.chef.config;
+
+
+/**
+ * Configuration properties and constants used in Chef connections.
+ */
+public interface ChefProperties {
+
+   /**
+    * The name of the Chef logger.
+    */
+   public static final String CHEF_LOGGER = "jclouds.chef";
+
+   /**
+    * Databag that holds chef bootstrap hints, should be a json ball in the
+    * following format:
+    * <p>
+    * {"tag":{"run_list":["recipe[apache2]"]}}
+    */
+   public static final String CHEF_BOOTSTRAP_DATABAG = 
"chef.bootstrap-databag";
+
+   /**
+    * The name of the validator client used to allow nodes to autoregister in
+    * the Chef server.
+    * <p>
+    * This property must be set prior to running the
+    * {@link ChefService#createBootstrapScriptForGroup(String)} method.
+    */
+   public static final String CHEF_VALIDATOR_NAME = "chef.validator-name";
+
+   /**
+    * The credential of the validator client used to allow nodes to 
autoregister
+    * in the Chef server.
+    * <p>
+    * This property must be set prior to running the
+    * {@link ChefService#createBootstrapScriptForGroup(String)} method.
+    */
+   public static final String CHEF_VALIDATOR_CREDENTIAL = 
"chef.validator-credential";
+
+   /**
+    * The version of the Chef gem to install when bootstrapping nodes.
+    * <p>
+    * If this property is not set, by default the latest available Chef gem 
will
+    * be installed. The values can be fixed versions such as '0.10.8' or
+    * constrained values such as '>= 0.10.8'.
+    * <p>
+    * This property must be set prior to running the
+    * {@link ChefService#createBootstrapScriptForGroup(String)} method.
+    */
+   public static final String CHEF_VERSION = "chef.version";
+
+   /**
+    * Boolean property. Default (false).
+    * <p>
+    * When bootstrapping a node, forces a gem system update before installing
+    * the Chef gems.
+    * <p>
+    * This property must be set prior to running the
+    * {@link ChefService#createBootstrapScriptForGroup(String)} method.
+    */
+   public static final String CHEF_UPDATE_GEM_SYSTEM = 
"chef.update-gem-system";
+
+   /**
+    * To be used in conjunction with {@link #CHEF_UPDATE_GEM_SYSTEM}. This
+    * property will force the version of RubyGems to update the system to.
+    * <p>
+    * This property must be set prior to running the
+    * {@link ChefService#createBootstrapScriptForGroup(String)} method.
+    */
+   public static final String CHEF_GEM_SYSTEM_VERSION = 
"chef.gem-system-version";
+
+   /**
+    * Boolean property. Default (false).
+    * <p>
+    * When bootstrapping a node, updates the existing gems before installing
+    * Chef.
+    * <p>
+    * This property must be set prior to running the
+    * {@link ChefService#createBootstrapScriptForGroup(String)} method.
+    */
+   public static final String CHEF_UPDATE_GEMS = "chef.update-gems";
+
+   /**
+    * Boolean property. Default (true).
+    * <p>
+    * When bootstrapping a node, install the Chef client using the Omnibus
+    * installer.
+    * <p>
+    * This property must be set prior to running the
+    * {@link ChefService#createBootstrapScriptForGroup(String)} method.
+    */
+   public static final String CHEF_USE_OMNIBUS = "chef.use-omnibus";
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/java/org/jclouds/chef/config/CookbookParser.java
----------------------------------------------------------------------
diff --git 
a/apis/chef/src/main/java/org/jclouds/chef/config/CookbookParser.java 
b/apis/chef/src/main/java/org/jclouds/chef/config/CookbookParser.java
new file mode 100644
index 0000000..34eedde
--- /dev/null
+++ b/apis/chef/src/main/java/org/jclouds/chef/config/CookbookParser.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.chef.config;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import javax.inject.Qualifier;
+
+/**
+ * Used to configure the cookbook Json parser.
+ * <p>
+ * Chef Server version 0.9 and 0.10 return a different Json when rquesting the
+ * cookbook definitions. This annotation can be used to setup the cookbook
+ * parser.
+ */
+@Target({ METHOD, PARAMETER, FIELD })
+@Retention(RUNTIME)
+@Qualifier
+public @interface CookbookParser {
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/java/org/jclouds/chef/config/CookbookVersionsParser.java
----------------------------------------------------------------------
diff --git 
a/apis/chef/src/main/java/org/jclouds/chef/config/CookbookVersionsParser.java 
b/apis/chef/src/main/java/org/jclouds/chef/config/CookbookVersionsParser.java
new file mode 100644
index 0000000..c946917
--- /dev/null
+++ 
b/apis/chef/src/main/java/org/jclouds/chef/config/CookbookVersionsParser.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.chef.config;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import javax.inject.Qualifier;
+
+/**
+ * Used to configure the cookbook versions Json parser.
+ * <p>
+ * Chef Server version 0.9 and 0.10 return a different Json when rquesting the
+ * cookbook versions. This annotation can be used to setup the cookbook 
versions
+ * parser.
+ */
+@Target({ METHOD, PARAMETER, FIELD })
+@Retention(RUNTIME)
+@Qualifier
+public @interface CookbookVersionsParser {
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/java/org/jclouds/chef/config/InstallChef.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/main/java/org/jclouds/chef/config/InstallChef.java 
b/apis/chef/src/main/java/org/jclouds/chef/config/InstallChef.java
new file mode 100644
index 0000000..d40ae91
--- /dev/null
+++ b/apis/chef/src/main/java/org/jclouds/chef/config/InstallChef.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.chef.config;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import javax.inject.Qualifier;
+
+/**
+ * Used to configure the Chef install script.
+ */
+@Target({ METHOD, PARAMETER, FIELD })
+@Retention(RUNTIME)
+@Qualifier
+public @interface InstallChef {
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/java/org/jclouds/chef/config/Validator.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/main/java/org/jclouds/chef/config/Validator.java 
b/apis/chef/src/main/java/org/jclouds/chef/config/Validator.java
new file mode 100644
index 0000000..99825d8
--- /dev/null
+++ b/apis/chef/src/main/java/org/jclouds/chef/config/Validator.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.chef.config;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import javax.inject.Qualifier;
+
+/**
+ * Used to configure the validator client information.
+ * <p>
+ * In a Chef server it must be only one validator client. This client is used 
by
+ * new nodes to autoregister themselves in the Chef server.
+ */
+@Target({ METHOD, PARAMETER, FIELD })
+@Retention(RUNTIME)
+@Qualifier
+public @interface Validator {
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/java/org/jclouds/chef/domain/Attribute.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/main/java/org/jclouds/chef/domain/Attribute.java 
b/apis/chef/src/main/java/org/jclouds/chef/domain/Attribute.java
new file mode 100644
index 0000000..34be6b7
--- /dev/null
+++ b/apis/chef/src/main/java/org/jclouds/chef/domain/Attribute.java
@@ -0,0 +1,235 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.chef.domain;
+
+import static org.jclouds.chef.util.CollectionUtils.copyOfOrEmpty;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.beans.ConstructorProperties;
+import java.util.List;
+import java.util.Set;
+
+import org.jclouds.domain.JsonBall;
+import org.jclouds.javax.annotation.Nullable;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * An attribute in a cookbook metadata.
+ */
+public class Attribute {
+   public static Builder builder() {
+      return new Builder();
+   }
+
+   public static class Builder {
+      private String required;
+      private boolean calculated;
+      private ImmutableSet.Builder<String> choice = ImmutableSet.builder();
+      private JsonBall defaultValue;
+      private String type;
+      private ImmutableList.Builder<String> recipes = ImmutableList.builder();
+      private String displayName;
+      private String description;
+
+      public Builder required(String required) {
+         this.required = checkNotNull(required, "required");
+         return this;
+      }
+
+      public Builder calculated(boolean calculated) {
+         this.calculated = calculated;
+         return this;
+      }
+
+      public Builder choice(String choice) {
+         this.choice.add(checkNotNull(choice, "choice"));
+         return this;
+      }
+
+      public Builder choices(Iterable<String> choices) {
+         this.choice.addAll(checkNotNull(choices, "choices"));
+         return this;
+      }
+
+      public Builder defaultValue(JsonBall defaultValue) {
+         this.defaultValue = checkNotNull(defaultValue, "defaultValue");
+         return this;
+      }
+
+      public Builder type(String type) {
+         this.type = checkNotNull(type, "type");
+         return this;
+      }
+
+      public Builder recipe(String recipe) {
+         this.recipes.add(checkNotNull(recipe, "recipe"));
+         return this;
+      }
+
+      public Builder recipes(Iterable<String> recipes) {
+         this.recipes.addAll(checkNotNull(recipes, "recipes"));
+         return this;
+      }
+
+      public Builder displayName(String displayName) {
+         this.displayName = checkNotNull(displayName, "displayName");
+         return this;
+      }
+
+      public Builder description(String description) {
+         this.description = checkNotNull(description, "description");
+         return this;
+      }
+
+      public Attribute build() {
+         return new Attribute(required, calculated, choice.build(), 
defaultValue, type, recipes.build(), displayName,
+               description);
+      }
+   }
+
+   private final String required;
+   private final boolean calculated;
+   private final Set<String> choice;
+   @SerializedName("default")
+   private final JsonBall defaultValue;
+   private final String type;
+   private final List<String> recipes;
+   @SerializedName("display_name")
+   private final String displayName;
+   private final String description;
+
+   @ConstructorProperties({ "required", "calculated", "choice", "default", 
"type", "recipes", "display_name",
+         "description" })
+   protected Attribute(String required, boolean calculated, @Nullable 
Set<String> choice, JsonBall defaultValue,
+         String type, @Nullable List<String> recipes, String displayName, 
String description) {
+      this.required = required;
+      this.calculated = calculated;
+      this.choice = copyOfOrEmpty(choice);
+      this.defaultValue = defaultValue;
+      this.type = type;
+      this.recipes = copyOfOrEmpty(recipes);
+      this.displayName = displayName;
+      this.description = description;
+   }
+
+   public String getRequired() {
+      return required;
+   }
+
+   public boolean isCalculated() {
+      return calculated;
+   }
+
+   public Set<String> getChoice() {
+      return choice;
+   }
+
+   public JsonBall getDefaultValue() {
+      return defaultValue;
+   }
+
+   public String getType() {
+      return type;
+   }
+
+   public List<String> getRecipes() {
+      return recipes;
+   }
+
+   public String getDisplayName() {
+      return displayName;
+   }
+
+   public String getDescription() {
+      return description;
+   }
+
+   @Override
+   public int hashCode() {
+      final int prime = 31;
+      int result = 1;
+      result = prime * result + (calculated ? 1231 : 1237);
+      result = prime * result + ((choice == null) ? 0 : choice.hashCode());
+      result = prime * result + ((defaultValue == null) ? 0 : 
defaultValue.hashCode());
+      result = prime * result + ((description == null) ? 0 : 
description.hashCode());
+      result = prime * result + ((displayName == null) ? 0 : 
displayName.hashCode());
+      result = prime * result + ((recipes == null) ? 0 : recipes.hashCode());
+      result = prime * result + ((required == null) ? 0 : required.hashCode());
+      result = prime * result + ((type == null) ? 0 : type.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;
+      Attribute other = (Attribute) obj;
+      if (calculated != other.calculated)
+         return false;
+      if (choice == null) {
+         if (other.choice != null)
+            return false;
+      } else if (!choice.equals(other.choice))
+         return false;
+      if (defaultValue == null) {
+         if (other.defaultValue != null)
+            return false;
+      } else if (!defaultValue.equals(other.defaultValue))
+         return false;
+      if (description == null) {
+         if (other.description != null)
+            return false;
+      } else if (!description.equals(other.description))
+         return false;
+      if (displayName == null) {
+         if (other.displayName != null)
+            return false;
+      } else if (!displayName.equals(other.displayName))
+         return false;
+      if (recipes == null) {
+         if (other.recipes != null)
+            return false;
+      } else if (!recipes.equals(other.recipes))
+         return false;
+      if (required == null) {
+         if (other.required != null)
+            return false;
+      } else if (!required.equals(other.required))
+         return false;
+      if (type == null) {
+         if (other.type != null)
+            return false;
+      } else if (!type.equals(other.type))
+         return false;
+      return true;
+   }
+
+   @Override
+   public String toString() {
+      return "Attribute [calculated=" + calculated + ", choice=" + choice + ", 
defaultValue=" + defaultValue
+            + ", description=" + description + ", displayName=" + displayName 
+ ", recipes=" + recipes + ", required="
+            + required + ", type=" + type + "]";
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/java/org/jclouds/chef/domain/BootstrapConfig.java
----------------------------------------------------------------------
diff --git 
a/apis/chef/src/main/java/org/jclouds/chef/domain/BootstrapConfig.java 
b/apis/chef/src/main/java/org/jclouds/chef/domain/BootstrapConfig.java
new file mode 100644
index 0000000..c10c150
--- /dev/null
+++ b/apis/chef/src/main/java/org/jclouds/chef/domain/BootstrapConfig.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.chef.domain;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.List;
+
+import org.jclouds.domain.JsonBall;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableList;
+
+/**
+ * Configures how the nodes in a group will bootstrap.
+ * 
+ * @since 1.7
+ */
+public class BootstrapConfig {
+   public static Builder builder() {
+      return new Builder();
+   }
+
+   public static class Builder {
+      private ImmutableList.Builder<String> runList = ImmutableList.builder();
+      private String environment;
+      private JsonBall attribtues;
+
+      /**
+       * Sets the run list that will be executed in the nodes of the group.
+       */
+      public Builder runList(Iterable<String> runList) {
+         this.runList.addAll(checkNotNull(runList, "runList"));
+         return this;
+      }
+
+      /**
+       * Sets the environment where the nodes in the group will be deployed.
+       */
+      public Builder environment(String environment) {
+         this.environment = checkNotNull(environment, "environment");
+         return this;
+      }
+
+      /**
+       * Sets the attributes that will be populated to the deployed nodes.
+       */
+      public Builder attributes(JsonBall attributes) {
+         this.attribtues = checkNotNull(attributes, "attributes");
+         return this;
+      }
+
+      public BootstrapConfig build() {
+         return new BootstrapConfig(runList.build(), 
Optional.fromNullable(environment),
+               Optional.fromNullable(attribtues));
+      }
+   }
+
+   private final List<String> runList;
+   private final Optional<String> environment;
+   private final Optional<JsonBall> attribtues;
+
+   protected BootstrapConfig(List<String> runList, Optional<String> 
environment, Optional<JsonBall> attribtues) {
+      this.runList = checkNotNull(runList, "runList");
+      this.environment = checkNotNull(environment, "environment");
+      this.attribtues = checkNotNull(attribtues, "attributes");
+   }
+
+   public List<String> getRunList() {
+      return runList;
+   }
+
+   public Optional<String> getEnvironment() {
+      return environment;
+   }
+
+   public Optional<JsonBall> getAttribtues() {
+      return attribtues;
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/java/org/jclouds/chef/domain/ChecksumStatus.java
----------------------------------------------------------------------
diff --git 
a/apis/chef/src/main/java/org/jclouds/chef/domain/ChecksumStatus.java 
b/apis/chef/src/main/java/org/jclouds/chef/domain/ChecksumStatus.java
new file mode 100644
index 0000000..e5a48ca
--- /dev/null
+++ b/apis/chef/src/main/java/org/jclouds/chef/domain/ChecksumStatus.java
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.chef.domain;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import java.beans.ConstructorProperties;
+import java.net.URI;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * The checksum of an uploaded resource.
+ */
+public class ChecksumStatus {
+   public static Builder builder() {
+      return new Builder();
+   }
+
+   public static class Builder {
+      private URI url;
+      private boolean needsUpload;
+
+      public Builder url(URI url) {
+         this.url = checkNotNull(url, "url");
+         return this;
+      }
+
+      public Builder needsUpload(boolean needsUpload) {
+         this.needsUpload = needsUpload;
+         return this;
+      }
+
+      public ChecksumStatus build() {
+         return new ChecksumStatus(url, needsUpload);
+      }
+   }
+
+   private final URI url;
+   @SerializedName("needs_upload")
+   private final boolean needsUpload;
+
+   @ConstructorProperties({ "url", "needs_upload" })
+   protected ChecksumStatus(URI url, boolean needsUpload) {
+      this.url = url;
+      this.needsUpload = needsUpload;
+   }
+
+   public URI getUrl() {
+      return url;
+   }
+
+   public boolean needsUpload() {
+      return needsUpload;
+   }
+
+   @Override
+   public int hashCode() {
+      final int prime = 31;
+      int result = 1;
+      result = prime * result + (needsUpload ? 1231 : 1237);
+      result = prime * result + ((url == null) ? 0 : url.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;
+      ChecksumStatus other = (ChecksumStatus) obj;
+      if (needsUpload != other.needsUpload)
+         return false;
+      if (url == null) {
+         if (other.url != null)
+            return false;
+      } else if (!url.equals(other.url))
+         return false;
+      return true;
+   }
+
+   @Override
+   public String toString() {
+      return "ChecksumStatus [needsUpload=" + needsUpload + ", url=" + url + 
"]";
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/java/org/jclouds/chef/domain/Client.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/main/java/org/jclouds/chef/domain/Client.java 
b/apis/chef/src/main/java/org/jclouds/chef/domain/Client.java
new file mode 100644
index 0000000..7335609
--- /dev/null
+++ b/apis/chef/src/main/java/org/jclouds/chef/domain/Client.java
@@ -0,0 +1,182 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.chef.domain;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.beans.ConstructorProperties;
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
+
+import org.jclouds.javax.annotation.Nullable;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * Client object.
+ */
+public class Client {
+   public static Builder builder() {
+      return new Builder();
+   }
+
+   public static class Builder {
+      private X509Certificate certificate;
+      private PrivateKey privateKey;
+      private String orgname;
+      private String clientname;
+      private String name;
+      private boolean validator;
+
+      public Builder certificate(X509Certificate certificate) {
+         this.certificate = checkNotNull(certificate, "certificate");
+         return this;
+      }
+
+      public Builder privateKey(PrivateKey privateKey) {
+         this.privateKey = checkNotNull(privateKey, "privateKey");
+         return this;
+      }
+
+      public Builder orgname(String orgname) {
+         this.orgname = checkNotNull(orgname, "orgname");
+         return this;
+      }
+
+      public Builder clientname(String clientname) {
+         this.clientname = checkNotNull(clientname, "clientname");
+         return this;
+      }
+
+      public Builder name(String name) {
+         this.name = checkNotNull(name, "name");
+         return this;
+      }
+
+      public Builder isValidator(boolean validator) {
+         this.validator = validator;
+         return this;
+      }
+
+      public Client build() {
+         return new Client(certificate, orgname, clientname, name, validator, 
privateKey);
+      }
+   }
+
+   private final X509Certificate certificate;
+   @SerializedName("private_key")
+   private final PrivateKey privateKey;
+   private final String orgname;
+   private final String clientname;
+   private final String name;
+   private final boolean validator;
+
+   @ConstructorProperties({ "certificate", "orgname", "clientname", "name", 
"validator", "private_key" })
+   protected Client(X509Certificate certificate, String orgname, String 
clientname, String name, boolean validator,
+         @Nullable PrivateKey privateKey) {
+      this.certificate = certificate;
+      this.orgname = orgname;
+      this.clientname = clientname;
+      this.name = name;
+      this.validator = validator;
+      this.privateKey = privateKey;
+   }
+
+   public PrivateKey getPrivateKey() {
+      return privateKey;
+   }
+
+   public X509Certificate getCertificate() {
+      return certificate;
+   }
+
+   public String getOrgname() {
+      return orgname;
+   }
+
+   public String getClientname() {
+      return clientname;
+   }
+
+   public String getName() {
+      return name;
+   }
+
+   public boolean isValidator() {
+      return validator;
+   }
+
+   @Override
+   public int hashCode() {
+      final int prime = 31;
+      int result = 1;
+      result = prime * result + ((certificate == null) ? 0 : 
certificate.hashCode());
+      result = prime * result + ((clientname == null) ? 0 : 
clientname.hashCode());
+      result = prime * result + ((name == null) ? 0 : name.hashCode());
+      result = prime * result + ((orgname == null) ? 0 : orgname.hashCode());
+      result = prime * result + ((privateKey == null) ? 0 : 
privateKey.hashCode());
+      result = prime * result + (validator ? 1231 : 1237);
+      return result;
+   }
+
+   @Override
+   public boolean equals(Object obj) {
+      if (this == obj)
+         return true;
+      if (obj == null)
+         return false;
+      if (getClass() != obj.getClass())
+         return false;
+      Client other = (Client) obj;
+      if (certificate == null) {
+         if (other.certificate != null)
+            return false;
+      } else if (!certificate.equals(other.certificate))
+         return false;
+      if (clientname == null) {
+         if (other.clientname != null)
+            return false;
+      } else if (!clientname.equals(other.clientname))
+         return false;
+      if (name == null) {
+         if (other.name != null)
+            return false;
+      } else if (!name.equals(other.name))
+         return false;
+      if (orgname == null) {
+         if (other.orgname != null)
+            return false;
+      } else if (!orgname.equals(other.orgname))
+         return false;
+      if (privateKey == null) {
+         if (other.privateKey != null)
+            return false;
+      } else if (!privateKey.equals(other.privateKey))
+         return false;
+      if (validator != other.validator)
+         return false;
+      return true;
+   }
+
+   @Override
+   public String toString() {
+      return "Client [name=" + name + ", clientname=" + clientname + ", 
orgname=" + orgname + ", isValidator="
+            + validator + ", certificate=" + certificate + ", privateKey=" + 
(privateKey == null ? "not " : "")
+            + "present]";
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/java/org/jclouds/chef/domain/CookbookDefinition.java
----------------------------------------------------------------------
diff --git 
a/apis/chef/src/main/java/org/jclouds/chef/domain/CookbookDefinition.java 
b/apis/chef/src/main/java/org/jclouds/chef/domain/CookbookDefinition.java
new file mode 100644
index 0000000..17db702
--- /dev/null
+++ b/apis/chef/src/main/java/org/jclouds/chef/domain/CookbookDefinition.java
@@ -0,0 +1,217 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.chef.domain;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.jclouds.chef.util.CollectionUtils.copyOfOrEmpty;
+
+import java.beans.ConstructorProperties;
+import java.net.URI;
+import java.util.Set;
+
+import org.jclouds.javax.annotation.Nullable;
+
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * Cookbook definition as returned by the Chef server >= 0.10.8.
+ */
+public class CookbookDefinition {
+   public static Builder builder() {
+      return new Builder();
+   }
+
+   public static class Builder {
+      private String name;
+      private URI url;
+      private ImmutableSet.Builder<Version> versions = ImmutableSet.builder();
+
+      public Builder name(String name) {
+         this.name = checkNotNull(name, "name");
+         return this;
+      }
+
+      public Builder url(URI url) {
+         this.url = checkNotNull(url, "url");
+         return this;
+      }
+
+      public Builder version(Version version) {
+         this.versions.add(checkNotNull(version, "version"));
+         return this;
+      }
+
+      public Builder versions(Iterable<Version> versions) {
+         this.versions.addAll(checkNotNull(versions, "versions"));
+         return this;
+      }
+
+      public Builder from(CookbookDefinition def) {
+         this.url = checkNotNull(def.getUrl(), "url");
+         this.versions.addAll(checkNotNull(def.getVersions(), "versions"));
+         this.name = def.getName();
+         return this;
+      }
+
+      public CookbookDefinition build() {
+         return new CookbookDefinition(name, url, versions.build());
+      }
+   }
+
+   private final String name;
+   private final URI url;
+   private final Set<Version> versions;
+
+   @ConstructorProperties({"name", "url", "versions" })
+   protected CookbookDefinition(String name, URI url, @Nullable Set<Version> 
versions) {
+      this.name = name;
+      this.url = url;
+      this.versions = copyOfOrEmpty(versions);
+   }
+
+   public String getName() {
+      return name;
+   }
+
+   public URI getUrl() {
+      return url;
+   }
+
+   public Set<Version> getVersions() {
+      return versions;
+   }
+
+   @Override
+   public int hashCode() {
+      final int prime = 31;
+      int result = 1;
+      result = prime * result + ((name == null) ? 0 : name.hashCode());
+      result = prime * result + ((url == null) ? 0 : url.hashCode());
+      result = prime * result + ((versions == null) ? 0 : versions.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;
+      CookbookDefinition other = (CookbookDefinition) obj;
+      if (name == null) {
+         if (other.name != null)
+            return false;
+      } else if (!name.equals(other.name))
+         return false;
+      if (url == null) {
+         if (other.url != null)
+            return false;
+      } else if (!url.equals(other.url))
+         return false;
+      if (versions == null) {
+         if (other.versions != null)
+            return false;
+      } else if (!versions.equals(other.versions))
+         return false;
+      return true;
+   }
+
+   @Override
+   public String toString() {
+      return "CookbookDefinition [name=" + name + ", url=" + url + ", 
versions=" + versions + "]";
+   }
+
+   public static class Version {
+      public static Builder builder() {
+         return new Builder();
+      }
+
+      public static class Builder {
+         private URI url;
+         private String version;
+
+         public Builder url(URI url) {
+            this.url = checkNotNull(url, "url");
+            return this;
+         }
+
+         public Builder version(String version) {
+            this.version = checkNotNull(version, "version");
+            return this;
+         }
+
+         public Version build() {
+            return new Version(url, version);
+         }
+      }
+
+      private final URI url;
+      private final String version;
+
+      @ConstructorProperties({ "url", "version" })
+      protected Version(URI url, String version) {
+         this.url = url;
+         this.version = version;
+      }
+
+      public URI getUrl() {
+         return url;
+      }
+
+      public String getVersion() {
+         return version;
+      }
+
+      @Override
+      public int hashCode() {
+         final int prime = 31;
+         int result = 1;
+         result = prime * result + ((url == null) ? 0 : url.hashCode());
+         result = prime * result + ((version == null) ? 0 : 
version.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;
+         Version other = (Version) obj;
+         if (url == null) {
+            if (other.url != null)
+               return false;
+         } else if (!url.equals(other.url))
+            return false;
+         if (version == null) {
+            if (other.version != null)
+               return false;
+         } else if (!version.equals(other.version))
+            return false;
+         return true;
+      }
+
+      @Override
+      public String toString() {
+         return "Version [url=" + url + ", version=" + version + "]";
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/java/org/jclouds/chef/domain/CookbookVersion.java
----------------------------------------------------------------------
diff --git 
a/apis/chef/src/main/java/org/jclouds/chef/domain/CookbookVersion.java 
b/apis/chef/src/main/java/org/jclouds/chef/domain/CookbookVersion.java
new file mode 100644
index 0000000..df9bd82
--- /dev/null
+++ b/apis/chef/src/main/java/org/jclouds/chef/domain/CookbookVersion.java
@@ -0,0 +1,369 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.chef.domain;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.jclouds.chef.util.CollectionUtils.copyOfOrEmpty;
+
+import java.beans.ConstructorProperties;
+import java.util.Set;
+
+import org.jclouds.javax.annotation.Nullable;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * Cookbook object.
+ */
+public class CookbookVersion {
+   public static Builder builder(String name, String version) {
+      return new Builder(name, version);
+   }
+
+   public static class Builder {
+      private String cookbookName;
+      private ImmutableSet.Builder<Resource> definitions = 
ImmutableSet.builder();
+      private ImmutableSet.Builder<Attribute> attributes = 
ImmutableSet.builder();
+      private ImmutableSet.Builder<Resource> files = ImmutableSet.builder();
+      private Metadata metadata = Metadata.builder().build();
+      private ImmutableSet.Builder<Resource> providers = 
ImmutableSet.builder();
+      private ImmutableSet.Builder<Resource> resources = 
ImmutableSet.builder();
+      private ImmutableSet.Builder<Resource> templates = 
ImmutableSet.builder();
+      private ImmutableSet.Builder<Resource> libraries = 
ImmutableSet.builder();
+      private String version;
+      private ImmutableSet.Builder<Resource> recipes = ImmutableSet.builder();
+      private ImmutableSet.Builder<Resource> rootFiles = 
ImmutableSet.builder();
+
+      public Builder(String name, String version) {
+         this.cookbookName = checkNotNull(name, "name");
+         this.version = checkNotNull(version, "version");
+      }
+
+      public Builder cookbookName(String cookbookName) {
+         this.cookbookName = checkNotNull(cookbookName, "cookbookName");
+         return this;
+      }
+
+      public Builder definition(Resource definition) {
+         this.definitions.add(checkNotNull(definition, "definition"));
+         return this;
+      }
+
+      public Builder definitions(Iterable<Resource> definitions) {
+         this.definitions.addAll(checkNotNull(definitions, "definitions"));
+         return this;
+      }
+
+      public Builder attribute(Attribute attribute) {
+         this.attributes.add(checkNotNull(attribute, "attribute"));
+         return this;
+      }
+
+      public Builder attributes(Iterable<Attribute> attributes) {
+         this.attributes.addAll(checkNotNull(attributes, "attributes"));
+         return this;
+      }
+
+      public Builder file(Resource file) {
+         this.files.add(checkNotNull(file, "file"));
+         return this;
+      }
+
+      public Builder files(Iterable<Resource> files) {
+         this.files.addAll(checkNotNull(files, "files"));
+         return this;
+      }
+
+      public Builder metadata(Metadata metadata) {
+         this.metadata = checkNotNull(metadata, "metadata");
+         return this;
+      }
+
+      public Builder provider(Resource provider) {
+         this.providers.add(checkNotNull(provider, "provider"));
+         return this;
+      }
+
+      public Builder providers(Iterable<Resource> providers) {
+         this.providers.addAll(checkNotNull(providers, "providers"));
+         return this;
+      }
+
+      public Builder resource(Resource resource) {
+         this.resources.add(checkNotNull(resource, "resource"));
+         return this;
+      }
+
+      public Builder resources(Iterable<Resource> resources) {
+         this.resources.addAll(checkNotNull(resources, "resources"));
+         return this;
+      }
+
+      public Builder template(Resource template) {
+         this.templates.add(checkNotNull(template, "template"));
+         return this;
+      }
+
+      public Builder templates(Iterable<Resource> templates) {
+         this.templates.addAll(checkNotNull(templates, "templates"));
+         return this;
+      }
+
+      public Builder library(Resource library) {
+         this.libraries.add(checkNotNull(library, "library"));
+         return this;
+      }
+
+      public Builder libraries(Iterable<Resource> libraries) {
+         this.libraries.addAll(checkNotNull(libraries, "libraries"));
+         return this;
+      }
+
+      public Builder version(String version) {
+         this.version = checkNotNull(version, "version");
+         return this;
+      }
+
+      public Builder recipe(Resource recipe) {
+         this.recipes.add(checkNotNull(recipe, "recipe"));
+         return this;
+      }
+
+      public Builder recipes(Iterable<Resource> recipes) {
+         this.recipes.addAll(checkNotNull(recipes, "recipes"));
+         return this;
+      }
+
+      public Builder rootFile(Resource rootFile) {
+         this.rootFiles.add(checkNotNull(rootFile, "rootFile"));
+         return this;
+      }
+
+      public Builder rootFiles(Iterable<Resource> rootFiles) {
+         this.rootFiles.addAll(checkNotNull(rootFiles, "rootFiles"));
+         return this;
+      }
+
+      public CookbookVersion build() {
+         return new CookbookVersion(checkNotNull(cookbookName, "name") + "-" + 
checkNotNull(version, "version"),
+               definitions.build(), attributes.build(), files.build(), 
metadata, providers.build(), cookbookName,
+               resources.build(), templates.build(), libraries.build(), 
version, recipes.build(), rootFiles.build());
+      }
+   }
+
+   private final String name;
+   private final Set<Resource> definitions;
+   private final Set<Attribute> attributes;
+   private final Set<Resource> files;
+   private final Metadata metadata;
+   private final Set<Resource> providers;
+   @SerializedName("cookbook_name")
+   private final String cookbookName;
+   private final Set<Resource> resources;
+   private final Set<Resource> templates;
+   private final Set<Resource> libraries;
+   private final String version;
+   private final Set<Resource> recipes;
+   @SerializedName("root_files")
+   private final Set<Resource> rootFiles;
+
+   // internal
+   @SerializedName("json_class")
+   private String _jsonClass = "Chef::CookbookVersion";
+   @SerializedName("chef_type")
+   private String _chefType = "cookbook_version";
+
+   @ConstructorProperties({ "name", "definitions", "attributes", "files", 
"metadata", "providers", "cookbook_name",
+         "resources", "templates", "libraries", "version", "recipes", 
"root_files" })
+   protected CookbookVersion(String name, @Nullable Set<Resource> definitions, 
@Nullable Set<Attribute> attributes,
+         @Nullable Set<Resource> files, Metadata metadata, @Nullable 
Set<Resource> providers, String cookbookName,
+         @Nullable Set<Resource> resources, @Nullable Set<Resource> templates, 
@Nullable Set<Resource> libraries,
+         String version, @Nullable Set<Resource> recipes, @Nullable 
Set<Resource> rootFiles) {
+      this.name = name;
+      this.definitions = copyOfOrEmpty(definitions);
+      this.attributes = copyOfOrEmpty(attributes);
+      this.files = copyOfOrEmpty(files);
+      this.metadata = metadata;
+      this.providers = copyOfOrEmpty(providers);
+      this.cookbookName = cookbookName;
+      this.resources = copyOfOrEmpty(resources);
+      this.templates = copyOfOrEmpty(templates);
+      this.libraries = copyOfOrEmpty(libraries);
+      this.version = version;
+      this.recipes = copyOfOrEmpty(recipes);
+      this.rootFiles = copyOfOrEmpty(rootFiles);
+   }
+
+   public String getName() {
+      return name;
+   }
+
+   public Set<Resource> getDefinitions() {
+      return definitions;
+   }
+
+   public Set<Attribute> getAttributes() {
+      return attributes;
+   }
+
+   public Set<Resource> getFiles() {
+      return files;
+   }
+
+   public Metadata getMetadata() {
+      return metadata;
+   }
+
+   public Set<Resource> getSuppliers() {
+      return providers;
+   }
+
+   public String getCookbookName() {
+      return cookbookName;
+   }
+
+   public Set<Resource> getResources() {
+      return resources;
+   }
+
+   public Set<Resource> getTemplates() {
+      return templates;
+   }
+
+   public Set<Resource> getLibraries() {
+      return libraries;
+   }
+
+   public String getVersion() {
+      return version;
+   }
+
+   public Set<Resource> getRecipes() {
+      return recipes;
+   }
+
+   public Set<Resource> getRootFiles() {
+      return rootFiles;
+   }
+
+   @Override
+   public int hashCode() {
+      final int prime = 31;
+      int result = 1;
+      result = prime * result + ((attributes == null) ? 0 : 
attributes.hashCode());
+      result = prime * result + ((cookbookName == null) ? 0 : 
cookbookName.hashCode());
+      result = prime * result + ((definitions == null) ? 0 : 
definitions.hashCode());
+      result = prime * result + ((files == null) ? 0 : files.hashCode());
+      result = prime * result + ((libraries == null) ? 0 : 
libraries.hashCode());
+      result = prime * result + ((metadata == null) ? 0 : metadata.hashCode());
+      result = prime * result + ((name == null) ? 0 : name.hashCode());
+      result = prime * result + ((providers == null) ? 0 : 
providers.hashCode());
+      result = prime * result + ((recipes == null) ? 0 : recipes.hashCode());
+      result = prime * result + ((resources == null) ? 0 : 
resources.hashCode());
+      result = prime * result + ((rootFiles == null) ? 0 : 
rootFiles.hashCode());
+      result = prime * result + ((templates == null) ? 0 : 
templates.hashCode());
+      result = prime * result + ((version == null) ? 0 : version.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;
+      CookbookVersion other = (CookbookVersion) obj;
+      if (attributes == null) {
+         if (other.attributes != null)
+            return false;
+      } else if (!attributes.equals(other.attributes))
+         return false;
+      if (cookbookName == null) {
+         if (other.cookbookName != null)
+            return false;
+      } else if (!cookbookName.equals(other.cookbookName))
+         return false;
+      if (definitions == null) {
+         if (other.definitions != null)
+            return false;
+      } else if (!definitions.equals(other.definitions))
+         return false;
+      if (files == null) {
+         if (other.files != null)
+            return false;
+      } else if (!files.equals(other.files))
+         return false;
+      if (libraries == null) {
+         if (other.libraries != null)
+            return false;
+      } else if (!libraries.equals(other.libraries))
+         return false;
+      if (metadata == null) {
+         if (other.metadata != null)
+            return false;
+      } else if (!metadata.equals(other.metadata))
+         return false;
+      if (name == null) {
+         if (other.name != null)
+            return false;
+      } else if (!name.equals(other.name))
+         return false;
+      if (providers == null) {
+         if (other.providers != null)
+            return false;
+      } else if (!providers.equals(other.providers))
+         return false;
+      if (recipes == null) {
+         if (other.recipes != null)
+            return false;
+      } else if (!recipes.equals(other.recipes))
+         return false;
+      if (resources == null) {
+         if (other.resources != null)
+            return false;
+      } else if (!resources.equals(other.resources))
+         return false;
+      if (rootFiles == null) {
+         if (other.rootFiles != null)
+            return false;
+      } else if (!rootFiles.equals(other.rootFiles))
+         return false;
+      if (templates == null) {
+         if (other.templates != null)
+            return false;
+      } else if (!templates.equals(other.templates))
+         return false;
+      if (version == null) {
+         if (other.version != null)
+            return false;
+      } else if (!version.equals(other.version))
+         return false;
+      return true;
+   }
+
+   @Override
+   public String toString() {
+      return "Cookbook [attributes=" + attributes + ", cookbookName=" + 
cookbookName + ", definitions=" + definitions
+            + ", files=" + files + ", libraries=" + libraries + ", metadata=" 
+ metadata + ", name=" + name
+            + ", providers=" + providers + ", recipes=" + recipes + ", 
resources=" + resources + ", rootFiles="
+            + rootFiles + ", templates=" + templates + ", version=" + version 
+ "]";
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/java/org/jclouds/chef/domain/DatabagItem.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/main/java/org/jclouds/chef/domain/DatabagItem.java 
b/apis/chef/src/main/java/org/jclouds/chef/domain/DatabagItem.java
new file mode 100644
index 0000000..ec68d99
--- /dev/null
+++ b/apis/chef/src/main/java/org/jclouds/chef/domain/DatabagItem.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.chef.domain;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import org.jclouds.domain.JsonBall;
+
+/**
+ * An item in a data bag.
+ */
+public class DatabagItem extends JsonBall {
+
+   private final String id;
+
+   public DatabagItem(String id, String value) {
+      super(value);
+      this.id = checkNotNull(id, "id");
+   }
+
+   @Override
+   public int hashCode() {
+      final int prime = 31;
+      int result = super.hashCode();
+      result = prime * result + ((id == null) ? 0 : id.hashCode());
+      return result;
+   }
+
+   @Override
+   public boolean equals(Object obj) {
+      if (this == obj)
+         return true;
+      if (!super.equals(obj))
+         return false;
+      if (getClass() != obj.getClass())
+         return false;
+      DatabagItem other = (DatabagItem) obj;
+      if (id == null) {
+         if (other.id != null)
+            return false;
+      } else if (!id.equals(other.id))
+         return false;
+      return true;
+   }
+
+   public String getId() {
+      return id;
+   }
+}

Reply via email to