You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jclouds.apache.org by ad...@apache.org on 2013/09/30 09:16:40 UTC

[1/3] git commit: JCLOUDS-300. Add BlobStore abstraction to openstack-swift api

Updated Branches:
  refs/heads/master fd3f8cd01 -> cbac63e18


JCLOUDS-300. Add BlobStore abstraction to openstack-swift api


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

Branch: refs/heads/master
Commit: cbac63e184d6e24ba3503cb7d95edb6e2df6f9d4
Parents: 0426ebf
Author: Adrian Cole <ad...@gmail.com>
Authored: Sun Sep 29 23:53:04 2013 -0700
Committer: Adrian Cole <ad...@gmail.com>
Committed: Mon Sep 30 00:16:17 2013 -0700

----------------------------------------------------------------------
 openstack-swift/pom.xml                         |  13 +-
 .../openstack/swift/v1/SwiftApiMetadata.java    |   9 +-
 .../openstack/swift/v1/TemporaryUrlSigner.java  |  11 +-
 .../blobstore/RegionScopedSwiftBlobStore.java   | 208 ++++++++++++++
 .../RegionScopedTemporaryUrlBlobSigner.java     | 102 +++++++
 .../config/SignUsingTemporaryUrls.java          |  78 +++++
 .../config/SwiftBlobStoreContextModule.java     |  34 +++
 .../v1/blobstore/functions/ToBlobMetadata.java  |  71 +++++
 .../functions/ToListContainerOptions.java       |  52 ++++
 .../blobstore/functions/ToResourceMetadata.java |  45 +++
 .../v1/blobstore/functions/ToSwiftObject.java   |  55 ++++
 .../internal/SubmissionAsyncBlobStore.java      | 284 +++++++++++++++++++
 .../SwiftBlobIntegrationLiveTest.java           |  75 +++++
 .../integration/SwiftBlobLiveTest.java          |  38 +++
 .../integration/SwiftBlobSignerLiveTest.java    |  38 +++
 .../SwiftContainerIntegrationLiveTest.java      |  53 ++++
 .../integration/SwiftContainerLiveTest.java     |  38 +++
 .../SwiftServiceIntegrationLiveTest.java        |  38 +++
 openstack-swift/src/test/resources/logback.xml  |   2 +
 19 files changed, 1228 insertions(+), 16 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/blob/cbac63e1/openstack-swift/pom.xml
----------------------------------------------------------------------
diff --git a/openstack-swift/pom.xml b/openstack-swift/pom.xml
index 9520153..2debd5e 100644
--- a/openstack-swift/pom.xml
+++ b/openstack-swift/pom.xml
@@ -41,17 +41,10 @@
     <test.openstack-swift.identity>FIXME_IDENTITY</test.openstack-swift.identity>
     <test.openstack-swift.credential>FIXME_CREDENTIALS</test.openstack-swift.credential>
     <test.jclouds.keystone.credential-type>passwordCredentials</test.jclouds.keystone.credential-type>
-    <jclouds.blobstore.httpstream.url>FIXME_HTTPURL</jclouds.blobstore.httpstream.url>
-    <jclouds.blobstore.httpstream.md5>FIXME_HTTPMD5</jclouds.blobstore.httpstream.md5>
-
+    <jclouds.blobstore.httpstream.url>http://archive.apache.org/dist/commons/logging/binaries/commons-logging-1.1.1-bin.tar.gz</jclouds.blobstore.httpstream.url>
+    <jclouds.blobstore.httpstream.md5>e5de09672af9b386c30a311654d8541a</jclouds.blobstore.httpstream.md5>
     <jclouds.osgi.export>org.jclouds.openstack.swift.v1*;version="${project.version}"</jclouds.osgi.export>
-    <jclouds.osgi.import>
-      org.jclouds.blobstore.internal;version="${jclouds.version}",
-      org.jclouds.rest.internal;version="${jclouds.version}",
-      org.jclouds.labs*;version="${project.version}",
-      org.jclouds*;version="${jclouds.version}",
-      *
-    </jclouds.osgi.import>
+    <jclouds.osgi.import>org.jclouds*;version="${project.version}",*</jclouds.osgi.import>
   </properties>
 
   <repositories>

http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/blob/cbac63e1/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/SwiftApiMetadata.java
----------------------------------------------------------------------
diff --git a/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/SwiftApiMetadata.java b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/SwiftApiMetadata.java
index 1c08c78..a3ce97c 100644
--- a/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/SwiftApiMetadata.java
+++ b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/SwiftApiMetadata.java
@@ -18,15 +18,19 @@ package org.jclouds.openstack.swift.v1;
 
 import static org.jclouds.openstack.keystone.v2_0.config.KeystoneProperties.CREDENTIAL_TYPE;
 import static org.jclouds.openstack.keystone.v2_0.config.KeystoneProperties.SERVICE_TYPE;
+import static org.jclouds.reflect.Reflection2.typeToken;
 
 import java.net.URI;
 import java.util.Properties;
 
 import org.jclouds.apis.ApiMetadata;
+import org.jclouds.blobstore.BlobStoreContext;
 import org.jclouds.openstack.keystone.v2_0.config.AuthenticationApiModule;
 import org.jclouds.openstack.keystone.v2_0.config.CredentialTypes;
 import org.jclouds.openstack.keystone.v2_0.config.KeystoneAuthenticationModule;
 import org.jclouds.openstack.keystone.v2_0.config.KeystoneAuthenticationModule.RegionModule;
+import org.jclouds.openstack.swift.v1.blobstore.config.SignUsingTemporaryUrls;
+import org.jclouds.openstack.swift.v1.blobstore.config.SwiftBlobStoreContextModule;
 import org.jclouds.openstack.swift.v1.config.SwiftHttpApiModule;
 import org.jclouds.openstack.swift.v1.config.SwiftTypeAdapters;
 import org.jclouds.openstack.v2_0.ServiceType;
@@ -75,12 +79,15 @@ public class SwiftApiMetadata extends BaseHttpApiMetadata<SwiftApi> {
          .endpointName("KeyStone base url ending in /v2.0/")
          .defaultEndpoint("http://localhost:5000/v2.0/")
          .defaultProperties(SwiftApiMetadata.defaultProperties())
+         .view(typeToken(BlobStoreContext.class))
          .defaultModules(ImmutableSet.<Class<? extends Module>>builder()
                                      .add(AuthenticationApiModule.class)
                                      .add(KeystoneAuthenticationModule.class)
                                      .add(RegionModule.class)
                                      .add(SwiftTypeAdapters.class)
-                                     .add(SwiftHttpApiModule.class).build());
+                                     .add(SwiftHttpApiModule.class)
+                                     .add(SwiftBlobStoreContextModule.class)
+                                     .add(SignUsingTemporaryUrls.class).build());
       }
       
       @Override

