JCLOUDS-547: Improved header and option support for Swift/Cloud Files
diff --git a/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/binders/SetPayload.java b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/binders/SetPayload.java
index 70aa6f0..fbc6fe4 100644
--- a/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/binders/SetPayload.java
+++ b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/binders/SetPayload.java
@@ -20,6 +20,12 @@
 import static com.google.common.io.BaseEncoding.base16;
 import static com.google.common.net.HttpHeaders.ETAG;
 import static com.google.common.net.HttpHeaders.TRANSFER_ENCODING;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.OBJECT_DELETE_AT;
+
+import java.util.Date;
+
+import javax.ws.rs.core.MediaType;
 
 import org.jclouds.http.HttpRequest;
 import org.jclouds.http.HttpRequest.Builder;
@@ -32,6 +38,11 @@
    public <R extends HttpRequest> R bindToRequest(R request, Object input) {
       Builder<?> builder = request.toBuilder();
       Payload payload = Payload.class.cast(input);
+
+      if (payload.getContentMetadata().getContentType() == null) {
+         payload.getContentMetadata().setContentType(MediaType.APPLICATION_OCTET_STREAM);
+      }
+
       Long contentLength = payload.getContentMetadata().getContentLength();
       if (contentLength != null && contentLength >= 0) {
          checkArgument(contentLength <= 5l * 1024 * 1024 * 1024, "maximum size for put object is 5GB, %s",
@@ -39,11 +50,19 @@
       } else {
          builder.replaceHeader(TRANSFER_ENCODING, "chunked").build();
       }
+
       byte[] md5 = payload.getContentMetadata().getContentMD5();
       if (md5 != null) {
          // Swift will validate the md5, if placed as an ETag header
          builder.replaceHeader(ETAG, base16().lowerCase().encode(md5));
       }
+
+      Date expires = payload.getContentMetadata().getExpires();
+      if (expires != null) {
+         builder.replaceHeader(OBJECT_DELETE_AT, String.valueOf(
+               MILLISECONDS.toSeconds(expires.getTime())))
+               .build();
+      }
       return (R) builder.payload(payload).build();
    }
 }