http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/blob/cbac63e1/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/TemporaryUrlSigner.java
----------------------------------------------------------------------
diff --git a/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/TemporaryUrlSigner.java b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/TemporaryUrlSigner.java
index 35f2b12..7a3cf8b 100644
--- a/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/TemporaryUrlSigner.java
+++ b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/TemporaryUrlSigner.java
@@ -1,9 +1,10 @@
 /*
- * Copyright 2013 Netflix, Inc.
- *
- * Licensed 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
+ * 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
  *

http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/blob/cbac63e1/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/blobstore/RegionScopedSwiftBlobStore.java
----------------------------------------------------------------------
diff --git a/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/blobstore/RegionScopedSwiftBlobStore.java b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/blobstore/RegionScopedSwiftBlobStore.java
new file mode 100644
index 0000000..ad88550
--- /dev/null
+++ b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/blobstore/RegionScopedSwiftBlobStore.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.openstack.swift.v1.blobstore;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import java.util.List;
+import java.util.Set;
+
+import javax.inject.Inject;
+
+import org.jclouds.blobstore.BlobStoreContext;
+import org.jclouds.blobstore.domain.Blob;
+import org.jclouds.blobstore.domain.BlobMetadata;
+import org.jclouds.blobstore.domain.MutableBlobMetadata;
+import org.jclouds.blobstore.domain.PageSet;
+import org.jclouds.blobstore.domain.StorageMetadata;
+import org.jclouds.blobstore.domain.internal.BlobImpl;
+import org.jclouds.blobstore.domain.internal.PageSetImpl;
+import org.jclouds.blobstore.functions.BlobToHttpGetOptions;
+import org.jclouds.blobstore.internal.BaseBlobStore;
+import org.jclouds.blobstore.options.CreateContainerOptions;
+import org.jclouds.blobstore.options.GetOptions;
+import org.jclouds.blobstore.options.ListContainerOptions;
+import org.jclouds.blobstore.options.PutOptions;
+import org.jclouds.blobstore.strategy.internal.FetchBlobMetadata;
+import org.jclouds.blobstore.util.BlobUtils;
+import org.jclouds.domain.Location;
+import org.jclouds.openstack.swift.v1.SwiftApi;
+import org.jclouds.openstack.swift.v1.blobstore.functions.ToBlobMetadata;
+import org.jclouds.openstack.swift.v1.blobstore.functions.ToListContainerOptions;
+import org.jclouds.openstack.swift.v1.blobstore.functions.ToResourceMetadata;
+import org.jclouds.openstack.swift.v1.domain.Container;
+import org.jclouds.openstack.swift.v1.domain.ObjectList;
+import org.jclouds.openstack.swift.v1.domain.SwiftObject;
+import org.jclouds.openstack.swift.v1.features.ObjectApi;
+
+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.collect.FluentIterable;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+
+public class RegionScopedSwiftBlobStore extends BaseBlobStore {
+
+   private final BlobToHttpGetOptions toGetOptions = new BlobToHttpGetOptions();
+   private final ToListContainerOptions toListContainerOptions = new ToListContainerOptions();
+
+   private final SwiftApi api;
+   private final Supplier<Location> region;
+   private final ToResourceMetadata toResourceMetadata;
+   private final FetchBlobMetadata fetchMetadata;
+
+   @Inject
+   protected RegionScopedSwiftBlobStore(BlobStoreContext context, BlobUtils blobUtils, final Supplier<Location> region,
+         SwiftApi api, FetchBlobMetadata fetchMetadata) {
+      super(context, blobUtils, region, new Supplier<Set<? extends Location>>() {
+         @Override
+         public Set<? extends Location> get() {
+            return ImmutableSet.of(region.get());
+         }
+      });
+      this.api = api;
+      this.toResourceMetadata = new ToResourceMetadata(region);
+      this.region = region;
+      this.fetchMetadata = fetchMetadata;
+   }
+
+   /** all commands are scoped to a region. */
+   protected String regionId() {
+      return region.get().getId();
+   }
+
+   @Override
+   public PageSet<? extends StorageMetadata> list() {
+      // TODO: there may eventually be >10k containers..
+      FluentIterable<StorageMetadata> containers = api.containerApiInRegion(regionId()).listFirstPage()
+            .transform(toResourceMetadata);
+      return new PageSetImpl<StorageMetadata>(containers, null);
+   }
+
+   @Override
+   public boolean containerExists(String container) {
+      Container val = api.containerApiInRegion(regionId()).get(container);
+      containerCache.put(container, Optional.fromNullable(val));
+      return val != null;
+   }
+
+   @Override
+   public boolean createContainerInLocation(Location location, String container) {
+      return createContainerInLocation(location, container, CreateContainerOptions.NONE);
+   }
+
+   @Override
+   public boolean createContainerInLocation(Location location, String container, CreateContainerOptions options) {
+      checkArgument(location == null || location.equals(region.get()), "location must be null or %s", region.get());
+      if (options.isPublicRead()) {
+         return api.containerApiInRegion(regionId()).createIfAbsent(container, ANYBODY_READ);
+      }
+      return api.containerApiInRegion(regionId()).createIfAbsent(container, BASIC_CONTAINER);
+   }
+
+   private static final org.jclouds.openstack.swift.v1.options.CreateContainerOptions BASIC_CONTAINER = new org.jclouds.openstack.swift.v1.options.CreateContainerOptions();
+   private static final org.jclouds.openstack.swift.v1.options.CreateContainerOptions ANYBODY_READ = new org.jclouds.openstack.swift.v1.options.CreateContainerOptions()
+         .anybodyRead();
+
+   @Override
+   public PageSet<? extends StorageMetadata> list(String container, ListContainerOptions options) {
+      ObjectApi objectApi = api.objectApiInRegionForContainer(regionId(), container);
+      ObjectList objects = objectApi.list(toListContainerOptions.apply(options));
+      if (objects == null) {
+         containerCache.put(container, Optional.<Container> absent());
+         return new PageSetImpl<StorageMetadata>(ImmutableList.<StorageMetadata> of(), null);
+      } else {
+         containerCache.put(container, Optional.of(objects.container()));
+         List<MutableBlobMetadata> list = Lists.transform(objects, toBlobMetadata(container));
+         int limit = Optional.fromNullable(options.getMaxResults()).or(10000);
+         String marker = list.size() == limit ? list.get(limit - 1).getName() : null;
+         PageSet<StorageMetadata> pageSet = new PageSetImpl<StorageMetadata>(list, marker);
+         if (options.isDetailed()) {
+            return fetchMetadata.setContainerName(container).apply(pageSet);
+         }
+         return pageSet;
+      }
+   }
+
+   @Override
+   public boolean blobExists(String container, String name) {
+      return blobMetadata(container, name) != null;
+   }
+
+   @Override
+   public String putBlob(String container, Blob blob) {
+      return putBlob(container, blob, PutOptions.NONE);
+   }
+
+   @Override
+   public String putBlob(String container, Blob blob, PutOptions options) {
+      if (options.isMultipart()) {
+         throw new UnsupportedOperationException();
+      }
+      ObjectApi objectApi = api.objectApiInRegionForContainer(regionId(), container);
+      return objectApi.replace(blob.getMetadata().getName(), blob.getPayload(), blob.getMetadata().getUserMetadata());
+   }
+
+   @Override
+   public BlobMetadata blobMetadata(String container, String name) {
+      SwiftObject object = api.objectApiInRegionForContainer(regionId(), container).head(name);
+      if (object == null) {
+         return null;
+      }
+      return toBlobMetadata(container).apply(object);
+   }
+
+   @Override
+   public Blob getBlob(String container, String name, GetOptions options) {
+      ObjectApi objectApi = api.objectApiInRegionForContainer(regionId(), container);
+      SwiftObject object = objectApi.get(name, toGetOptions.apply(options));
+      if (object == null) {
+         return null;
+      }
+      Blob blob = new BlobImpl(toBlobMetadata(container).apply(object));
+      blob.setPayload(object.payload());
+      return blob;
+   }
+
+   @Override
+   public void removeBlob(String container, String name) {
+      api.objectApiInRegionForContainer(regionId(), container).delete(name);
+   }
+
+   @Override
+   protected boolean deleteAndVerifyContainerGone(String container) {
+      api.containerApiInRegion(regionId()).deleteIfEmpty(container);
+      containerCache.invalidate(container);
+      return true;
+   }
+
+   protected final LoadingCache<String, Optional<Container>> containerCache = CacheBuilder.newBuilder().build(
+         new CacheLoader<String, Optional<Container>>() {
+            public Optional<Container> load(String container) {
+               return Optional.fromNullable(api.containerApiInRegion(regionId()).get(container));
+            }
+         });
+
+   protected Function<SwiftObject, MutableBlobMetadata> toBlobMetadata(String container) {
+      return new ToBlobMetadata(containerCache.getUnchecked(container).get());
+   }
+}

http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/blob/cbac63e1/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/blobstore/RegionScopedTemporaryUrlBlobSigner.java
----------------------------------------------------------------------
diff --git a/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/blobstore/RegionScopedTemporaryUrlBlobSigner.java b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/blobstore/RegionScopedTemporaryUrlBlobSigner.java
new file mode 100644
index 0000000..81dce15
--- /dev/null
+++ b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/blobstore/RegionScopedTemporaryUrlBlobSigner.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.openstack.swift.v1.blobstore;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.net.URI;
+import java.util.concurrent.TimeUnit;
+
+import javax.inject.Provider;
+
+import org.jclouds.blobstore.BlobRequestSigner;
+import org.jclouds.blobstore.domain.Blob;
+import org.jclouds.blobstore.functions.BlobToHttpGetOptions;
+import org.jclouds.date.TimeStamp;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.http.Uris;
+import org.jclouds.http.options.GetOptions;
+import org.jclouds.location.Region;
+import org.jclouds.openstack.swift.v1.TemporaryUrlSigner;
+
+import com.google.common.base.Supplier;
+import com.google.inject.Inject;
+
+/**
+ * Uses {@link TemporaryUrlSigner} to sign requests for access to blobs. If no
+ * interval is supplied, it defaults to a year.
+ */
+public class RegionScopedTemporaryUrlBlobSigner implements BlobRequestSigner {
+   private static final long YEAR = TimeUnit.DAYS.toSeconds(365);
+   private final BlobToHttpGetOptions toGetOptions = new BlobToHttpGetOptions();
+
+   private final Supplier<TemporaryUrlSigner> signer;
+   private final Supplier<URI> storageUrl;
+   private final Provider<Long> timestamp;
+
+   @Inject
+   protected RegionScopedTemporaryUrlBlobSigner(Supplier<TemporaryUrlSigner> signer, @Region Supplier<URI> storageUrl,
+         @TimeStamp Provider<Long> timestamp) {
+      this.signer = signer;
+      this.storageUrl = storageUrl;
+      this.timestamp = timestamp;
+   }
+
+   @Override
+   public HttpRequest signGetBlob(String container, String name) {
+      return signGetBlob(container, name, YEAR);
+   }
+
+   @Override
+   public HttpRequest signGetBlob(String container, String name, long timeInSeconds) {
+      return sign("GET", container, name, GetOptions.NONE, timestamp.get() + timeInSeconds);
+   }
+
+   @Override
+   public HttpRequest signGetBlob(String container, String name, org.jclouds.blobstore.options.GetOptions options) {
+      return sign("GET", container, name, toGetOptions.apply(options), timestamp.get() + YEAR);
+   }
+
+   @Override
+   public HttpRequest signPutBlob(String container, Blob blob) {
+      return signPutBlob(container, blob, YEAR);
+   }
+
+   @Override
+   public HttpRequest signPutBlob(String container, Blob blob, long timeInSeconds) {
+      return sign("PUT", container, blob.getMetadata().getName(), GetOptions.NONE, timestamp.get() + timeInSeconds);
+   }
+
+   @Override
+   public HttpRequest signRemoveBlob(String container, String name) {
+      return sign("DELETE", container, name, GetOptions.NONE, timestamp.get() + YEAR);
+   }
+
+   private HttpRequest sign(String method, String container, String name, GetOptions options, long expires) {
+      checkNotNull(container, "container");
+      checkNotNull(name, "name");
+      URI url = Uris.uriBuilder(storageUrl.get()).appendPath(container).appendPath(name).build();
+      String signature = signer.get().sign(method, url.getPath(), expires);
+      return HttpRequest.builder()
+                        .method(method)
+                        .endpoint(url)
+                        .addQueryParams(options.buildQueryParameters())
+                        .addQueryParam("temp_url_sig", signature)
+                        .addQueryParam("temp_url_expires", String.valueOf(expires))
+                        .headers(options.buildRequestHeaders()).build();
+   }
+}

http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/blob/cbac63e1/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/blobstore/config/SignUsingTemporaryUrls.java
----------------------------------------------------------------------
diff --git a/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/blobstore/config/SignUsingTemporaryUrls.java b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/blobstore/config/SignUsingTemporaryUrls.java
new file mode 100644
index 0000000..91c753c
--- /dev/null
+++ b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/blobstore/config/SignUsingTemporaryUrls.java
@@ -0,0 +1,78 @@
+/*
+ * 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.blobstore.config;
+
+import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL;
+
+import java.net.URI;
+import java.util.Map;
+
+import javax.inject.Singleton;
+
+import org.jclouds.blobstore.BlobRequestSigner;
+import org.jclouds.date.TimeStamp;
+import org.jclouds.location.Region;
+import org.jclouds.openstack.swift.v1.SwiftApi;
+import org.jclouds.openstack.swift.v1.TemporaryUrlSigner;
+import org.jclouds.openstack.swift.v1.blobstore.RegionScopedTemporaryUrlBlobSigner;
+
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
+import com.google.inject.AbstractModule;
+import com.google.inject.Provides;
+import com.google.inject.name.Named;
+
+public class SignUsingTemporaryUrls extends AbstractModule {
+
+   @Override
+   protected void configure() {
+      bind(BlobRequestSigner.class).to(RegionScopedTemporaryUrlBlobSigner.class);
+   }
+
+   @Provides
+   @TimeStamp
+   protected Long unixEpochTimestamp() {
+      return System.currentTimeMillis() / 1000;
+   }
+
+   @Provides
+   @Singleton
+   Supplier<TemporaryUrlSigner> regionScopedTemporaryUrlSigner(final SwiftApi api,
+         @Region final Supplier<String> defaultRegion, @Named(PROPERTY_SESSION_INTERVAL) final long seconds) {
+      return Suppliers.memoize(new Supplier<TemporaryUrlSigner>() {
+
+         @Override
+         public TemporaryUrlSigner get() {
+            return TemporaryUrlSigner.checkApiEvery(api.accountApiInRegion(defaultRegion.get()), seconds);
+         }
+      });
+   }
+
+   @Provides
+   @Singleton
+   @Region
+   Supplier<URI> storageUrl(@Region final Supplier<String> defaultRegion,
+         @Region final Supplier<Map<String, Supplier<URI>>> regionToUris) {
+      return Suppliers.memoize(new Supplier<URI>() {
+
+         @Override
+         public URI get() {
+            return regionToUris.get().get(defaultRegion.get()).get();
+         }
+      });
+   }
+}

http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/blob/cbac63e1/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/blobstore/config/SwiftBlobStoreContextModule.java
----------------------------------------------------------------------
diff --git a/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/blobstore/config/SwiftBlobStoreContextModule.java b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/blobstore/config/SwiftBlobStoreContextModule.java
new file mode 100644
index 0000000..4452cdd
--- /dev/null
+++ b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/blobstore/config/SwiftBlobStoreContextModule.java
@@ -0,0 +1,34 @@
+/*
+ * 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.blobstore.config;
+
+import org.jclouds.blobstore.AsyncBlobStore;
+import org.jclouds.blobstore.BlobStore;
+import org.jclouds.blobstore.attr.ConsistencyModel;
+import org.jclouds.openstack.swift.v1.blobstore.RegionScopedSwiftBlobStore;
+import org.jclouds.openstack.swift.v1.blobstore.internal.SubmissionAsyncBlobStore;
+
+import com.google.inject.AbstractModule;
+
+public class SwiftBlobStoreContextModule extends AbstractModule {
+   @Override
+   protected void configure() {
+      bind(ConsistencyModel.class).toInstance(ConsistencyModel.STRICT);
+      bind(AsyncBlobStore.class).to(SubmissionAsyncBlobStore.class);
+      bind(BlobStore.class).to(RegionScopedSwiftBlobStore.class);
+   }
+}

http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/blob/cbac63e1/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/blobstore/functions/ToBlobMetadata.java
----------------------------------------------------------------------
diff --git a/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/blobstore/functions/ToBlobMetadata.java b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/blobstore/functions/ToBlobMetadata.java
new file mode 100644
index 0000000..eda42aa
--- /dev/null
+++ b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/blobstore/functions/ToBlobMetadata.java
@@ -0,0 +1,71 @@
+/*
+ * 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.blobstore.functions;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.io.BaseEncoding.base16;
+
+import org.jclouds.blobstore.domain.MutableBlobMetadata;
+import org.jclouds.blobstore.domain.StorageType;
+import org.jclouds.blobstore.domain.internal.MutableBlobMetadataImpl;
+import org.jclouds.blobstore.strategy.IfDirectoryReturnNameStrategy;
+import org.jclouds.blobstore.strategy.internal.MarkersIfDirectoryReturnNameStrategy;
+import org.jclouds.openstack.swift.v1.domain.Container;
+import org.jclouds.openstack.swift.v1.domain.SwiftObject;
+
+import com.google.common.base.Function;
+
+public class ToBlobMetadata implements Function<SwiftObject, MutableBlobMetadata> {
+
+   private final IfDirectoryReturnNameStrategy ifDirectoryReturnName = new MarkersIfDirectoryReturnNameStrategy();
+   private final Container container;
+
+   public ToBlobMetadata(Container container) {
+      this.container = checkNotNull(container, "container");
+   }
+
+   @Override
+   public MutableBlobMetadata apply(SwiftObject from) {
+      if (from == null)
+         return null;
+      MutableBlobMetadata to = new MutableBlobMetadataImpl();
+      to.setContainer(container.name());
+      if (container.anybodyRead().isPresent()) {
+         to.setPublicUri(from.uri());
+      }
+      to.setUri(from.uri());
+      to.setETag(from.etag());
+      to.setName(from.name());
+      to.setLastModified(from.lastModified());
+      to.setContentMetadata(from.payload().getContentMetadata());
+      to.getContentMetadata().setContentMD5(base16().lowerCase().decode(from.etag()));
+      to.setUserMetadata(from.metadata());
+      String directoryName = ifDirectoryReturnName.execute(to);
+      if (directoryName != null) {
+         to.setName(directoryName);
+         to.setType(StorageType.RELATIVE_PATH);
+      } else {
+         to.setType(StorageType.BLOB);
+      }
+      return to;
+   }
+
+   @Override
+   public String toString() {
+      return "ObjectToBlobMetadata(" + container + ")";
+   }
+}

http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/blob/cbac63e1/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/blobstore/functions/ToListContainerOptions.java
----------------------------------------------------------------------
diff --git a/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/blobstore/functions/ToListContainerOptions.java b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/blobstore/functions/ToListContainerOptions.java
new file mode 100644
index 0000000..2650f5b
--- /dev/null
+++ b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/blobstore/functions/ToListContainerOptions.java
@@ -0,0 +1,52 @@
+/*
+ * 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.blobstore.functions;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import org.jclouds.blobstore.options.ListContainerOptions;
+
+import com.google.common.base.Function;
+
+public class ToListContainerOptions implements
+      Function<ListContainerOptions, org.jclouds.openstack.swift.v1.options.ListContainerOptions> {
+
+   @Override
+   public org.jclouds.openstack.swift.v1.options.ListContainerOptions apply(ListContainerOptions from) {
+      checkNotNull(from, "set options to instance NONE instead of passing null");
+      org.jclouds.openstack.swift.v1.options.ListContainerOptions options = new org.jclouds.openstack.swift.v1.options.ListContainerOptions();
+      if ((from.getDir() == null) && (from.isRecursive())) {
+         options.prefix("");
+      }
+      if ((from.getDir() == null) && (!from.isRecursive())) {
+         options.path("");
+      }
+      if ((from.getDir() != null) && (from.isRecursive())) {
+         options.prefix(from.getDir().endsWith("/") ? from.getDir() : from.getDir() + "/");
+      }
+      if ((from.getDir() != null) && (!from.isRecursive())) {
+         options.path(from.getDir());
+      }
+      if (from.getMarker() != null) {
+         options.marker(from.getMarker());
+      }
+      if (from.getMaxResults() != null) {
+         options.limit(from.getMaxResults());
+      }
+      return options;
+   }
+}

http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/blob/cbac63e1/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/blobstore/functions/ToResourceMetadata.java
----------------------------------------------------------------------
diff --git a/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/blobstore/functions/ToResourceMetadata.java b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/blobstore/functions/ToResourceMetadata.java
new file mode 100644
index 0000000..6d12ce8
--- /dev/null
+++ b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/blobstore/functions/ToResourceMetadata.java
@@ -0,0 +1,45 @@
+/*
+ * 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.blobstore.functions;
+
+import org.jclouds.blobstore.domain.MutableStorageMetadata;
+import org.jclouds.blobstore.domain.StorageMetadata;
+import org.jclouds.blobstore.domain.StorageType;
+import org.jclouds.blobstore.domain.internal.MutableStorageMetadataImpl;
+import org.jclouds.domain.Location;
+import org.jclouds.openstack.swift.v1.domain.Container;
+
+import com.google.common.base.Function;
+import com.google.common.base.Supplier;
+
+public class ToResourceMetadata implements Function<Container, StorageMetadata> {
+   private Supplier<Location> region;
+
+   public ToResourceMetadata(Supplier<Location> region) {
+      this.region = region;
+   }
+
+   @Override
+   public StorageMetadata apply(Container from) {
+      MutableStorageMetadata to = new MutableStorageMetadataImpl();
+      to.setName(from.name());
+      to.setLocation(region.get());
+      to.setType(StorageType.CONTAINER);
+      to.setUserMetadata(from.metadata());
+      return to;
+   }
+}

http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/blob/cbac63e1/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/blobstore/functions/ToSwiftObject.java
----------------------------------------------------------------------
diff --git a/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/blobstore/functions/ToSwiftObject.java b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/blobstore/functions/ToSwiftObject.java
new file mode 100644
index 0000000..aabf63c
--- /dev/null
+++ b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/blobstore/functions/ToSwiftObject.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.openstack.swift.v1.blobstore.functions;
+
+import org.jclouds.blobstore.domain.BlobMetadata;
+import org.jclouds.blobstore.domain.StorageMetadata;
+import org.jclouds.io.Payload;
+import org.jclouds.io.Payloads;
+import org.jclouds.openstack.swift.v1.domain.SwiftObject;
+import org.jclouds.openstack.swift.v1.domain.SwiftObject.Builder;
+
+import com.google.common.base.Function;
+
+public class ToSwiftObject implements Function<StorageMetadata, SwiftObject> {
+
+   @Override
+   public SwiftObject apply(StorageMetadata in) {
+      if (!(in instanceof BlobMetadata)) {
+         return null;
+      }
+      BlobMetadata from = BlobMetadata.class.cast(in);
+      Builder to = SwiftObject.builder();
+      to.name(from.getName());
+      to.etag(from.getETag());
+      to.lastModified(from.getLastModified());
+      long bytes = from.getContentMetadata().getContentLength();
+      String contentType = from.getContentMetadata().getContentType();
+      to.payload(payload(bytes, contentType));
+      to.metadata(from.getUserMetadata());
+      return to.build();
+   }
+
+   private static final byte[] NO_CONTENT = new byte[] {};
+
+   private static Payload payload(long bytes, String contentType) {
+      Payload payload = Payloads.newByteArrayPayload(NO_CONTENT);
+      payload.getContentMetadata().setContentLength(bytes);
+      payload.getContentMetadata().setContentType(contentType);
+      return payload;
+   }
+}

http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/blob/cbac63e1/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/blobstore/internal/SubmissionAsyncBlobStore.java
----------------------------------------------------------------------
diff --git a/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/blobstore/internal/SubmissionAsyncBlobStore.java b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/blobstore/internal/SubmissionAsyncBlobStore.java
new file mode 100644
index 0000000..1d083b2
--- /dev/null
+++ b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/blobstore/internal/SubmissionAsyncBlobStore.java
@@ -0,0 +1,284 @@
+/*
+ * 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.blobstore.internal;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.jclouds.Constants.PROPERTY_USER_THREADS;
+
+import java.util.Set;
+import java.util.concurrent.Callable;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import org.jclouds.blobstore.AsyncBlobStore;
+import org.jclouds.blobstore.BlobStore;
+import org.jclouds.blobstore.BlobStoreContext;
+import org.jclouds.blobstore.domain.Blob;
+import org.jclouds.blobstore.domain.BlobBuilder;
+import org.jclouds.blobstore.domain.BlobMetadata;
+import org.jclouds.blobstore.domain.PageSet;
+import org.jclouds.blobstore.domain.StorageMetadata;
+import org.jclouds.blobstore.options.CreateContainerOptions;
+import org.jclouds.blobstore.options.GetOptions;
+import org.jclouds.blobstore.options.ListContainerOptions;
+import org.jclouds.blobstore.options.PutOptions;
+import org.jclouds.domain.Location;
+
+import com.google.common.collect.ForwardingObject;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+
+/**
+ * Adapter that allows you to reuse an existing {@link BlobStore} to implement
+ * the deprecated {@link AsyncBlobStore} interface.
+ * 
+ * @deprecated will be removed in jclouds 1.7, as async interfaces are no longer
+ *             supported. Please use {@link BlobStore}
+ */
+@Deprecated
+public class SubmissionAsyncBlobStore extends ForwardingObject implements AsyncBlobStore {
+   private final BlobStore blobstore;
+   private final ListeningExecutorService executor;
+
+   @Inject
+   SubmissionAsyncBlobStore(BlobStore blobstore, @Named(PROPERTY_USER_THREADS) ListeningExecutorService executor) {
+      this.blobstore = checkNotNull(blobstore, "blobstore");
+      this.executor = checkNotNull(executor, "executor");
+   }
+
+   @Override
+   protected BlobStore delegate() {
+      return blobstore;
+   }
+
+   @Override
+   public BlobStoreContext getContext() {
+      return delegate().getContext();
+   }
+
+   @Override
+   public BlobBuilder blobBuilder(String name) {
+      return delegate().blobBuilder(name);
+   }
+
+   @Override
+   public ListenableFuture<Set<? extends Location>> listAssignableLocations() {
+      return executor.submit(new Callable<Set<? extends Location>>() {
+         public Set<? extends Location> call() {
+            return delegate().listAssignableLocations();
+         }
+      });
+   }
+
+   @Override
+   public ListenableFuture<PageSet<? extends StorageMetadata>> list() {
+      return executor.submit(new Callable<PageSet<? extends StorageMetadata>>() {
+         public PageSet<? extends StorageMetadata> call() {
+            return delegate().list();
+         }
+      });
+   }
+
+   @Override
+   public ListenableFuture<Boolean> containerExists(final String container) {
+      return executor.submit(new Callable<Boolean>() {
+         public Boolean call() {
+            return delegate().containerExists(container);
+         }
+      });
+   }
+
+   @Override
+   public ListenableFuture<Boolean> createContainerInLocation(final Location location, final String container) {
+      return executor.submit(new Callable<Boolean>() {
+         public Boolean call() {
+            return delegate().createContainerInLocation(location, container);
+         }
+      });
+   }
+
+   @Override
+   public ListenableFuture<Boolean> createContainerInLocation(final Location location, final String container,
+         final CreateContainerOptions options) {
+      return executor.submit(new Callable<Boolean>() {
+         public Boolean call() {
+            return delegate().createContainerInLocation(location, container, options);
+         }
+      });
+   }
+
+   @Override
+   public ListenableFuture<PageSet<? extends StorageMetadata>> list(final String container) {
+      return executor.submit(new Callable<PageSet<? extends StorageMetadata>>() {
+         public PageSet<? extends StorageMetadata> call() {
+            return delegate().list(container);
+         }
+      });
+   }
+
+   @Override
+   public ListenableFuture<PageSet<? extends StorageMetadata>> list(final String container,
+         final ListContainerOptions options) {
+      return executor.submit(new Callable<PageSet<? extends StorageMetadata>>() {
+         public PageSet<? extends StorageMetadata> call() {
+            return delegate().list(container, options);
+         }
+      });
+   }
+
+   @Override
+   public ListenableFuture<Void> clearContainer(final String container) {
+      return executor.submit(new Callable<Void>() {
+         public Void call() {
+            delegate().clearContainer(container);
+            return null;
+         }
+      });
+   }
+
+   @Override
+   public ListenableFuture<Void> clearContainer(final String container, final ListContainerOptions options) {
+      return executor.submit(new Callable<Void>() {
+         public Void call() {
+            delegate().clearContainer(container, options);
+            return null;
+         }
+      });
+   }
+
+   @Override
+   public ListenableFuture<Void> deleteContainer(final String container) {
+      return executor.submit(new Callable<Void>() {
+         public Void call() {
+            delegate().deleteContainer(container);
+            return null;
+         }
+      });
+   }
+
+   @Override
+   public ListenableFuture<Boolean> directoryExists(final String container, final String directory) {
+      return executor.submit(new Callable<Boolean>() {
+         public Boolean call() {
+            return delegate().directoryExists(container, directory);
+         }
+      });
+   }
+
+   @Override
+   public ListenableFuture<Void> createDirectory(final String container, final String directory) {
+      return executor.submit(new Callable<Void>() {
+         public Void call() {
+            delegate().createDirectory(container, directory);
+            return null;
+         }
+      });
+   }
+
+   @Override
+   public ListenableFuture<Void> deleteDirectory(final String containerName, final String name) {
+      return executor.submit(new Callable<Void>() {
+         public Void call() {
+            delegate().deleteDirectory(containerName, name);
+            return null;
+         }
+      });
+   }
+
+   @Override
+   public ListenableFuture<Boolean> blobExists(final String container, final String name) {
+      return executor.submit(new Callable<Boolean>() {
+         public Boolean call() {
+            return delegate().blobExists(container, name);
+         }
+      });
+   }
+
+   @Override
+   public ListenableFuture<String> putBlob(final String container, final Blob blob) {
+      return executor.submit(new Callable<String>() {
+         public String call() {
+            return delegate().putBlob(container, blob);
+         }
+      });
+   }
+
+   @Override
+   public ListenableFuture<String> putBlob(final String container, final Blob blob, final PutOptions options) {
+      return executor.submit(new Callable<String>() {
+         public String call() {
+            return delegate().putBlob(container, blob, options);
+         }
+      });
+   }
+
+   @Override
+   public ListenableFuture<BlobMetadata> blobMetadata(final String container, final String key) {
+      return executor.submit(new Callable<BlobMetadata>() {
+         public BlobMetadata call() {
+            return delegate().blobMetadata(container, key);
+         }
+      });
+   }
+
+   @Override
+   public ListenableFuture<Blob> getBlob(final String container, final String key) {
+      return executor.submit(new Callable<Blob>() {
+         public Blob call() {
+            return delegate().getBlob(container, key);
+         }
+      });
+   }
+
+   @Override
+   public ListenableFuture<Blob> getBlob(final String container, final String key, final GetOptions options) {
+      return executor.submit(new Callable<Blob>() {
+         public Blob call() {
+            return delegate().getBlob(container, key, options);
+         }
+      });
+   }
+
+   @Override
+   public ListenableFuture<Void> removeBlob(final String container, final String key) {
+      return executor.submit(new Callable<Void>() {
+         public Void call() {
+            delegate().removeBlob(container, key);
+            return null;
+         }
+      });
+   }
+
+   @Override
+   public ListenableFuture<Long> countBlobs(final String container) {
+      return executor.submit(new Callable<Long>() {
+         public Long call() {
+            return delegate().countBlobs(container);
+         }
+      });
+   }
+
+   @Override
+   public ListenableFuture<Long> countBlobs(final String container, final ListContainerOptions options) {
+      return executor.submit(new Callable<Long>() {
+         public Long call() {
+            return delegate().countBlobs(container, options);
+         }
+      });
+   }
+}