diff --git a/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/domain/Account.java b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/domain/Account.java
index 7ec15e0..34b63bc 100644
--- a/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/domain/Account.java
+++ b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/domain/Account.java
@@ -27,6 +27,8 @@
 import com.google.common.base.Objects.ToStringHelper;
 import com.google.common.base.Optional;
 import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.Multimap;
 
 /**
  * Represents an Account in OpenStack Object Storage.
@@ -39,13 +41,16 @@
    private final long objectCount;
    private final long bytesUsed;
    private final Map<String, String> metadata;
+   private final Multimap<String, String> headers;
 
    // parsed from headers, so ConstructorProperties here would be misleading
-   protected Account(long containerCount, long objectCount, long bytesUsed, Map<String, String> metadata) {
+   protected Account(long containerCount, long objectCount, long bytesUsed, Map<String, String> metadata,
+         Multimap<String, String> headers) {
       this.containerCount = containerCount;
       this.objectCount = objectCount;
       this.bytesUsed = bytesUsed;
-      this.metadata = checkNotNull(metadata, "metadata");
+      this.metadata = metadata == null ? ImmutableMap.<String, String> of() : metadata;
+      this.headers = headers == null ? ImmutableMultimap.<String, String> of() : headers;
    }
 
    /**
@@ -87,6 +92,13 @@
       return metadata;
    }
 
+   /**
+    * @return The HTTP headers for this account.
+    */
+   public Multimap<String, String> getHeaders() {
+      return headers;
+   }
+
    @Override
    public boolean equals(Object object) {
       if (this == object) {
@@ -126,13 +138,14 @@
    }
 
    public Builder toBuilder() {
-      return builder().fromContainer(this);
+      return builder().fromAccount(this);
    }
 
    public static class Builder {
       protected long containerCount;
       protected long objectCount;
       protected long bytesUsed;
+      protected Multimap<String, String> headers = ImmutableMultimap.of();
       protected Map<String, String> metadata = ImmutableMap.of();
 
       /**
@@ -183,15 +196,24 @@
          return this;
       }
 
-      public Account build() {
-         return new Account(containerCount, objectCount, bytesUsed, metadata);
+      /**
+       * @see Account#getHeaders()
+       */
+      public Builder headers(Multimap<String, String> headers) {
+        this.headers = headers;
+        return this;
       }
 
-      public Builder fromContainer(Account from) {
+      public Account build() {
+         return new Account(containerCount, objectCount, bytesUsed, metadata, headers);
+      }
+
+      public Builder fromAccount(Account from) {
          return containerCount(from.getContainerCount())
                .objectCount(from.getObjectCount())
                .bytesUsed(from.getBytesUsed())
-               .metadata(from.getMetadata());
+               .metadata(from.getMetadata())
+               .headers(from.getHeaders());
       }
    }
 }
diff --git a/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/domain/Container.java b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/domain/Container.java
index 1c80c4e..cc671de 100644
--- a/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/domain/Container.java
+++ b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/domain/Container.java
@@ -28,6 +28,8 @@
 import com.google.common.base.Objects.ToStringHelper;
 import com.google.common.base.Optional;
 import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.Multimap;
 
 /**
  * Represents a Container in OpenStack Object Storage.
@@ -41,15 +43,17 @@
    private final long bytesUsed;
    private final Optional<Boolean> anybodyRead;
    private final Map<String, String> metadata;
+   private final Multimap<String, String> headers;
 
-   @ConstructorProperties({ "name", "count", "bytes", "anybodyRead", "metadata" })
+   @ConstructorProperties({ "name", "count", "bytes", "anybodyRead", "metadata", "headers"})
    protected Container(String name, long objectCount, long bytesUsed, Optional<Boolean> anybodyRead,
-         Map<String, String> metadata) {
+         Map<String, String> metadata, Multimap<String, String> headers) {
       this.name = checkNotNull(name, "name");
       this.objectCount = objectCount;
       this.bytesUsed = bytesUsed;
       this.anybodyRead = anybodyRead == null ? Optional.<Boolean> absent() : anybodyRead;
       this.metadata = metadata == null ? ImmutableMap.<String, String> of() : metadata;
+      this.headers = headers == null ? ImmutableMultimap.<String, String> of() : headers;
    }
 
    /**
@@ -95,6 +99,13 @@
       return metadata;
    }
 
+   /**
+    * @return The HTTP headers for this account.
+    */
+   public Multimap<String, String> getHeaders() {
+      return headers;
+   }
+
    @Override
    public boolean equals(Object object) {
       if (this == object) {
@@ -153,6 +164,7 @@
       protected long bytesUsed;
       protected Optional<Boolean> anybodyRead = Optional.absent();
       protected Map<String, String> metadata = ImmutableMap.of();
+      protected Multimap<String, String> headers = ImmutableMultimap.of();
 
       /**
        * @see Container#getName()
@@ -201,8 +213,16 @@
          return this;
       }
 
+      /**
+       * @see Container#getHeaders()
+       */
+      public Builder headers(Multimap<String, String> headers) {
+         this.headers = headers;
+         return this;
+      }
+
       public Container build() {
-         return new Container(name, objectCount, bytesUsed, anybodyRead, metadata);
+         return new Container(name, objectCount, bytesUsed, anybodyRead, metadata, headers);
       }
 
       public Builder fromContainer(Container from) {
@@ -210,7 +230,8 @@
                .objectCount(from.getObjectCount())
                .bytesUsed(from.getBytesUsed())
                .anybodyRead(from.getAnybodyRead().orNull())
-               .metadata(from.getMetadata());
+               .metadata(from.getMetadata())
+               .headers(from.getHeaders());
       }
    }
 }
diff --git a/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/domain/SwiftObject.java b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/domain/SwiftObject.java
index 3d00b25..f979c2d 100644
--- a/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/domain/SwiftObject.java
+++ b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/domain/SwiftObject.java
@@ -186,7 +186,7 @@
       protected String etag;
       protected Date lastModified;
       protected Payload payload;
-      protected Multimap<String, String> headers;
+      protected Multimap<String, String> headers = ImmutableMultimap.of();
       protected Map<String, String> metadata = ImmutableMap.of();
 
       /**
diff --git a/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/features/ContainerApi.java b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/features/ContainerApi.java
index 19143dd..0537f33 100644
--- a/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/features/ContainerApi.java
+++ b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/features/ContainerApi.java
@@ -42,6 +42,7 @@
 import org.jclouds.openstack.swift.v1.functions.ParseContainerFromHeaders;
 import org.jclouds.openstack.swift.v1.options.CreateContainerOptions;
 import org.jclouds.openstack.swift.v1.options.ListContainerOptions;
+import org.jclouds.openstack.swift.v1.options.UpdateContainerOptions;
 import org.jclouds.rest.annotations.BinderParam;
 import org.jclouds.rest.annotations.Fallback;
 import org.jclouds.rest.annotations.QueryParams;
@@ -146,13 +147,30 @@
    Container get(@PathParam("containerName") String containerName);
 
    /**
+    * Updates the {@link Container}.
+    *
+    * @param containerName
+    *           the container name corresponding to {@link Container#getName()}.
+    * @param options
+    *           the container options to update.
+    *
+    * @return {@code true} if the container metadata was successfully created or updated,
+    *         {@code false} if not.
+    */
+   @Named("container:update")
+   @POST
+   @Fallback(FalseOnNotFoundOr404.class)
+   @Path("/{containerName}")
+   boolean update(@PathParam("containerName") String containerName, UpdateContainerOptions options);
+
+   /**
     * Creates or updates the {@link Container} metadata.
-    * 
+    *
     * @param containerName
     *           the container name corresponding to {@link Container#getName()}.
     * @param metadata
     *           the container metadata to create or update.
-    * 
+    *
     * @return {@code true} if the container metadata was successfully created or updated,
     *         {@code false} if not.
     */
diff --git a/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/functions/EntriesWithoutMetaPrefix.java b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/functions/EntriesWithoutMetaPrefix.java
index 27e3741..473da3e 100644
--- a/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/functions/EntriesWithoutMetaPrefix.java
+++ b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/functions/EntriesWithoutMetaPrefix.java
@@ -31,7 +31,7 @@
  * 
  * @return the extracted metadata without the prefixed keys. 
  */
-enum EntriesWithoutMetaPrefix implements Function<Multimap<String, String>, Map<String, String>> {
+public enum EntriesWithoutMetaPrefix implements Function<Multimap<String, String>, Map<String, String>> {
    INSTANCE;
 
    @Override
diff --git a/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/options/CreateContainerOptions.java b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/options/CreateContainerOptions.java
index 1fd146d..2014f61 100644
--- a/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/options/CreateContainerOptions.java
+++ b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/options/CreateContainerOptions.java
@@ -19,71 +19,84 @@
 import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.CONTAINER_ACL_ANYBODY_READ;
 import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.CONTAINER_METADATA_PREFIX;
 import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.CONTAINER_READ;
-import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.CONTAINER_VERSIONS_LOCATION;
+import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.VERSIONS_LOCATION;
 
 import java.util.Map;
 
 import org.jclouds.http.options.BaseHttpRequestOptions;
 import org.jclouds.openstack.swift.v1.binders.BindMetadataToHeaders;
 
+import com.google.common.collect.Multimap;
+
 /**
- * Options for creating a {@link Container}. 
+ * Options for creating a {@link Container} 
  * 
  * @see ContainerApi#create(String, CreateContainerOptions)
  */
 public class CreateContainerOptions extends BaseHttpRequestOptions {
-   /**
-    * @deprecated This field will be removed in jclouds 1.8.
-    */
-   @Deprecated
+
    public static final CreateContainerOptions NONE = new CreateContainerOptions();
 
    /** 
-    * Sets the metadata on a container at creation. 
+    * Sets the headers on a container at creation.
     */
-   public CreateContainerOptions metadata(Map<String, String> metadata) {
-      if (!metadata.isEmpty()) {
-         this.headers.putAll(bindMetadataToHeaders.toHeaders(metadata));
-      }
+   public CreateContainerOptions headers(Multimap<String, String> headers) {
+      this.headers.putAll(headers);
       return this;
    }
 
-   /** 
-    * Sets the public ACL on the container so that anybody can read it. 
+   /**
+    * Sets the metadata on a container at creation. 
+    */
+   public CreateContainerOptions metadata(Map<String, String> metadata) {
+      this.headers.putAll(bindMetadataToHeaders.toHeaders(metadata));
+      return this;
+   }
+
+   /**
+    * Sets the public ACL on the container so that anybody can read it.
     */
    public CreateContainerOptions anybodyRead() {
       this.headers.put(CONTAINER_READ, CONTAINER_ACL_ANYBODY_READ);
       return this;
    }
 
-   /** 
+   /**
     * Sets the container that will contain object versions.
     */
    public CreateContainerOptions versionsLocation(String containerName) {
-      this.headers.put(CONTAINER_VERSIONS_LOCATION, containerName);
+      this.headers.put(VERSIONS_LOCATION, containerName);
       return this;
    }
 
    public static class Builder {
 
-      /** 
-       * @see CreateContainerOptions#anybodyRead 
+      /**
+       * @see CreateContainerOptions#anybodyRead
        */
       public static CreateContainerOptions anybodyRead() {
          CreateContainerOptions options = new CreateContainerOptions();
          return options.anybodyRead();
       }
 
-      /** 
-       * @see CreateContainerOptions#metadata 
+      /**
+       * @see CreateContainerOptions#headers
+       */
+      public static CreateContainerOptions headers(Multimap<String, String> headers) {
+         CreateContainerOptions options = new CreateContainerOptions();
+         return options.headers(headers);
+      }
+
+      /**
+       * @see CreateContainerOptions#metadata
        */
       public static CreateContainerOptions metadata(Map<String, String> metadata) {
          CreateContainerOptions options = new CreateContainerOptions();
          return options.metadata(metadata);
       }
 
-      /** 
-       * @see CreateContainerOptions#versionsLocation 
+      /**
+       * @see CreateContainerOptions#versionsLocation
        */
       public static CreateContainerOptions versionsLocation(String containerName) {
          CreateContainerOptions options = new CreateContainerOptions();
diff --git a/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/options/PutOptions.java b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/options/PutOptions.java
index 1897e6d..d4a1ed3 100644
--- a/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/options/PutOptions.java
+++ b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/options/PutOptions.java
@@ -23,36 +23,48 @@
 import org.jclouds.http.options.BaseHttpRequestOptions;
 import org.jclouds.openstack.swift.v1.binders.BindMetadataToHeaders;
 
+import com.google.common.collect.Multimap;
+
 /**
  * Options for creating an Object. 
  */
 public class PutOptions extends BaseHttpRequestOptions {
-   /**
-    * @deprecated This field will be removed in jclouds 1.8
-    */
-   @Deprecated
+
    public static final PutOptions NONE = new PutOptions();
 
-   /** 
-    * Sets the metadata on a container at creation. 
+   /**
+    * Sets the metadata on a container at creation.
     */
    public PutOptions metadata(Map<String, String> metadata) {
-      if (!metadata.isEmpty()) {
-         this.headers.putAll(bindMetadataToHeaders.toHeaders(metadata));
-      }
+      this.headers.putAll(bindMetadataToHeaders.toHeaders(metadata));
+      return this;
+   }
+
+   /**
+    * Sets the headers on a container at creation.
+    */
+   public PutOptions headers(Multimap<String, String> headers) {
+      this.headers.putAll(headers);
       return this;
    }
 
    public static class Builder {
 
-      /** 
-       * @see PutOptions#metadata 
+      /**
+       * @see PutOptions#headers
+       */
+      public static PutOptions headers(Multimap<String, String> headers) {
+         PutOptions options = new PutOptions();
+         return options.headers(headers);
+      }
+
+      /**
+       * @see PutOptions#metadata
        */
       public static PutOptions metadata(Map<String, String> metadata) {
          PutOptions options = new PutOptions();
          return options.metadata(metadata);
       }
-
    }
 
    private static final BindMetadataToHeaders bindMetadataToHeaders = new BindMetadataToHeaders(OBJECT_METADATA_PREFIX);
diff --git a/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/options/UpdateContainerOptions.java b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/options/UpdateContainerOptions.java
new file mode 100644
index 0000000..f74b7fe
--- /dev/null
+++ b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/options/UpdateContainerOptions.java
@@ -0,0 +1,107 @@
+/*
+ * 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.openstack.swift.v1.options;
+
+import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.CONTAINER_ACL_ANYBODY_READ;
+import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.CONTAINER_METADATA_PREFIX;
+import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.CONTAINER_READ;
+import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.VERSIONS_LOCATION;
+
+import java.util.Map;
+
+import org.jclouds.http.options.BaseHttpRequestOptions;
+import org.jclouds.openstack.swift.v1.binders.BindMetadataToHeaders;
+
+import com.google.common.collect.Multimap;
+
+/**
+ * Options for updating a {@link Container}.
+ *
+ * @see org.jclouds.openstack.swift.v1.features.ContainerApi#update(String, UpdateContainerOptions)
+ */
+public class UpdateContainerOptions extends BaseHttpRequestOptions {
+   public static final UpdateContainerOptions NONE = new UpdateContainerOptions();
+
+   /**
+    * Sets the headers on a container at creation.
+    */
+   public UpdateContainerOptions headers(Multimap<String, String> headers) {
+      this.headers.putAll(headers);
+      return this;
+   }
+
+   /**
+    * Sets the metadata on a container at creation.
+    */
+   public UpdateContainerOptions metadata(Map<String, String> metadata) {
+      this.headers.putAll(bindMetadataToHeaders.toHeaders(metadata));
+      return this;
+   }
+
+   /**
+    * Sets the public ACL on the container so that anybody can read it.
+    */
+   public UpdateContainerOptions anybodyRead() {
+      this.headers.put(CONTAINER_READ, CONTAINER_ACL_ANYBODY_READ);
+      return this;
+   }
+
+   /**
+    * Sets the container that will contain object versions.
+    */
+   public UpdateContainerOptions versionsLocation(String containerName) {
+      this.headers.put(VERSIONS_LOCATION, containerName);
+      return this;
+   }
+
+   public static class Builder {
+
+      /**
+       * @see UpdateContainerOptions#anybodyRead
+       */
+      public static UpdateContainerOptions anybodyRead() {
+         UpdateContainerOptions options = new UpdateContainerOptions();
+         return options.anybodyRead();
+      }
+
+      /**
+       * @see UpdateContainerOptions#headers
+       */
+      public static UpdateContainerOptions headers(Multimap<String, String> headers) {
+         UpdateContainerOptions options = new UpdateContainerOptions();
+         return options.headers(headers);
+      }
+
+      /**
+       * @see UpdateContainerOptions#metadata
+       */
+      public static UpdateContainerOptions metadata(Map<String, String> metadata) {
+         UpdateContainerOptions options = new UpdateContainerOptions();
+         return options.metadata(metadata);
+      }
+
+      /**
+       * @see UpdateContainerOptions#versionsLocation
+       */
+      public static UpdateContainerOptions versionsLocation(String containerName) {
+         UpdateContainerOptions options = new UpdateContainerOptions();
+         return options.versionsLocation(containerName);
+      }
+   }
+
+   private static final BindMetadataToHeaders bindMetadataToHeaders = new BindMetadataToHeaders(CONTAINER_METADATA_PREFIX);
+}
diff --git a/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/reference/SwiftHeaders.java b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/reference/SwiftHeaders.java
index baffbb5..73fc0e0 100644
--- a/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/reference/SwiftHeaders.java
+++ b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/reference/SwiftHeaders.java
@@ -66,6 +66,12 @@
    String CONTAINER_SYNC_TO = "X-Container-Sync-To";
 
    // Versioning
+   String VERSIONS_LOCATION = "X-Versions-Location";
+
+   /**
+    * @deprecated Please use {@link #VERSIONS_LOCATION}. This field will be removed in jclouds 1.8.
+    */
+   @Deprecated
    String CONTAINER_VERSIONS_LOCATION = "X-Versions-Location";
 
    // Misc functionality
diff --git a/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/ContainerApiLiveTest.java b/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/ContainerApiLiveTest.java
index dc2b6cc..04585a0 100644
--- a/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/ContainerApiLiveTest.java
+++ b/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/ContainerApiLiveTest.java
@@ -27,13 +27,17 @@
 import org.jclouds.openstack.swift.v1.SwiftApi;
 import org.jclouds.openstack.swift.v1.domain.Container;
 import org.jclouds.openstack.swift.v1.internal.BaseSwiftApiLiveTest;
+import org.jclouds.openstack.swift.v1.options.CreateContainerOptions;
 import org.jclouds.openstack.swift.v1.options.ListContainerOptions;
+import org.jclouds.openstack.swift.v1.options.UpdateContainerOptions;
+import org.jclouds.openstack.swift.v1.reference.SwiftHeaders;
 import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
 import com.google.common.collect.FluentIterable;
 import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMultimap;
 
 /**
  * Provides live tests for the {@link ContainerApi}.
@@ -43,6 +47,26 @@
 
    private String name = getClass().getSimpleName();
 
+   public void testCreateWithOptions() throws Exception {
+      for (String regionId : regions) {
+         ImmutableMultimap<String, String> headers =
+               ImmutableMultimap.of(SwiftHeaders.STATIC_WEB_INDEX, "__index.html",
+                                    SwiftHeaders.STATIC_WEB_ERROR, "__error.html");
+         CreateContainerOptions opts = new CreateContainerOptions().headers(headers);
+
+         assertNotNull(api.getContainerApiForRegion(regionId).create(name, opts));
+
+         Container container = api.getContainerApiForRegion(regionId).get(name);
+         assertNotNull(container);
+         assertEquals(container.getName(), name);
+         assertEquals(container.getMetadata().size(), 2);
+         assertEquals(container.getMetadata().get("web-index"), "__index.html");
+         assertEquals(container.getMetadata().get("web-error"), "__error.html");
+
+         assertTrue(api.getContainerApiForRegion(regionId).deleteIfEmpty(name));
+      }
+   }
+
    public void testCreateWithSpacesAndSpecialCharacters() throws Exception {
       final String nameWithSpaces = "container # ! special";
 
@@ -80,6 +104,33 @@
       }
    }
 
+   public void testUpdate() throws Exception {
+      for (String regionId : regions) {
+         ImmutableMultimap<String, String> headers =
+               ImmutableMultimap.of(SwiftHeaders.STATIC_WEB_INDEX, "__index.html",
+                                    SwiftHeaders.STATIC_WEB_ERROR, "__error.html");
+         UpdateContainerOptions opts = new UpdateContainerOptions().headers(headers);
+
+         assertNotNull(api.getContainerApiForRegion(regionId).create(name));
+
+         Container container = api.getContainerApiForRegion(regionId).get(name);
+         assertNotNull(container);
+         assertEquals(container.getName(), name);
+         assertTrue(container.getMetadata().isEmpty());
+
+         assertNotNull(api.getContainerApiForRegion(regionId).update(name, opts));
+
+         Container updatedContainer = api.getContainerApiForRegion(regionId).get(name);
+         assertNotNull(updatedContainer);
+         assertEquals(updatedContainer.getName(), name);
+         assertEquals(updatedContainer.getMetadata().size(), 2);
+         assertEquals(updatedContainer.getMetadata().get("web-index"), "__index.html");
+         assertEquals(updatedContainer.getMetadata().get("web-error"), "__error.html");
+
+         assertTrue(api.getContainerApiForRegion(regionId).deleteIfEmpty(name));
+      }
+   }
+
    public void testGet() throws Exception {
       for (String regionId : regions) {
          Container container = api.getContainerApiForRegion(regionId).get(name);
diff --git a/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/options/CreateContainerOptionsTest.java b/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/options/CreateContainerOptionsTest.java
new file mode 100644
index 0000000..1da648b
--- /dev/null
+++ b/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/options/CreateContainerOptionsTest.java
@@ -0,0 +1,69 @@
+/*
+ * 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.openstack.swift.v1.options;
+
+import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.CONTAINER_ACL_ANYBODY_READ;
+import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.CONTAINER_METADATA_PREFIX;
+import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.CONTAINER_READ;
+import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.VERSIONS_LOCATION;
+import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.CONTAINER_QUOTA_BYTES;
+import static org.testng.Assert.assertEquals;
+
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.Multimap;
+
+/**
+ * Tests behavior of {@link CreateContainerOptions}.
+ */
+@Test(groups = "unit")
+public class CreateContainerOptionsTest {
+
+   public void testMetadata() {
+      CreateContainerOptions options =
+            new CreateContainerOptions().metadata(ImmutableMap.of("ApiName", "swift", "metaKey2", "Value2", "METAKEY3", "VALUE 3 "));
+
+      Multimap<String, String> headers = options.buildRequestHeaders();
+      assertEquals(headers.get(CONTAINER_METADATA_PREFIX + "apiname"), ImmutableList.of("swift"));
+      assertEquals(headers.get(CONTAINER_METADATA_PREFIX + "metakey2"), ImmutableList.of("Value2"));
+      assertEquals(headers.get(CONTAINER_METADATA_PREFIX + "metakey3"), ImmutableList.of("VALUE 3 "));
+   }
+
+   public void testHeaders() {
+      CreateContainerOptions options =
+            new CreateContainerOptions().headers(ImmutableMultimap.of(CONTAINER_QUOTA_BYTES, "5120", CONTAINER_METADATA_PREFIX + "apiname", "swift"));
+
+      Multimap<String, String> headers = options.buildRequestHeaders();
+      assertEquals(headers.get(CONTAINER_QUOTA_BYTES), ImmutableList.of("5120"));
+      assertEquals(headers.get(CONTAINER_METADATA_PREFIX + "apiname"), ImmutableList.of("swift"));
+   }
+
+   public void testAnybodyRead() {
+      CreateContainerOptions options =
+            new CreateContainerOptions().headers(ImmutableMultimap.of(CONTAINER_READ, CONTAINER_ACL_ANYBODY_READ));
+      assertEquals(options.buildRequestHeaders().get(CONTAINER_READ), ImmutableList.of(CONTAINER_ACL_ANYBODY_READ));
+   }
+
+   public void testVersionsLocation() {
+      CreateContainerOptions options =
+            new CreateContainerOptions().headers(ImmutableMultimap.of(VERSIONS_LOCATION, "containerWithVersions"));
+      assertEquals(options.buildRequestHeaders().get(VERSIONS_LOCATION), ImmutableList.of("containerWithVersions"));
+   }
+}
diff --git a/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/options/PutOptionsTest.java b/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/options/PutOptionsTest.java
new file mode 100644
index 0000000..fddf4cd
--- /dev/null
+++ b/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/options/PutOptionsTest.java
@@ -0,0 +1,48 @@
+/*
+ * 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.openstack.swift.v1.options;
+
+import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.OBJECT_METADATA_PREFIX;
+import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.OBJECT_DELETE_AT;
+import static org.testng.Assert.assertEquals;
+
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMultimap;
+
+/**
+ * Tests behavior of {@link PutOptions}.
+ */
+@Test(groups = "unit")
+public class PutOptionsTest {
+
+   public void testPutMetadata() {
+      PutOptions options = 
+            new PutOptions().metadata(ImmutableMap.of("ApiName", "swift"));
+      assertEquals(options.buildRequestHeaders().get(OBJECT_METADATA_PREFIX + "apiname"), ImmutableList.of("swift"));
+
+   }
+
+   public void testPutHeaders() {
+      PutOptions options = 
+            new PutOptions().headers(ImmutableMultimap.of(OBJECT_DELETE_AT, "123456789"));
+      assertEquals(options.buildRequestHeaders().get(OBJECT_DELETE_AT), ImmutableList.of("123456789"));
+   }
+
+}
diff --git a/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/options/UpdateContainerOptionsTest.java b/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/options/UpdateContainerOptionsTest.java
new file mode 100644
index 0000000..00f95f7
--- /dev/null
+++ b/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/options/UpdateContainerOptionsTest.java
@@ -0,0 +1,122 @@
+/*
+ * 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.openstack.swift.v1.options;
+
+import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.CONTAINER_ACL_ANYBODY_READ;
+import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.CONTAINER_METADATA_PREFIX;
+import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.CONTAINER_QUOTA_BYTES;
+import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.CONTAINER_READ;
+import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.VERSIONS_LOCATION;
+import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.STATIC_WEB_DIRECTORY_TYPE;
+import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.STATIC_WEB_ERROR;
+import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.STATIC_WEB_INDEX;
+import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.STATIC_WEB_LISTINGS;
+import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.STATIC_WEB_LISTINGS_CSS;
+import static org.testng.Assert.assertEquals;
+
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.Multimap;
+import com.google.common.net.MediaType;
+
+/**
+ * Tests behavior of {@link UpdateContainerOptions}.
+ */
+@Test(groups = "unit")
+public class UpdateContainerOptionsTest {
+
+   public void testAnybodyRead() {
+      UpdateContainerOptions options = new UpdateContainerOptions().anybodyRead();
+      assertEquals(options.buildRequestHeaders().get(CONTAINER_READ), ImmutableList.of(CONTAINER_ACL_ANYBODY_READ));
+   }
+
+   public void testAnybodyReadViaHeaders() {
+      UpdateContainerOptions options =
+            new UpdateContainerOptions().headers(ImmutableMultimap.of(CONTAINER_READ, CONTAINER_ACL_ANYBODY_READ));
+      assertEquals(options.buildRequestHeaders().get(CONTAINER_READ), ImmutableList.of(CONTAINER_ACL_ANYBODY_READ));
+   }
+
+   public void testVersionsLocation() {
+      UpdateContainerOptions options = new UpdateContainerOptions().versionsLocation("containerWithVersions");
+      assertEquals(options.buildRequestHeaders().get(VERSIONS_LOCATION), ImmutableList.of("containerWithVersions"));
+   }
+
+   public void testVersionsLocationViaHeaders() {
+      UpdateContainerOptions options =
+            new UpdateContainerOptions().headers(ImmutableMultimap.of(VERSIONS_LOCATION, "containerWithVersions"));
+      assertEquals(options.buildRequestHeaders().get(VERSIONS_LOCATION), ImmutableList.of("containerWithVersions"));
+   }
+
+   public void testMetadata() {
+      UpdateContainerOptions options =
+            new UpdateContainerOptions().metadata(ImmutableMap.of("ApiName", "swift", "metaKey2", "Value2", "METAKEY3", "VALUE 3 "));
+
+      Multimap<String, String> headers = options.buildRequestHeaders();
+      assertEquals(headers.get(CONTAINER_METADATA_PREFIX + "apiname"), ImmutableList.of("swift"));
+      assertEquals(headers.get(CONTAINER_METADATA_PREFIX + "metakey2"), ImmutableList.of("Value2"));
+      assertEquals(headers.get(CONTAINER_METADATA_PREFIX + "metakey3"), ImmutableList.of("VALUE 3 "));
+   }
+
+   public void testHeaders() {
+      UpdateContainerOptions options =
+            new UpdateContainerOptions().headers(ImmutableMultimap.of(CONTAINER_QUOTA_BYTES, "5120", CONTAINER_METADATA_PREFIX + "apiname", "swift"));
+
+      Multimap<String, String> headers = options.buildRequestHeaders();
+      assertEquals(headers.get(CONTAINER_QUOTA_BYTES), ImmutableList.of("5120"));
+      assertEquals(headers.get(CONTAINER_METADATA_PREFIX + "apiname"), ImmutableList.of("swift"));
+   }
+
+   public void testStaticWebsiteDirectoryType() {
+      MediaType appDir = MediaType.create("application", "directory");
+      Multimap<String, String> headers = ImmutableMultimap.of(STATIC_WEB_DIRECTORY_TYPE, appDir.toString());
+      UpdateContainerOptions options = new UpdateContainerOptions().headers(headers);
+      assertEquals(options.buildRequestHeaders().get(STATIC_WEB_DIRECTORY_TYPE), ImmutableList.of(appDir.toString()));
+   }
+
+   public void testStaticWebsiteIndexPage() {
+      Multimap<String, String> headers = ImmutableMultimap.of(STATIC_WEB_INDEX, "index.html");
+      UpdateContainerOptions options = new UpdateContainerOptions().headers(headers);
+      assertEquals(options.buildRequestHeaders().get(STATIC_WEB_INDEX), ImmutableList.of("index.html"));
+   }
+
+   public void testStaticWebsiteErrorPage() {
+      Multimap<String, String> headers = ImmutableMultimap.of(STATIC_WEB_ERROR, "error.html");
+      UpdateContainerOptions options = new UpdateContainerOptions().headers(headers);
+      assertEquals(options.buildRequestHeaders().get(STATIC_WEB_ERROR), ImmutableList.of("error.html"));
+   }
+
+   public void testEnableStaticWebsiteListings() {
+      Multimap<String, String> headers = ImmutableMultimap.of(STATIC_WEB_LISTINGS, "true");
+      UpdateContainerOptions options = new UpdateContainerOptions().headers(headers);
+      assertEquals(options.buildRequestHeaders().get(STATIC_WEB_LISTINGS), ImmutableList.of("true"));
+   }
+
+   public void testDiableStaticWebsiteListings() {
+      Multimap<String, String> headers = ImmutableMultimap.of(STATIC_WEB_LISTINGS, "false");
+      UpdateContainerOptions options = new UpdateContainerOptions().headers(headers);
+      assertEquals(options.buildRequestHeaders().get(STATIC_WEB_LISTINGS), ImmutableList.of("false"));
+   }
+
+   public void testStaticWebsiteListingsCSS() {
+      Multimap<String, String> headers = ImmutableMultimap.of(STATIC_WEB_LISTINGS_CSS, "listings.css");
+      UpdateContainerOptions options = new UpdateContainerOptions().headers(headers);
+      assertEquals(options.buildRequestHeaders().get(STATIC_WEB_LISTINGS_CSS), ImmutableList.of("listings.css"));
+   }
+}
diff --git a/rackspace-cloudfiles/src/main/java/org/jclouds/rackspace/cloudfiles/v1/CloudFilesApi.java b/rackspace-cloudfiles/src/main/java/org/jclouds/rackspace/cloudfiles/v1/CloudFilesApi.java
index 9750490..c59927a 100644
--- a/rackspace-cloudfiles/src/main/java/org/jclouds/rackspace/cloudfiles/v1/CloudFilesApi.java
+++ b/rackspace-cloudfiles/src/main/java/org/jclouds/rackspace/cloudfiles/v1/CloudFilesApi.java
@@ -45,4 +45,17 @@
     */
    @Delegate
    CDNApi getCDNApiForRegion(@EndpointParam(parser = RegionToCDNEndpoint.class) @Nullable String region);
+
+   /**
+    * Provides access to Cloud Files CDN features.
+    * 
+    * @param region  the region to access the CDN API.
+    * 
+    * @return the {@link CDNApi} for the specified region.
+    * 
+    * @deprecated Please use {@link #getCDNApiForRegion(String)}. This method will be removed in jclouds 1.8.
+    */
+   @Deprecated
+   @Delegate
+   CDNApi cdnApiInRegion(@EndpointParam(parser = RegionToCDNEndpoint.class) @Nullable String region);
 }
diff --git a/rackspace-cloudfiles/src/main/java/org/jclouds/rackspace/cloudfiles/v1/features/CDNApi.java b/rackspace-cloudfiles/src/main/java/org/jclouds/rackspace/cloudfiles/v1/features/CDNApi.java
index ccc1177..b3a2514 100644
--- a/rackspace-cloudfiles/src/main/java/org/jclouds/rackspace/cloudfiles/v1/features/CDNApi.java
+++ b/rackspace-cloudfiles/src/main/java/org/jclouds/rackspace/cloudfiles/v1/features/CDNApi.java
@@ -39,12 +39,12 @@
 import org.jclouds.Fallbacks.NullOnNotFoundOr404;
 import org.jclouds.javax.annotation.Nullable;
 import org.jclouds.openstack.keystone.v2_0.filters.AuthenticateRequest;
-import org.jclouds.openstack.swift.v1.options.ListContainerOptions;
 import org.jclouds.rackspace.cloudfiles.v1.CloudFilesApi;
 import org.jclouds.rackspace.cloudfiles.v1.binders.BindCDNPurgeEmailAddressesToHeaders;
 import org.jclouds.rackspace.cloudfiles.v1.domain.CDNContainer;
 import org.jclouds.rackspace.cloudfiles.v1.functions.ParseCDNContainerFromHeaders;
 import org.jclouds.rackspace.cloudfiles.v1.functions.ParseCDNContainerURIFromHeaders;
+import org.jclouds.rackspace.cloudfiles.v1.options.ListCDNContainerOptions;
 import org.jclouds.rackspace.cloudfiles.v1.options.UpdateCDNContainerOptions;
 import org.jclouds.rackspace.cloudfiles.v1.reference.CloudFilesHeaders;
 import org.jclouds.rest.annotations.BinderParam;
@@ -95,7 +95,7 @@
    @QueryParams(keys = {"format", "enabled_only"}, values = {"json", "true"})
    @Fallback(EmptyFluentIterableOnNotFoundOr404.class)
    @Path("/")
-   FluentIterable<CDNContainer> list(ListContainerOptions options);
+   FluentIterable<CDNContainer> list(ListCDNContainerOptions options);
 
    /**
     * Gets the specified CDN Container.
diff --git a/rackspace-cloudfiles/src/main/java/org/jclouds/rackspace/cloudfiles/v1/options/ListCDNContainerOptions.java b/rackspace-cloudfiles/src/main/java/org/jclouds/rackspace/cloudfiles/v1/options/ListCDNContainerOptions.java
new file mode 100644
index 0000000..35baa53
--- /dev/null
+++ b/rackspace-cloudfiles/src/main/java/org/jclouds/rackspace/cloudfiles/v1/options/ListCDNContainerOptions.java
@@ -0,0 +1,86 @@
+/*
+ * 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.rackspace.cloudfiles.v1.options;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+
+import org.jclouds.http.options.BaseHttpRequestOptions;
+
+/**
+ * Options for listing containers. 
+ * 
+ * @see {@link org.jclouds.rackspace.cloudfiles.v1.features.CDNAp#list(ListCDNContainerOptions) CDNApi.list(ListCDNContainerOptions)}
+ */
+public class ListCDNContainerOptions extends BaseHttpRequestOptions {
+
+   /** 
+    * For an integer value <i>n</i>, limits the number of results to <i>n</n>. 
+    */
+   public ListCDNContainerOptions limit(int limit) {
+      checkState(limit >= 0, "limit must be >= 0");
+      checkState(limit <= 10000, "limit must be <= 10000");
+      queryParameters.put("limit", Integer.toString(limit));
+      return this;
+   }
+
+   /** 
+    * Given a string value <i>x</i>, returns container names greater in value than the specified
+    * {@code marker}. Only strings using UTF-8 encoding are valid. Using {@code marker} provides
+    * a mechanism for iterating through the entire list of containers.
+    */
+   public ListCDNContainerOptions marker(String marker) {
+      queryParameters.put("marker", checkNotNull(marker, "marker"));
+      return this;
+   }
+
+   /** 
+    * Given a string value <i>x</i>, returns container names lesser in value than the specified 
+    * end marker. Only strings using UTF-8 encoding are valid.
+    */
+   public ListCDNContainerOptions endMarker(String endMarker) {
+      queryParameters.put("end_marker", checkNotNull(endMarker, "endMarker"));
+      return this;
+   }
+
+   public static class Builder {
+
+      /** 
+       * @see ListCDNContainerOptions#limit
+       */
+      public static ListCDNContainerOptions limit(int limit) {
+         ListCDNContainerOptions options = new ListCDNContainerOptions();
+         return options.limit(limit);
+      }
+
+      /** 
+       * @see ListCDNContainerOptions#marker
+       */
+      public static ListCDNContainerOptions marker(String marker) {
+         ListCDNContainerOptions options = new ListCDNContainerOptions();
+         return options.marker(marker);
+      }
+
+      /** 
+       * @see ListCDNContainerOptions#endMarker
+       */
+      public static ListCDNContainerOptions endMarker(String endMarker) {
+         ListCDNContainerOptions options = new ListCDNContainerOptions();
+         return options.endMarker(endMarker);
+      }
+   }
+}
diff --git a/rackspace-cloudfiles/src/main/java/org/jclouds/rackspace/cloudfiles/v1/options/UpdateCDNContainerOptions.java b/rackspace-cloudfiles/src/main/java/org/jclouds/rackspace/cloudfiles/v1/options/UpdateCDNContainerOptions.java
index 0fcb177..5f6d396 100644
--- a/rackspace-cloudfiles/src/main/java/org/jclouds/rackspace/cloudfiles/v1/options/UpdateCDNContainerOptions.java
+++ b/rackspace-cloudfiles/src/main/java/org/jclouds/rackspace/cloudfiles/v1/options/UpdateCDNContainerOptions.java
@@ -23,11 +23,11 @@
 import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.STATIC_WEB_INDEX;
 import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.STATIC_WEB_LISTINGS;
 import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.STATIC_WEB_LISTINGS_CSS;
+import static org.jclouds.rackspace.cloudfiles.v1.reference.CloudFilesConstants.CDN_TTL_MAX;
+import static org.jclouds.rackspace.cloudfiles.v1.reference.CloudFilesConstants.CDN_TTL_MIN;
 import static org.jclouds.rackspace.cloudfiles.v1.reference.CloudFilesHeaders.CDN_ENABLED;
 import static org.jclouds.rackspace.cloudfiles.v1.reference.CloudFilesHeaders.CDN_LOG_RETENTION;
 import static org.jclouds.rackspace.cloudfiles.v1.reference.CloudFilesHeaders.CDN_TTL;
-import static org.jclouds.rackspace.cloudfiles.v1.reference.CloudFilesHeaders.CDN_TTL_MAX;
-import static org.jclouds.rackspace.cloudfiles.v1.reference.CloudFilesHeaders.CDN_TTL_MIN;
 
 import org.jclouds.http.options.BaseHttpRequestOptions;
 
diff --git a/rackspace-cloudfiles/src/main/java/org/jclouds/rackspace/cloudfiles/v1/reference/CloudFilesConstants.java b/rackspace-cloudfiles/src/main/java/org/jclouds/rackspace/cloudfiles/v1/reference/CloudFilesConstants.java
new file mode 100644
index 0000000..095ea10
--- /dev/null
+++ b/rackspace-cloudfiles/src/main/java/org/jclouds/rackspace/cloudfiles/v1/reference/CloudFilesConstants.java
@@ -0,0 +1,27 @@
+/*
+ * 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.rackspace.cloudfiles.v1.reference;
+
+
+/**
+ * Constants specified by Rackspace Cloud Files.
+ */
+public interface CloudFilesConstants {
+   int CDN_TTL_MIN = 900;
+   int CDN_TTL_MAX = 31536000;
+   int CDN_TTL_DEFAULT = 259200;
+}
diff --git a/rackspace-cloudfiles/src/main/java/org/jclouds/rackspace/cloudfiles/v1/reference/CloudFilesHeaders.java b/rackspace-cloudfiles/src/main/java/org/jclouds/rackspace/cloudfiles/v1/reference/CloudFilesHeaders.java
index c6d2bc5..c4f9983 100644
--- a/rackspace-cloudfiles/src/main/java/org/jclouds/rackspace/cloudfiles/v1/reference/CloudFilesHeaders.java
+++ b/rackspace-cloudfiles/src/main/java/org/jclouds/rackspace/cloudfiles/v1/reference/CloudFilesHeaders.java
@@ -19,13 +19,8 @@
 import org.jclouds.openstack.swift.v1.reference.SwiftHeaders;
 
 /**
- * Additional headers specified by Rackspace Cloud Files CDN.
+ * Additional headers specified by Rackspace Cloud Files.
  * 
- * @see <a
- *      href="http://docs.rackspace.com/files/api/v1/cf-devguide/content/index.html">
- *      Cloud Files API</a>
- *      
- * @author Jeremy Daggett
  */
 public interface CloudFilesHeaders extends SwiftHeaders {
    // Access logs
@@ -40,11 +35,6 @@
    String CDN_STREAMING_URI = "X-Cdn-Streaming-Uri";
    String CDN_IOS_URI = "X-Cdn-Ios-Uri";
 
-   // CDN TTL Limits
-   int CDN_TTL_MIN = 900;
-   int CDN_TTL_MAX = 31536000;
-   int CDN_TTL_DEFAULT = 259200;
-
    // CDN Purge
    String CDN_PURGE_OBJECT_EMAIL = "X-Purge-Email";
    String CDN_PURGE_OBJECT_FAILED = "X-Purge-Failed-Reason";
diff --git a/rackspace-cloudfiles/src/test/java/org/jclouds/rackspace/cloudfiles/v1/features/CloudFilesCDNApiLiveTest.java b/rackspace-cloudfiles/src/test/java/org/jclouds/rackspace/cloudfiles/v1/features/CloudFilesCDNApiLiveTest.java
index 785ae61..3c68bee 100644
--- a/rackspace-cloudfiles/src/test/java/org/jclouds/rackspace/cloudfiles/v1/features/CloudFilesCDNApiLiveTest.java
+++ b/rackspace-cloudfiles/src/test/java/org/jclouds/rackspace/cloudfiles/v1/features/CloudFilesCDNApiLiveTest.java
@@ -28,17 +28,15 @@
 import org.jclouds.io.Payload;
 import org.jclouds.io.Payloads;
 import org.jclouds.openstack.swift.v1.features.ObjectApi;
-import org.jclouds.openstack.swift.v1.options.CreateContainerOptions;
-import org.jclouds.openstack.swift.v1.options.ListContainerOptions;
 import org.jclouds.rackspace.cloudfiles.v1.domain.CDNContainer;
 import org.jclouds.rackspace.cloudfiles.v1.internal.BaseCloudFilesApiLiveTest;
+import org.jclouds.rackspace.cloudfiles.v1.options.ListCDNContainerOptions;
 import org.jclouds.rackspace.cloudfiles.v1.options.UpdateCDNContainerOptions;
 import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
 import com.google.common.io.ByteSource;
 
 
@@ -101,7 +99,7 @@
    public void testListWithOptions() throws Exception {
       String lexicographicallyBeforeName = name.substring(0, name.length() - 1);
       for (String regionId : regions) {
-         ListContainerOptions options = ListContainerOptions.Builder.marker(lexicographicallyBeforeName);
+         ListCDNContainerOptions options = new ListCDNContainerOptions().marker(lexicographicallyBeforeName);
           
          CDNContainer cdnContainer = api.getCDNApiForRegion(regionId).list(options).get(0);
          assertCDNContainerNotNull(cdnContainer);
diff --git a/rackspace-cloudfiles/src/test/java/org/jclouds/rackspace/cloudfiles/v1/features/CloudFilesCDNApiMockTest.java b/rackspace-cloudfiles/src/test/java/org/jclouds/rackspace/cloudfiles/v1/features/CloudFilesCDNApiMockTest.java
index b60798e..c8e68d2 100644
--- a/rackspace-cloudfiles/src/test/java/org/jclouds/rackspace/cloudfiles/v1/features/CloudFilesCDNApiMockTest.java
+++ b/rackspace-cloudfiles/src/test/java/org/jclouds/rackspace/cloudfiles/v1/features/CloudFilesCDNApiMockTest.java
@@ -35,10 +35,10 @@
 import java.net.URI;
 import java.util.List;
 
-import org.jclouds.openstack.swift.v1.options.ListContainerOptions;
 import org.jclouds.openstack.v2_0.internal.BaseOpenStackMockTest;
 import org.jclouds.rackspace.cloudfiles.v1.CloudFilesApi;
 import org.jclouds.rackspace.cloudfiles.v1.domain.CDNContainer;
+import org.jclouds.rackspace.cloudfiles.v1.options.ListCDNContainerOptions;
 import org.testng.annotations.Test;
 
 import com.google.common.collect.FluentIterable;
@@ -105,7 +105,7 @@
 
       try {
          CloudFilesApi api = api(server.getUrl("/").toString(), "rackspace-cloudfiles");
-         ListContainerOptions options = ListContainerOptions.Builder.marker("cdn-container-3");
+         ListCDNContainerOptions options = new ListCDNContainerOptions().marker("cdn-container-3");
          ImmutableList<CDNContainer> containers = api.getCDNApiForRegion("DFW").list(options).toList();
 
          for(CDNContainer container : containers) {
@@ -129,7 +129,7 @@
 
       try {
          CloudFilesApi api = api(server.getUrl("/").toString(), "rackspace-cloudfiles");
-         ListContainerOptions options = ListContainerOptions.Builder.marker("cdn-container-3");
+         ListCDNContainerOptions options = ListCDNContainerOptions.Builder.marker("cdn-container-3");
          FluentIterable<CDNContainer> containers = api.getCDNApiForRegion("DFW").list(options);
 
          assertEquals(server.getRequestCount(), 2);
diff --git a/rackspace-cloudfiles/src/test/java/org/jclouds/rackspace/cloudfiles/v1/options/UpdateCDNContainerOptionsTest.java b/rackspace-cloudfiles/src/test/java/org/jclouds/rackspace/cloudfiles/v1/options/UpdateCDNContainerOptionsTest.java
index 3ff8c00..8d7b7e0 100644
--- a/rackspace-cloudfiles/src/test/java/org/jclouds/rackspace/cloudfiles/v1/options/UpdateCDNContainerOptionsTest.java
+++ b/rackspace-cloudfiles/src/test/java/org/jclouds/rackspace/cloudfiles/v1/options/UpdateCDNContainerOptionsTest.java
@@ -21,10 +21,10 @@
 import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.STATIC_WEB_INDEX;
 import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.STATIC_WEB_LISTINGS;
 import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.STATIC_WEB_LISTINGS_CSS;
+import static org.jclouds.rackspace.cloudfiles.v1.reference.CloudFilesConstants.CDN_TTL_MAX;
+import static org.jclouds.rackspace.cloudfiles.v1.reference.CloudFilesConstants.CDN_TTL_MIN;
 import static org.jclouds.rackspace.cloudfiles.v1.reference.CloudFilesHeaders.CDN_LOG_RETENTION;
 import static org.jclouds.rackspace.cloudfiles.v1.reference.CloudFilesHeaders.CDN_TTL;
-import static org.jclouds.rackspace.cloudfiles.v1.reference.CloudFilesHeaders.CDN_TTL_MAX;
-import static org.jclouds.rackspace.cloudfiles.v1.reference.CloudFilesHeaders.CDN_TTL_MIN;
 import static org.testng.Assert.assertEquals;
 
 import org.testng.annotations.Test;