http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/blob/cbac63e1/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/blobstore/integration/SwiftBlobIntegrationLiveTest.java
----------------------------------------------------------------------
diff --git a/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/blobstore/integration/SwiftBlobIntegrationLiveTest.java b/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/blobstore/integration/SwiftBlobIntegrationLiveTest.java
new file mode 100644
index 0000000..39f0a0f
--- /dev/null
+++ b/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/blobstore/integration/SwiftBlobIntegrationLiveTest.java
@@ -0,0 +1,75 @@
+/*
+ * 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.blobstore.integration;
+
+import static org.jclouds.openstack.keystone.v2_0.config.KeystoneProperties.CREDENTIAL_TYPE;
+
+import java.util.Properties;
+
+import org.jclouds.blobstore.domain.Blob;
+import org.jclouds.blobstore.integration.internal.BaseBlobIntegrationTest;
+import org.testng.SkipException;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+@Test(groups = "live")
+public class SwiftBlobIntegrationLiveTest extends BaseBlobIntegrationTest {
+   public SwiftBlobIntegrationLiveTest() {
+      provider = "openstack-swift";
+   }
+
+   @Override
+   protected Properties setupProperties() {
+      Properties props = super.setupProperties();
+      setIfTestSystemPropertyPresent(props, CREDENTIAL_TYPE);
+      return props;
+   }
+
+   // Object/Container name contains forbidden chars from "<>
+   @Override
+   @DataProvider(name = "delete")
+   public Object[][] createData() {
+      return new Object[][] { { "normal" }, { "sp ace" }, { "qu?stion" }, { "unic₪de" }, { "path/foo" }, { "colon:" },
+            { "asteri*k" }, { "p|pe" } };
+   }
+
+   @Override
+   public void testGetTwoRanges() {
+      throw new SkipException("unsupported in swift");
+   }
+
+   @Override
+   public void testCreateBlobWithExpiry() throws InterruptedException {
+      throw new SkipException("unsupported in swift");
+   }
+
+   @Test(groups = { "integration", "live" })
+   public void testGetIfUnmodifiedSince() throws InterruptedException {
+      throw new SkipException("unsupported in swift");
+   }
+
+   @Override
+   protected int getIncorrectContentMD5StatusCode() {
+      return 422;
+   }
+
+   @Override
+   protected void checkContentLanguage(Blob blob, String contentLanguage) {
+      assert blob.getPayload().getContentMetadata().getContentLanguage() == null;
+      assert blob.getMetadata().getContentMetadata().getContentLanguage() == null;
+   }
+}

http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/blob/cbac63e1/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/blobstore/integration/SwiftBlobLiveTest.java
----------------------------------------------------------------------
diff --git a/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/blobstore/integration/SwiftBlobLiveTest.java b/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/blobstore/integration/SwiftBlobLiveTest.java
new file mode 100644
index 0000000..f7b6b00
--- /dev/null
+++ b/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/blobstore/integration/SwiftBlobLiveTest.java
@@ -0,0 +1,38 @@
+/*
+ * 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.blobstore.integration;
+
+import static org.jclouds.openstack.keystone.v2_0.config.KeystoneProperties.CREDENTIAL_TYPE;
+
+import java.util.Properties;
+
+import org.jclouds.blobstore.integration.internal.BaseBlobLiveTest;
+import org.testng.annotations.Test;
+
+@Test(groups = "live")
+public class SwiftBlobLiveTest extends BaseBlobLiveTest {
+   public SwiftBlobLiveTest() {
+      provider = "openstack-swift";
+   }
+
+   @Override
+   protected Properties setupProperties() {
+      Properties props = super.setupProperties();
+      setIfTestSystemPropertyPresent(props, CREDENTIAL_TYPE);
+      return props;
+   }
+}

http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/blob/cbac63e1/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/blobstore/integration/SwiftBlobSignerLiveTest.java
----------------------------------------------------------------------
diff --git a/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/blobstore/integration/SwiftBlobSignerLiveTest.java b/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/blobstore/integration/SwiftBlobSignerLiveTest.java
new file mode 100644
index 0000000..36fc024
--- /dev/null
+++ b/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/blobstore/integration/SwiftBlobSignerLiveTest.java
@@ -0,0 +1,38 @@
+/*
+ * 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.blobstore.integration;
+
+import static org.jclouds.openstack.keystone.v2_0.config.KeystoneProperties.CREDENTIAL_TYPE;
+
+import java.util.Properties;
+
+import org.jclouds.blobstore.integration.internal.BaseBlobSignerLiveTest;
+import org.testng.annotations.Test;
+
+@Test(groups = "live")
+public class SwiftBlobSignerLiveTest extends BaseBlobSignerLiveTest {
+   public SwiftBlobSignerLiveTest() {
+      provider = "openstack-swift";
+   }
+
+   @Override
+   protected Properties setupProperties() {
+      Properties props = super.setupProperties();
+      setIfTestSystemPropertyPresent(props, CREDENTIAL_TYPE);
+      return props;
+   }
+}

http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/blob/cbac63e1/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/blobstore/integration/SwiftContainerIntegrationLiveTest.java
----------------------------------------------------------------------
diff --git a/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/blobstore/integration/SwiftContainerIntegrationLiveTest.java b/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/blobstore/integration/SwiftContainerIntegrationLiveTest.java
new file mode 100644
index 0000000..4e682f8
--- /dev/null
+++ b/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/blobstore/integration/SwiftContainerIntegrationLiveTest.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.openstack.swift.v1.blobstore.integration;
+
+import static org.jclouds.openstack.keystone.v2_0.config.KeystoneProperties.CREDENTIAL_TYPE;
+import static org.testng.Assert.assertTrue;
+
+import java.util.Properties;
+
+import org.jclouds.blobstore.integration.internal.BaseContainerIntegrationTest;
+import org.testng.annotations.Test;
+
+@Test(groups = "live")
+public class SwiftContainerIntegrationLiveTest extends BaseContainerIntegrationTest {
+   public SwiftContainerIntegrationLiveTest() {
+      provider = "openstack-swift";
+   }
+
+   @Override
+   protected Properties setupProperties() {
+      Properties props = super.setupProperties();
+      setIfTestSystemPropertyPresent(props, CREDENTIAL_TYPE);
+      return props;
+   }
+
+   @Override
+   public void testListRootUsesDelimiter() throws InterruptedException {
+      try {
+         super.testListRootUsesDelimiter();
+      } catch (AssertionError e) {
+         // swift doesn't have the "common prefixes" in the response that s3
+         // does. If we wanted this to pass, we'd need to create
+         // pseudo-directories implicitly, which is costly and troublesome. It
+         // is better to fail this assertion.
+         assertTrue(e.getMessage().matches(".*16.* but .*15.*"), e.getMessage());
+         // ^^ squishy regex to deal with various formats of testng messages.
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/blob/cbac63e1/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/blobstore/integration/SwiftContainerLiveTest.java
----------------------------------------------------------------------
diff --git a/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/blobstore/integration/SwiftContainerLiveTest.java b/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/blobstore/integration/SwiftContainerLiveTest.java
new file mode 100644
index 0000000..8f5ab6c
--- /dev/null
+++ b/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/blobstore/integration/SwiftContainerLiveTest.java
@@ -0,0 +1,38 @@
+/*
+ * 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.blobstore.integration;
+
+import static org.jclouds.openstack.keystone.v2_0.config.KeystoneProperties.CREDENTIAL_TYPE;
+
+import java.util.Properties;
+
+import org.jclouds.blobstore.integration.internal.BaseContainerLiveTest;
+import org.testng.annotations.Test;
+
+@Test(groups = "live")
+public class SwiftContainerLiveTest extends BaseContainerLiveTest {
+   public SwiftContainerLiveTest() {
+      provider = "openstack-swift";
+   }
+
+   @Override
+   protected Properties setupProperties() {
+      Properties props = super.setupProperties();
+      setIfTestSystemPropertyPresent(props, CREDENTIAL_TYPE);
+      return props;
+   }
+}

http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/blob/cbac63e1/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/blobstore/integration/SwiftServiceIntegrationLiveTest.java
----------------------------------------------------------------------
diff --git a/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/blobstore/integration/SwiftServiceIntegrationLiveTest.java b/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/blobstore/integration/SwiftServiceIntegrationLiveTest.java
new file mode 100644
index 0000000..fa9a734
--- /dev/null
+++ b/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/blobstore/integration/SwiftServiceIntegrationLiveTest.java
@@ -0,0 +1,38 @@
+/*
+ * 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.blobstore.integration;
+
+import static org.jclouds.openstack.keystone.v2_0.config.KeystoneProperties.CREDENTIAL_TYPE;
+
+import java.util.Properties;
+
+import org.jclouds.blobstore.integration.internal.BaseServiceIntegrationTest;
+import org.testng.annotations.Test;
+
+@Test(groups = "live")
+public class SwiftServiceIntegrationLiveTest extends BaseServiceIntegrationTest {
+   public SwiftServiceIntegrationLiveTest() {
+      provider = "openstack-swift";
+   }
+
+   @Override
+   protected Properties setupProperties() {
+      Properties props = super.setupProperties();
+      setIfTestSystemPropertyPresent(props, CREDENTIAL_TYPE);
+      return props;
+   }
+}

http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/blob/cbac63e1/openstack-swift/src/test/resources/logback.xml
----------------------------------------------------------------------
diff --git a/openstack-swift/src/test/resources/logback.xml b/openstack-swift/src/test/resources/logback.xml
index 990ca22..ce891f1 100644
--- a/openstack-swift/src/test/resources/logback.xml
+++ b/openstack-swift/src/test/resources/logback.xml
@@ -51,10 +51,12 @@
         <appender-ref ref="FILE" />
     </logger>
 
+<!--
     <logger name="jclouds.wire">
         <level value="DEBUG" />
         <appender-ref ref="WIREFILE" />
     </logger>
+-->
 
     <logger name="jclouds.headers">
         <level value="DEBUG" />


[2/3] git commit: JCLOUDS-298. expose container metadata via ObjectList and port Object PUT code from legacy codebase

Posted by ad...@apache.org.
JCLOUDS-298. expose container metadata via ObjectList and port Object PUT code from legacy codebase


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

Branch: refs/heads/master
Commit: 0426ebf0c00113f3affc608c2ba83da7bb5554be
Parents: 523b134
Author: Adrian Cole <ad...@gmail.com>
Authored: Sun Sep 29 23:49:50 2013 -0700
Committer: Adrian Cole <ad...@gmail.com>
Committed: Mon Sep 30 00:16:17 2013 -0700

----------------------------------------------------------------------
 .../openstack/swift/v1/binders/SetPayload.java  | 49 ++++++++++++++++++++
 .../openstack/swift/v1/domain/ObjectList.java   | 47 +++++++++++++++++++
 .../openstack/swift/v1/features/BulkApi.java    |  2 +-
 .../openstack/swift/v1/features/ObjectApi.java  | 22 +++------
 .../functions/ParseObjectListFromResponse.java  | 21 ++++++---
 .../swift/v1/features/ObjectApiLiveTest.java    |  5 +-
 .../swift/v1/features/ObjectApiMockTest.java    | 16 ++++---
 .../swift/v1/internal/BaseSwiftApiLiveTest.java | 21 +++++----
 8 files changed, 142 insertions(+), 41 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/blob/0426ebf0/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/binders/SetPayload.java
----------------------------------------------------------------------
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
new file mode 100644
index 0000000..70aa6f0
--- /dev/null
+++ b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/binders/SetPayload.java
@@ -0,0 +1,49 @@
+/*
+ * 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.binders;
+
+import static com.google.common.base.Preconditions.checkArgument;
+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 org.jclouds.http.HttpRequest;
+import org.jclouds.http.HttpRequest.Builder;
+import org.jclouds.io.Payload;
+import org.jclouds.rest.Binder;
+
+public class SetPayload implements Binder {
+   @SuppressWarnings("unchecked")
+   @Override
+   public <R extends HttpRequest> R bindToRequest(R request, Object input) {
+      Builder<?> builder = request.toBuilder();
+      Payload payload = Payload.class.cast(input);
+      Long contentLength = payload.getContentMetadata().getContentLength();
+      if (contentLength != null && contentLength >= 0) {
+         checkArgument(contentLength <= 5l * 1024 * 1024 * 1024, "maximum size for put object is 5GB, %s",
+               contentLength);
+      } 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));
+      }
+      return (R) builder.payload(payload).build();
+   }
+}

http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/blob/0426ebf0/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/domain/ObjectList.java
----------------------------------------------------------------------
diff --git a/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/domain/ObjectList.java b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/domain/ObjectList.java
new file mode 100644
index 0000000..350e8e1
--- /dev/null
+++ b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/domain/ObjectList.java
@@ -0,0 +1,47 @@
+/*
+ * 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.domain;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.List;
+
+import com.google.common.collect.ForwardingList;
+
+public class ObjectList extends ForwardingList<SwiftObject> {
+
+   public static ObjectList create(List<SwiftObject> objects, Container container) {
+      return new ObjectList(objects, container);
+   }
+
+   private final List<SwiftObject> objects;
+   private final Container container;
+
+   protected ObjectList(List<SwiftObject> objects, Container container) {
+      this.objects = checkNotNull(objects, "objects");
+      this.container = checkNotNull(container, "container");
+   }
+
+   public Container container() {
+      return container;
+   }
+
+   @Override
+   protected List<SwiftObject> delegate() {
+      return objects;
+   }
+}

http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/blob/0426ebf0/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/features/BulkApi.java
----------------------------------------------------------------------
diff --git a/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/features/BulkApi.java b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/features/BulkApi.java
index 0cc4546..60eb851 100644
--- a/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/features/BulkApi.java
+++ b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/features/BulkApi.java
@@ -33,9 +33,9 @@ import org.jclouds.http.HttpRequest;
 import org.jclouds.io.Payload;
 import org.jclouds.io.Payloads;
 import org.jclouds.openstack.keystone.v2_0.filters.AuthenticateRequest;
+import org.jclouds.openstack.swift.v1.binders.SetPayload;
 import org.jclouds.openstack.swift.v1.domain.BulkDeleteResponse;
 import org.jclouds.openstack.swift.v1.domain.ExtractArchiveResponse;
-import org.jclouds.openstack.swift.v1.features.ObjectApi.SetPayload;
 import org.jclouds.rest.Binder;
 import org.jclouds.rest.annotations.BinderParam;
 import org.jclouds.rest.annotations.QueryParams;

http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/blob/0426ebf0/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/features/ObjectApi.java
----------------------------------------------------------------------
diff --git a/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/features/ObjectApi.java b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/features/ObjectApi.java
index ebcc41b..14ce2f4 100644
--- a/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/features/ObjectApi.java
+++ b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/features/ObjectApi.java
@@ -30,31 +30,28 @@ import javax.ws.rs.PUT;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 
-import org.jclouds.Fallbacks.EmptyFluentIterableOnNotFoundOr404;
 import org.jclouds.Fallbacks.FalseOnNotFoundOr404;
 import org.jclouds.Fallbacks.NullOnNotFoundOr404;
 import org.jclouds.Fallbacks.VoidOnNotFoundOr404;
-import org.jclouds.http.HttpRequest;
 import org.jclouds.http.options.GetOptions;
 import org.jclouds.io.Payload;
 import org.jclouds.javax.annotation.Nullable;
 import org.jclouds.openstack.keystone.v2_0.filters.AuthenticateRequest;
 import org.jclouds.openstack.swift.v1.binders.BindMetadataToHeaders.BindObjectMetadataToHeaders;
 import org.jclouds.openstack.swift.v1.binders.BindMetadataToHeaders.BindRemoveObjectMetadataToHeaders;
+import org.jclouds.openstack.swift.v1.binders.SetPayload;
+import org.jclouds.openstack.swift.v1.domain.ObjectList;
 import org.jclouds.openstack.swift.v1.domain.SwiftObject;
 import org.jclouds.openstack.swift.v1.functions.ETagHeader;
 import org.jclouds.openstack.swift.v1.functions.ParseObjectFromResponse;
 import org.jclouds.openstack.swift.v1.functions.ParseObjectListFromResponse;
 import org.jclouds.openstack.swift.v1.options.ListContainerOptions;
-import org.jclouds.rest.Binder;
 import org.jclouds.rest.annotations.BinderParam;
 import org.jclouds.rest.annotations.Fallback;
 import org.jclouds.rest.annotations.QueryParams;
 import org.jclouds.rest.annotations.RequestFilters;
 import org.jclouds.rest.annotations.ResponseParser;
 
-import com.google.common.collect.FluentIterable;
-
 /**
  * @see <a href=
  *      "http://docs.openstack.org/api/openstack-object-storage/1.0/content/storage-object-services.html"
@@ -67,15 +64,16 @@ public interface ObjectApi {
    /**
     * Lists up to 10,000 objects.
     * 
-    * @return a list of existing storage objects ordered by name.
+    * @return a list of existing storage objects ordered by name or null.
     */
    @Named("ListObjects")
    @GET
    @QueryParams(keys = "format", values = "json")
    @ResponseParser(ParseObjectListFromResponse.class)
-   @Fallback(EmptyFluentIterableOnNotFoundOr404.class)
+   @Fallback(NullOnNotFoundOr404.class)
    @Path("/")
-   FluentIterable<SwiftObject> list(ListContainerOptions options);
+   @Nullable
+   ObjectList list(ListContainerOptions options);
 
    /**
     * Creates or updates an object.
@@ -99,14 +97,6 @@ public interface ObjectApi {
    String replace(@PathParam("objectName") String objectName, @BinderParam(SetPayload.class) Payload payload,
          @BinderParam(BindObjectMetadataToHeaders.class) Map<String, String> metadata);
 
-   static class SetPayload implements Binder {
-      @SuppressWarnings("unchecked")
-      @Override
-      public <R extends HttpRequest> R bindToRequest(R request, Object input) {
-         return (R) request.toBuilder().payload(Payload.class.cast(input)).build();
-      }
-   }
-
    /**
     * Gets the {@link SwiftObject} metadata without its
     * {@link Payload#getInput() body}.

http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/blob/0426ebf0/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/functions/ParseObjectListFromResponse.java
----------------------------------------------------------------------
diff --git a/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/functions/ParseObjectListFromResponse.java b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/functions/ParseObjectListFromResponse.java
index 589e192..5cfa562 100644
--- a/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/functions/ParseObjectListFromResponse.java
+++ b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/functions/ParseObjectListFromResponse.java
@@ -27,13 +27,16 @@ import org.jclouds.http.HttpResponse;
 import org.jclouds.http.functions.ParseJson;
 import org.jclouds.io.Payload;
 import org.jclouds.io.Payloads;
+import org.jclouds.openstack.swift.v1.domain.Container;
+import org.jclouds.openstack.swift.v1.domain.ObjectList;
 import org.jclouds.openstack.swift.v1.domain.SwiftObject;
 import org.jclouds.rest.InvocationContext;
+import org.jclouds.rest.internal.GeneratedHttpRequest;
 
 import com.google.common.base.Function;
-import com.google.common.collect.FluentIterable;
+import com.google.common.collect.Lists;
 
-public class ParseObjectListFromResponse implements Function<HttpResponse, FluentIterable<SwiftObject>>,
+public class ParseObjectListFromResponse implements Function<HttpResponse, ObjectList>,
       InvocationContext<ParseObjectListFromResponse> {
 
    private static final class InternalObject {
@@ -45,18 +48,21 @@ public class ParseObjectListFromResponse implements Function<HttpResponse, Fluen
    }
 
    private final ParseJson<List<InternalObject>> json;
+   private final ParseContainerFromHeaders parseContainer;
 
    @Inject
-   ParseObjectListFromResponse(ParseJson<List<InternalObject>> json) {
+   ParseObjectListFromResponse(ParseJson<List<InternalObject>> json, ParseContainerFromHeaders parseContainer) {
       this.json = json;
+      this.parseContainer = parseContainer;
    }
 
    private ToSwiftObject toSwiftObject;
 
    @Override
-   public FluentIterable<SwiftObject> apply(HttpResponse from) {
-      return FluentIterable.from(json.apply(from)) //
-            .transform(toSwiftObject);
+   public ObjectList apply(HttpResponse from) {
+      List<SwiftObject> objects = Lists.transform(json.apply(from), toSwiftObject);
+      Container container = parseContainer.apply(from);
+      return ObjectList.create(objects, container);
    }
 
    static class ToSwiftObject implements Function<InternalObject, SwiftObject> {
@@ -79,12 +85,13 @@ public class ParseObjectListFromResponse implements Function<HttpResponse, Fluen
 
    @Override
    public ParseObjectListFromResponse setContext(HttpRequest request) {
+      parseContainer.name = GeneratedHttpRequest.class.cast(request).getCaller().get().getArgs().get(1).toString();
       String containerUri = request.getEndpoint().toString();
       int queryIndex = containerUri.indexOf('?');
       if (queryIndex != -1) {
          containerUri = containerUri.substring(0, queryIndex);
       }
-      this.toSwiftObject = new ToSwiftObject(containerUri);
+      toSwiftObject = new ToSwiftObject(containerUri);
       return this;
    }
 

http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/blob/0426ebf0/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/ObjectApiLiveTest.java
----------------------------------------------------------------------
diff --git a/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/ObjectApiLiveTest.java b/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/ObjectApiLiveTest.java
index 8dad19a..cfc7192 100644
--- a/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/ObjectApiLiveTest.java
+++ b/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/ObjectApiLiveTest.java
@@ -32,6 +32,7 @@ import java.util.Map.Entry;
 import java.util.concurrent.TimeUnit;
 
 import org.jclouds.http.options.GetOptions;
+import org.jclouds.openstack.swift.v1.domain.ObjectList;
 import org.jclouds.openstack.swift.v1.domain.SwiftObject;
 import org.jclouds.openstack.swift.v1.internal.BaseSwiftApiLiveTest;
 import org.jclouds.openstack.swift.v1.options.CreateContainerOptions;
@@ -40,7 +41,6 @@ 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;
 
 /**
@@ -56,7 +56,8 @@ public class ObjectApiLiveTest extends BaseSwiftApiLiveTest {
    public void list() throws Exception {
       for (String regionId : api.configuredRegions()) {
          ObjectApi objectApi = api.objectApiInRegionForContainer(regionId, containerName);
-         FluentIterable<SwiftObject> response = objectApi.list(new ListContainerOptions());
+         ObjectList response = objectApi.list(new ListContainerOptions());
+         assertEquals(response.container(), api.containerApiInRegion(regionId).get(containerName));
          assertNotNull(response);
          for (SwiftObject object : response) {
             checkObject(object);

http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/blob/0426ebf0/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/ObjectApiMockTest.java
----------------------------------------------------------------------
diff --git a/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/ObjectApiMockTest.java b/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/ObjectApiMockTest.java
index 789ec00..3b54eac 100644
--- a/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/ObjectApiMockTest.java
+++ b/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/ObjectApiMockTest.java
@@ -23,6 +23,7 @@ import static org.jclouds.io.Payloads.newStringPayload;
 import static org.jclouds.openstack.swift.v1.options.ListContainerOptions.Builder.marker;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertTrue;
+import static org.jclouds.openstack.swift.v1.features.ContainerApiMockTest.*;
 
 import java.net.URI;
 import java.util.Map;
@@ -32,6 +33,7 @@ import org.jclouds.date.internal.SimpleDateFormatDateService;
 import org.jclouds.io.Payload;
 import org.jclouds.io.Payloads;
 import org.jclouds.openstack.swift.v1.SwiftApi;
+import org.jclouds.openstack.swift.v1.domain.ObjectList;
 import org.jclouds.openstack.swift.v1.domain.SwiftObject;
 import org.jclouds.openstack.swift.v1.internal.BaseSwiftMockTest;
 import org.jclouds.openstack.swift.v1.options.ListContainerOptions;
@@ -82,13 +84,16 @@ public class ObjectApiMockTest extends BaseSwiftMockTest {
    public void list() throws Exception {
       MockWebServer server = mockSwiftServer();
       server.enqueue(new MockResponse().setBody(access));
-      server.enqueue(new MockResponse().setBody(objectList));
+      server.enqueue(containerResponse() //
+            .addHeader("X-Container-Read", ".r:*,.rlistings") //
+            .setBody(objectList));
 
       try {
          SwiftApi api = swiftApi(server.getUrl("/").toString());
-         ImmutableList<SwiftObject> objects = api.objectApiInRegionForContainer("DFW", "myContainer")
-               .list(new ListContainerOptions()).toList();
+         ObjectList objects = api.objectApiInRegionForContainer("DFW", "myContainer").list(new ListContainerOptions());
          assertEquals(objects, parsedObjectsForUrl(server.getUrl("/").toString()));
+         assertEquals(objects.container().name(), "myContainer");
+         assertTrue(objects.container().anybodyRead().get());
 
          assertEquals(server.getRequestCount(), 2);
          assertEquals(server.takeRequest().getRequestLine(), "POST /tokens HTTP/1.1");
@@ -102,12 +107,11 @@ public class ObjectApiMockTest extends BaseSwiftMockTest {
    public void listOptions() throws Exception {
       MockWebServer server = mockSwiftServer();
       server.enqueue(new MockResponse().setBody(access));
-      server.enqueue(new MockResponse().setBody(objectList));
+      server.enqueue(containerResponse().setBody(objectList));
 
       try {
          SwiftApi api = swiftApi(server.getUrl("/").toString());
-         ImmutableList<SwiftObject> objects = api.objectApiInRegionForContainer("DFW", "myContainer")
-               .list(marker("test")).toList();
+         ObjectList objects = api.objectApiInRegionForContainer("DFW", "myContainer").list(marker("test"));
          assertEquals(objects, parsedObjectsForUrl(server.getUrl("/").toString()));
 
          assertEquals(server.getRequestCount(), 2);

http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/blob/0426ebf0/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/internal/BaseSwiftApiLiveTest.java
----------------------------------------------------------------------
diff --git a/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/internal/BaseSwiftApiLiveTest.java b/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/internal/BaseSwiftApiLiveTest.java
index 491a5f6..8d8166e 100644
--- a/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/internal/BaseSwiftApiLiveTest.java
+++ b/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/internal/BaseSwiftApiLiveTest.java
@@ -18,17 +18,19 @@ package org.jclouds.openstack.swift.v1.internal;
 
 import static com.google.common.base.Preconditions.checkState;
 
+import java.util.List;
 import java.util.Properties;
 
 import org.jclouds.apis.BaseApiLiveTest;
 import org.jclouds.openstack.keystone.v2_0.config.KeystoneProperties;
 import org.jclouds.openstack.swift.v1.SwiftApi;
 import org.jclouds.openstack.swift.v1.domain.BulkDeleteResponse;
+import org.jclouds.openstack.swift.v1.domain.ObjectList;
 import org.jclouds.openstack.swift.v1.domain.SwiftObject;
 import org.jclouds.openstack.swift.v1.options.ListContainerOptions;
 
 import com.google.common.base.Function;
-import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
 
 public class BaseSwiftApiLiveTest extends BaseApiLiveTest<SwiftApi> {
 
@@ -44,14 +46,15 @@ public class BaseSwiftApiLiveTest extends BaseApiLiveTest<SwiftApi> {
    }
 
    protected void deleteAllObjectsInContainer(String regionId, final String containerName) {
-      ImmutableList<String> pathsToDelete = api.objectApiInRegionForContainer(regionId, containerName)
-            .list(new ListContainerOptions()).transform(new Function<SwiftObject, String>() {
-
-               public String apply(SwiftObject input) {
-                  return containerName + "/" + input.name();
-               }
-
-            }).toList();
+      ObjectList objects = api.objectApiInRegionForContainer(regionId, containerName).list(new ListContainerOptions());
+      if (objects == null) {
+         return;
+      }
+      List<String> pathsToDelete = Lists.transform(objects, new Function<SwiftObject, String>() {
+         public String apply(SwiftObject input) {
+            return containerName + "/" + input.name();
+         }
+      });
       if (!pathsToDelete.isEmpty()) {
          BulkDeleteResponse response = api.bulkApiInRegion(regionId).bulkDelete(pathsToDelete);
          checkState(response.errors().isEmpty(), "Errors deleting paths %s: %s", pathsToDelete, response);


[3/3] git commit: JCLOUDS-306. expose Container.anybodyRead

Posted by ad...@apache.org.
JCLOUDS-306. expose Container.anybodyRead


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

Branch: refs/heads/master
Commit: 523b134256a781e7c8a5aaf314301cee7f7bd702
Parents: fd3f8cd
Author: Adrian Cole <ad...@gmail.com>
Authored: Sun Sep 29 23:47:23 2013 -0700
Committer: Adrian Cole <ad...@gmail.com>
Committed: Mon Sep 30 00:16:17 2013 -0700

----------------------------------------------------------------------
 .../openstack/swift/v1/domain/Container.java    | 37 +++++++++++++++++---
 .../v1/functions/ParseContainerFromHeaders.java |  3 +-
 .../swift/v1/features/ContainerApiMockTest.java |  3 --
 .../features/CreatePublicContainerLiveTest.java | 29 +++------------
 4 files changed, 39 insertions(+), 33 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/blob/523b1342/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/domain/Container.java
----------------------------------------------------------------------
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 cfee278..eda6a6b 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
@@ -25,9 +25,11 @@ import java.util.Map;
 import java.util.Map.Entry;
 
 import org.jclouds.openstack.swift.v1.features.ContainerApi;
+import org.jclouds.openstack.swift.v1.options.CreateContainerOptions;
 
 import com.google.common.base.Objects;
 import com.google.common.base.Objects.ToStringHelper;
+import com.google.common.base.Optional;
 import com.google.common.collect.ImmutableMap;
 
 /**
@@ -40,13 +42,16 @@ public class Container implements Comparable<Container> {
    private final String name;
    private final long objectCount;
    private final long bytesUsed;
+   private final Optional<Boolean> anybodyRead;
    private final Map<String, String> metadata;
 
-   @ConstructorProperties({ "name", "count", "bytes", "metadata" })
-   protected Container(String name, long objectCount, long bytesUsed, Map<String, String> metadata) {
+   @ConstructorProperties({ "name", "count", "bytes", "anybodyRead", "metadata" })
+   protected Container(String name, long objectCount, long bytesUsed, Optional<Boolean> anybodyRead,
+         Map<String, String> metadata) {
       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;
    }
 
@@ -63,6 +68,17 @@ public class Container implements Comparable<Container> {
    }
 
    /**
+    * Absent except in {@link ContainerApi#get(String) GetContainer} commands.
+    * 
+    * When present, designates that the container is publicly readable.
+    * 
+    * @see CreateContainerOptions#anybodyRead()
+    */
+   public Optional<Boolean> anybodyRead() {
+      return anybodyRead;
+   }
+
+   /**
     * Empty except in {@link ContainerApi#get(String) GetContainer} commands.
     * 
     * <h3>Note</h3>
@@ -92,7 +108,7 @@ public class Container implements Comparable<Container> {
 
    @Override
    public int hashCode() {
-      return Objects.hashCode(name(), objectCount(), bytesUsed(), metadata());
+      return Objects.hashCode(name(), objectCount(), bytesUsed(), anybodyRead(), metadata());
    }
 
    @Override
@@ -101,10 +117,11 @@ public class Container implements Comparable<Container> {
    }
 
    protected ToStringHelper string() {
-      return toStringHelper("") //
+      return toStringHelper("").omitNullValues() //
             .add("name", name()) //
             .add("objectCount", objectCount()) //
             .add("bytesUsed", bytesUsed()) //
+            .add("anybodyRead", anybodyRead().orNull()) //
             .add("metadata", metadata());
    }
 
@@ -129,6 +146,7 @@ public class Container implements Comparable<Container> {
       protected String name;
       protected long objectCount;
       protected long bytesUsed;
+      protected Optional<Boolean> anybodyRead = Optional.absent();
       protected Map<String, String> metadata = ImmutableMap.of();
 
       /**
@@ -156,6 +174,14 @@ public class Container implements Comparable<Container> {
       }
 
       /**
+       * @see Container#anybodyRead()
+       */
+      public Builder anybodyRead(Boolean anybodyRead) {
+         this.anybodyRead = Optional.fromNullable(anybodyRead);
+         return this;
+      }
+
+      /**
        * Will lower-case all metadata keys due to a swift implementation
        * decision.
        * 
@@ -171,13 +197,14 @@ public class Container implements Comparable<Container> {
       }
 
       public Container build() {
-         return new Container(name, objectCount, bytesUsed, metadata);
+         return new Container(name, objectCount, bytesUsed, anybodyRead, metadata);
       }
 
       public Builder fromContainer(Container from) {
          return name(from.name()) //
                .objectCount(from.objectCount()) //
                .bytesUsed(from.bytesUsed()) //
+               .anybodyRead(from.anybodyRead().orNull()) //
                .metadata(from.metadata());
       }
    }

http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/blob/523b1342/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/functions/ParseContainerFromHeaders.java
----------------------------------------------------------------------
diff --git a/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/functions/ParseContainerFromHeaders.java b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/functions/ParseContainerFromHeaders.java
index e246af5..5fb3c9f 100644
--- a/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/functions/ParseContainerFromHeaders.java
+++ b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/functions/ParseContainerFromHeaders.java
@@ -27,7 +27,7 @@ import com.google.common.base.Function;
 public class ParseContainerFromHeaders implements Function<HttpResponse, Container>,
       InvocationContext<ParseContainerFromHeaders> {
 
-   private String name;
+   String name;
 
    @Override
    public Container apply(HttpResponse from) {
@@ -35,6 +35,7 @@ public class ParseContainerFromHeaders implements Function<HttpResponse, Contain
             .name(name) //
             .bytesUsed(Long.parseLong(from.getFirstHeaderOrNull("X-Container-Bytes-Used"))) //
             .objectCount(Long.parseLong(from.getFirstHeaderOrNull("X-Container-Object-Count"))) //
+            .anybodyRead(".r:*,.rlistings".equals(from.getFirstHeaderOrNull("X-Container-Read"))) //
             .metadata(EntriesWithoutMetaPrefix.INSTANCE.apply(from.getHeaders())).build();
    }
 

http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/blob/523b1342/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/ContainerApiMockTest.java
----------------------------------------------------------------------
diff --git a/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/ContainerApiMockTest.java b/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/ContainerApiMockTest.java
index 159021e..6431a99 100644
--- a/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/ContainerApiMockTest.java
+++ b/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/ContainerApiMockTest.java
@@ -36,9 +36,6 @@ import com.squareup.okhttp.mockwebserver.MockResponse;
 import com.squareup.okhttp.mockwebserver.MockWebServer;
 import com.squareup.okhttp.mockwebserver.RecordedRequest;
 
-/**
- * @author Adrian Cole
- */
 @Test
 public class ContainerApiMockTest extends BaseSwiftMockTest {
 

http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/blob/523b1342/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/CreatePublicContainerLiveTest.java
----------------------------------------------------------------------
diff --git a/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/CreatePublicContainerLiveTest.java b/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/CreatePublicContainerLiveTest.java
index 83f586e..f6251d0 100644
--- a/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/CreatePublicContainerLiveTest.java
+++ b/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/CreatePublicContainerLiveTest.java
@@ -16,40 +16,22 @@
  */
 package org.jclouds.openstack.swift.v1.features;
 
-import static org.jclouds.io.Payloads.newStringPayload;
 import static org.jclouds.openstack.swift.v1.options.CreateContainerOptions.Builder.anybodyRead;
-import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
 
-import java.io.InputStream;
-
-import org.jclouds.http.options.GetOptions;
 import org.jclouds.openstack.swift.v1.internal.BaseSwiftApiLiveTest;
-import org.jclouds.util.Strings2;
 import org.testng.annotations.AfterClass;
 import org.testng.annotations.Test;
 
-import com.google.common.collect.ImmutableMap;
-
-/**
- * @author Adrian Cole
- */
 @Test(groups = "live", testName = "CreatePublicContainerLiveTest")
 public class CreatePublicContainerLiveTest extends BaseSwiftApiLiveTest {
 
    private String name = getClass().getSimpleName();
-   private String containerName = getClass().getSimpleName() + "Container";
 
-   public void anybodyReadObjectUri() throws Exception {
+   public void anybodyReadUpdatesMetadata() throws Exception {
       for (String regionId : api.configuredRegions()) {
-         api.containerApiInRegion(regionId).createIfAbsent(containerName, anybodyRead());
-         api.containerApiInRegion(regionId).get(containerName);
-
-         ObjectApi objectApi = api.objectApiInRegionForContainer(regionId, containerName);
-         objectApi.replace(name, newStringPayload("swifty"), ImmutableMap.<String, String> of());
-
-         InputStream publicStream = objectApi.get(name, new GetOptions()).uri().toURL().openStream();
-
-         assertEquals(Strings2.toStringAndClose(publicStream), "swifty");
+         api.containerApiInRegion(regionId).createIfAbsent(name, anybodyRead());
+         assertTrue(api.containerApiInRegion(regionId).get(name).anybodyRead().get());
       }
    }
 
@@ -57,8 +39,7 @@ public class CreatePublicContainerLiveTest extends BaseSwiftApiLiveTest {
    @AfterClass(groups = "live")
    public void tearDown() {
       for (String regionId : api.configuredRegions()) {
-         api.objectApiInRegionForContainer(regionId, containerName).delete(name);
-         api.containerApiInRegion(regionId).deleteIfEmpty(containerName);
+         api.containerApiInRegion(regionId).deleteIfEmpty(name);
       }
       super.tearDown();
    }