You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jclouds.apache.org by an...@apache.org on 2014/10/09 00:17:14 UTC

[3/6] Import openstack-swift from labs.

http://git-wip-us.apache.org/repos/asf/jclouds/blob/8505539a/apis/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/functions/ParseObjectFromResponse.java
----------------------------------------------------------------------
diff --git a/apis/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/functions/ParseObjectFromResponse.java b/apis/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/functions/ParseObjectFromResponse.java
new file mode 100644
index 0000000..cc934e4
--- /dev/null
+++ b/apis/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/functions/ParseObjectFromResponse.java
@@ -0,0 +1,87 @@
+/*
+ * 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.functions;
+
+import static com.google.common.io.BaseEncoding.base16;
+import static com.google.common.net.HttpHeaders.ETAG;
+import static com.google.common.net.HttpHeaders.LAST_MODIFIED;
+import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.OBJECT_DELETE_AT;
+
+import java.net.URI;
+import java.util.Date;
+
+import javax.inject.Inject;
+
+import org.jclouds.date.DateService;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.io.MutableContentMetadata;
+import org.jclouds.io.Payload;
+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.hash.HashCode;
+
+public class ParseObjectFromResponse implements Function<HttpResponse, SwiftObject>,
+      InvocationContext<ParseObjectFromResponse> {
+   private final DateService dates;
+
+   @Inject
+   ParseObjectFromResponse(DateService dates) {
+      this.dates = dates;
+   }
+
+   private String uri;
+   private String name;
+
+   @Override
+   public SwiftObject apply(HttpResponse from) {
+
+      Payload payload = from.getPayload();
+      MutableContentMetadata contentMeta = payload.getContentMetadata();
+
+      String deleteAt = from.getFirstHeaderOrNull(OBJECT_DELETE_AT);
+      if (deleteAt != null) {
+         long fromEpoch = Long.parseLong(from.getFirstHeaderOrNull(OBJECT_DELETE_AT)) * 1000;
+         contentMeta.setExpires(new Date(fromEpoch));
+         payload.setContentMetadata(contentMeta);
+      }
+
+      String etag = from.getFirstHeaderOrNull(ETAG);
+      if (etag != null) {
+         payload.getContentMetadata().setContentMD5(HashCode.fromBytes(base16().lowerCase().decode(etag)));
+      }
+
+      return SwiftObject.builder()
+            .uri(URI.create(uri))
+            .name(name)
+            .etag(etag)
+            .payload(payload)
+            .lastModified(dates.rfc822DateParse(from.getFirstHeaderOrNull(LAST_MODIFIED)))
+            .headers(from.getHeaders())
+            .metadata(EntriesWithoutMetaPrefix.INSTANCE.apply(from.getHeaders())).build();
+   }
+
+   @Override
+   public ParseObjectFromResponse setContext(HttpRequest request) {
+      this.uri = request.getEndpoint().toString();
+      this.name = GeneratedHttpRequest.class.cast(request).getInvocation().getArgs().get(0).toString();
+      return this;
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/8505539a/apis/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/functions/ParseObjectListFromResponse.java
----------------------------------------------------------------------
diff --git a/apis/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/functions/ParseObjectListFromResponse.java b/apis/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/functions/ParseObjectListFromResponse.java
new file mode 100644
index 0000000..ff12e0f
--- /dev/null
+++ b/apis/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/functions/ParseObjectListFromResponse.java
@@ -0,0 +1,113 @@
+/*
+ * 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.functions;
+
+import static com.google.common.io.BaseEncoding.base16;
+import static org.jclouds.http.Uris.uriBuilder;
+
+import java.util.Date;
+import java.util.List;
+
+import javax.inject.Inject;
+
+import org.jclouds.http.HttpRequest;
+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.Lists;
+import com.google.common.hash.HashCode;
+import com.google.common.io.ByteSource;
+
+public class ParseObjectListFromResponse implements Function<HttpResponse, ObjectList>,
+      InvocationContext<ParseObjectListFromResponse> {
+
+   private static final class InternalObject {
+      String name;
+      String hash;
+      long bytes;
+      String content_type;
+      Date last_modified;
+      Date expires;
+   }
+
+   private final ParseJson<List<InternalObject>> json;
+   private final ParseContainerFromHeaders parseContainer;
+
+   @Inject
+   ParseObjectListFromResponse(ParseJson<List<InternalObject>> json, ParseContainerFromHeaders parseContainer) {
+      this.json = json;
+      this.parseContainer = parseContainer;
+   }
+
+   private ToSwiftObject toSwiftObject;
+
+   @Override
+   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> {
+      private final String containerUri;
+
+      ToSwiftObject(String containerUri) {
+         this.containerUri = containerUri;
+      }
+
+      @Override
+      public SwiftObject apply(InternalObject input) {
+         return SwiftObject.builder()
+               .uri(uriBuilder(containerUri).clearQuery().appendPath(input.name).build())
+               .name(input.name)
+               .etag(input.hash)
+               .payload(payload(input.bytes, input.hash, input.content_type, input.expires))
+               .lastModified(input.last_modified).build();
+      }
+   }
+
+   @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);
+      }
+      toSwiftObject = new ToSwiftObject(containerUri);
+      return this;
+   }
+
+   private static Payload payload(long bytes, String hash, String contentType, Date expires) {
+      Payload payload = Payloads.newByteSourcePayload(ByteSource.empty());
+      payload.getContentMetadata().setContentLength(bytes);
+      payload.getContentMetadata().setContentType(contentType);
+      payload.getContentMetadata().setExpires(expires);
+      if (hash != null) {
+         payload.getContentMetadata().setContentMD5(HashCode.fromBytes(base16().lowerCase().decode(hash)));
+      }
+      return payload;
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/8505539a/apis/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/handlers/SwiftErrorHandler.java
----------------------------------------------------------------------
diff --git a/apis/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/handlers/SwiftErrorHandler.java b/apis/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/handlers/SwiftErrorHandler.java
new file mode 100644
index 0000000..2bde41c
--- /dev/null
+++ b/apis/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/handlers/SwiftErrorHandler.java
@@ -0,0 +1,91 @@
+/*
+ * 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.handlers;
+
+import static org.jclouds.http.HttpUtils.closeClientButKeepContentStream;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.jclouds.blobstore.ContainerNotFoundException;
+import org.jclouds.blobstore.KeyNotFoundException;
+import org.jclouds.http.HttpCommand;
+import org.jclouds.http.HttpErrorHandler;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.http.HttpResponseException;
+import org.jclouds.openstack.swift.v1.CopyObjectException;
+import org.jclouds.openstack.swift.v1.reference.SwiftHeaders;
+import org.jclouds.rest.AuthorizationException;
+import org.jclouds.rest.InsufficientResourcesException;
+
+// TODO: is there error spec someplace? let's type errors, etc.
+public class SwiftErrorHandler implements HttpErrorHandler {
+   public static final String PREFIX = "^/v[0-9][^/]*/[a-zA-Z]+_[^/]+/";
+   public static final Pattern CONTAINER_PATH = Pattern.compile(PREFIX + "([^/]+)$");
+   public static final Pattern CONTAINER_KEY_PATH = Pattern.compile(PREFIX + "([^/]+)/(.*)");
+
+   public void handleError(HttpCommand command, HttpResponse response) {
+      // it is important to always read fully and close streams
+      byte[] data = closeClientButKeepContentStream(response);
+      String message = data != null ? new String(data) : null;
+
+      Exception exception = message != null ? new HttpResponseException(command, response, message)
+               : new HttpResponseException(command, response);
+      message = message != null ? message : String.format("%s -> %s", command.getCurrentRequest().getRequestLine(),
+               response.getStatusLine());
+      switch (response.getStatusCode()) {
+         case 401:
+            exception = new AuthorizationException(exception.getMessage(), exception);
+            break;
+         case 404:
+            Exception oldException = exception;         
+            String sourcePath = command.getCurrentRequest().getFirstHeaderOrNull(SwiftHeaders.OBJECT_COPY_FROM);
+            if (sourcePath != null) {
+               // the path returned here is in the form "/v1/tenant-id/destContainer/destObject"
+               String path = command.getCurrentRequest().getEndpoint().getPath();
+               int startOfDestinationPath = path.lastIndexOf("/", path.lastIndexOf("/") - 1);
+               // get the "/destContainer/destObject" portion of the path
+               String destinationPath = path.substring(startOfDestinationPath);
+               
+               exception = new CopyObjectException(sourcePath, destinationPath, message);
+               exception.initCause(oldException);
+            } else if (!command.getCurrentRequest().getMethod().equals("DELETE")) {
+               String path = command.getCurrentRequest().getEndpoint().getPath();
+               Matcher matcher = CONTAINER_PATH.matcher(path);
+               
+               if (matcher.find()) {
+                  exception = new ContainerNotFoundException(matcher.group(1), message);
+                  exception.initCause(oldException);
+               } else {
+                  matcher = CONTAINER_KEY_PATH.matcher(path);
+                  if (matcher.find()) {
+                     exception = new KeyNotFoundException(matcher.group(1), matcher.group(2), message);
+                     exception.initCause(oldException);
+                  }
+               }
+            }
+            break;
+         case 409:
+            exception = new IllegalStateException(exception.getMessage(), exception);
+            break;
+         case 413:
+            exception = new InsufficientResourcesException(exception.getMessage(), exception);
+            break;
+      }
+      command.setException(exception);
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/8505539a/apis/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/options/CreateContainerOptions.java
----------------------------------------------------------------------
diff --git a/apis/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/options/CreateContainerOptions.java b/apis/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/options/CreateContainerOptions.java
new file mode 100644
index 0000000..2014f61
--- /dev/null
+++ b/apis/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/options/CreateContainerOptions.java
@@ -0,0 +1,108 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.openstack.swift.v1.options;
+
+import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.CONTAINER_ACL_ANYBODY_READ;
+import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.CONTAINER_METADATA_PREFIX;
+import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.CONTAINER_READ;
+import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.VERSIONS_LOCATION;
+
+import java.util.Map;
+
+import org.jclouds.http.options.BaseHttpRequestOptions;
+import org.jclouds.openstack.swift.v1.binders.BindMetadataToHeaders;
+
+import com.google.common.collect.Multimap;
+
+/**
+ * Options for creating a {@link Container} 
+ * 
+ * @see ContainerApi#create(String, CreateContainerOptions)
+ */
+public class CreateContainerOptions extends BaseHttpRequestOptions {
+
+   public static final CreateContainerOptions NONE = new CreateContainerOptions();
+
+   /** 
+    * Sets the headers on a container at creation.
+    */
+   public CreateContainerOptions headers(Multimap<String, String> headers) {
+      this.headers.putAll(headers);
+      return this;
+   }
+
+   /**
+    * Sets the metadata on a container at creation. 
+    */
+   public CreateContainerOptions metadata(Map<String, String> metadata) {
+      this.headers.putAll(bindMetadataToHeaders.toHeaders(metadata));
+      return this;
+   }
+
+   /**
+    * Sets the public ACL on the container so that anybody can read it.
+    */
+   public CreateContainerOptions anybodyRead() {
+      this.headers.put(CONTAINER_READ, CONTAINER_ACL_ANYBODY_READ);
+      return this;
+   }
+
+   /**
+    * Sets the container that will contain object versions.
+    */
+   public CreateContainerOptions versionsLocation(String containerName) {
+      this.headers.put(VERSIONS_LOCATION, containerName);
+      return this;
+   }
+
+   public static class Builder {
+
+      /**
+       * @see CreateContainerOptions#anybodyRead
+       */
+      public static CreateContainerOptions anybodyRead() {
+         CreateContainerOptions options = new CreateContainerOptions();
+         return options.anybodyRead();
+      }
+
+      /**
+       * @see CreateContainerOptions#headers
+       */
+      public static CreateContainerOptions headers(Multimap<String, String> headers) {
+         CreateContainerOptions options = new CreateContainerOptions();
+         return options.headers(headers);
+      }
+
+      /**
+       * @see CreateContainerOptions#metadata
+       */
+      public static CreateContainerOptions metadata(Map<String, String> metadata) {
+         CreateContainerOptions options = new CreateContainerOptions();
+         return options.metadata(metadata);
+      }
+
+      /**
+       * @see CreateContainerOptions#versionsLocation
+       */
+      public static CreateContainerOptions versionsLocation(String containerName) {
+         CreateContainerOptions options = new CreateContainerOptions();
+         return options.versionsLocation(containerName);
+      }
+   }
+
+   private static final BindMetadataToHeaders bindMetadataToHeaders = new BindMetadataToHeaders(CONTAINER_METADATA_PREFIX);
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/8505539a/apis/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/options/ListContainerOptions.java
----------------------------------------------------------------------
diff --git a/apis/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/options/ListContainerOptions.java b/apis/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/options/ListContainerOptions.java
new file mode 100644
index 0000000..8b01aae
--- /dev/null
+++ b/apis/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/options/ListContainerOptions.java
@@ -0,0 +1,132 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.openstack.swift.v1.options;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+
+import org.jclouds.http.options.BaseHttpRequestOptions;
+
+/**
+ * Options for listing containers. 
+ * 
+ * @see ContainerApi#list(ListContainerOptions)
+ */
+public class ListContainerOptions extends BaseHttpRequestOptions {
+   public static final ListContainerOptions NONE = new ListContainerOptions();
+
+   /** 
+    * list operation returns no more than this amount. 
+    */
+   public ListContainerOptions limit(int limit) {
+      checkState(limit >= 0, "limit must be >= 0");
+      checkState(limit <= 10000, "limit must be <= 10000");
+      queryParameters.put("limit", Integer.toString(limit));
+      return this;
+   }
+
+   /** 
+    * object names greater in value than the specified marker are returned.
+    */
+   public ListContainerOptions marker(String marker) {
+      queryParameters.put("marker", checkNotNull(marker, "marker"));
+      return this;
+   }
+
+   /** 
+    * object names less in value than the specified marker are returned.
+    */
+   public ListContainerOptions endMarker(String endMarker) {
+      queryParameters.put("end_marker", checkNotNull(endMarker, "endMarker"));
+      return this;
+   }
+
+   /** 
+    * object names beginning with this substring are returned.
+    */
+   public ListContainerOptions prefix(String prefix) {
+      queryParameters.put("prefix", checkNotNull(prefix, "prefix"));
+      return this;
+   }
+
+   /** 
+    * object names nested in the container are returned.
+    */
+   public ListContainerOptions delimiter(char delimiter) {
+      queryParameters.put("delimiter", Character.toString(delimiter));
+      return this;
+   }
+
+   /** 
+    * object names nested in the pseudo path are returned.
+    */
+   public ListContainerOptions path(String path) {
+      queryParameters.put("path", checkNotNull(path, "path"));
+      return this;
+   }
+
+   public static class Builder {
+
+      /** 
+       * @see ListContainerOptions#limit
+       */
+      public static ListContainerOptions limit(int limit) {
+         ListContainerOptions options = new ListContainerOptions();
+         return options.limit(limit);
+      }
+
+      /** 
+       * @see ListContainerOptions#marker
+       */
+      public static ListContainerOptions marker(String marker) {
+         ListContainerOptions options = new ListContainerOptions();
+         return options.marker(marker);
+      }
+
+      /** 
+       * @see ListContainerOptions#endMarker
+       */
+      public static ListContainerOptions endMarker(String endMarker) {
+         ListContainerOptions options = new ListContainerOptions();
+         return options.endMarker(endMarker);
+      }
+
+      /** 
+       * @see ListContainerOptions#prefix 
+       */
+      public static ListContainerOptions prefix(String prefix) {
+         ListContainerOptions options = new ListContainerOptions();
+         return options.prefix(prefix);
+      }
+
+      /** 
+       * @see ListContainerOptions#delimiter 
+       */
+      public static ListContainerOptions delimiter(char delimiter) {
+         ListContainerOptions options = new ListContainerOptions();
+         return options.delimiter(delimiter);
+      }
+
+      /** 
+       * @see ListContainerOptions#path 
+       */
+      public static ListContainerOptions path(String path) {
+         ListContainerOptions options = new ListContainerOptions();
+         return options.path(path);
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/8505539a/apis/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/options/PutOptions.java
----------------------------------------------------------------------
diff --git a/apis/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/options/PutOptions.java b/apis/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/options/PutOptions.java
new file mode 100644
index 0000000..d4a1ed3
--- /dev/null
+++ b/apis/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/options/PutOptions.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.options;
+
+import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.OBJECT_METADATA_PREFIX;
+
+import java.util.Map;
+
+import org.jclouds.http.options.BaseHttpRequestOptions;
+import org.jclouds.openstack.swift.v1.binders.BindMetadataToHeaders;
+
+import com.google.common.collect.Multimap;
+
+/**
+ * Options for creating an Object. 
+ */
+public class PutOptions extends BaseHttpRequestOptions {
+
+   public static final PutOptions NONE = new PutOptions();
+
+   /**
+    * Sets the metadata on a container at creation.
+    */
+   public PutOptions metadata(Map<String, String> metadata) {
+      this.headers.putAll(bindMetadataToHeaders.toHeaders(metadata));
+      return this;
+   }
+
+   /**
+    * Sets the headers on a container at creation.
+    */
+   public PutOptions headers(Multimap<String, String> headers) {
+      this.headers.putAll(headers);
+      return this;
+   }
+
+   public static class Builder {
+
+      /**
+       * @see PutOptions#headers
+       */
+      public static PutOptions headers(Multimap<String, String> headers) {
+         PutOptions options = new PutOptions();
+         return options.headers(headers);
+      }
+
+      /**
+       * @see PutOptions#metadata
+       */
+      public static PutOptions metadata(Map<String, String> metadata) {
+         PutOptions options = new PutOptions();
+         return options.metadata(metadata);
+      }
+   }
+
+   private static final BindMetadataToHeaders bindMetadataToHeaders = new BindMetadataToHeaders(OBJECT_METADATA_PREFIX);
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/8505539a/apis/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/options/UpdateContainerOptions.java
----------------------------------------------------------------------
diff --git a/apis/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/options/UpdateContainerOptions.java b/apis/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/options/UpdateContainerOptions.java
new file mode 100644
index 0000000..f74b7fe
--- /dev/null
+++ b/apis/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/options/UpdateContainerOptions.java
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.openstack.swift.v1.options;
+
+import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.CONTAINER_ACL_ANYBODY_READ;
+import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.CONTAINER_METADATA_PREFIX;
+import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.CONTAINER_READ;
+import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.VERSIONS_LOCATION;
+
+import java.util.Map;
+
+import org.jclouds.http.options.BaseHttpRequestOptions;
+import org.jclouds.openstack.swift.v1.binders.BindMetadataToHeaders;
+
+import com.google.common.collect.Multimap;
+
+/**
+ * Options for updating a {@link Container}.
+ *
+ * @see org.jclouds.openstack.swift.v1.features.ContainerApi#update(String, UpdateContainerOptions)
+ */
+public class UpdateContainerOptions extends BaseHttpRequestOptions {
+   public static final UpdateContainerOptions NONE = new UpdateContainerOptions();
+
+   /**
+    * Sets the headers on a container at creation.
+    */
+   public UpdateContainerOptions headers(Multimap<String, String> headers) {
+      this.headers.putAll(headers);
+      return this;
+   }
+
+   /**
+    * Sets the metadata on a container at creation.
+    */
+   public UpdateContainerOptions metadata(Map<String, String> metadata) {
+      this.headers.putAll(bindMetadataToHeaders.toHeaders(metadata));
+      return this;
+   }
+
+   /**
+    * Sets the public ACL on the container so that anybody can read it.
+    */
+   public UpdateContainerOptions anybodyRead() {
+      this.headers.put(CONTAINER_READ, CONTAINER_ACL_ANYBODY_READ);
+      return this;
+   }
+
+   /**
+    * Sets the container that will contain object versions.
+    */
+   public UpdateContainerOptions versionsLocation(String containerName) {
+      this.headers.put(VERSIONS_LOCATION, containerName);
+      return this;
+   }
+
+   public static class Builder {
+
+      /**
+       * @see UpdateContainerOptions#anybodyRead
+       */
+      public static UpdateContainerOptions anybodyRead() {
+         UpdateContainerOptions options = new UpdateContainerOptions();
+         return options.anybodyRead();
+      }
+
+      /**
+       * @see UpdateContainerOptions#headers
+       */
+      public static UpdateContainerOptions headers(Multimap<String, String> headers) {
+         UpdateContainerOptions options = new UpdateContainerOptions();
+         return options.headers(headers);
+      }
+
+      /**
+       * @see UpdateContainerOptions#metadata
+       */
+      public static UpdateContainerOptions metadata(Map<String, String> metadata) {
+         UpdateContainerOptions options = new UpdateContainerOptions();
+         return options.metadata(metadata);
+      }
+
+      /**
+       * @see UpdateContainerOptions#versionsLocation
+       */
+      public static UpdateContainerOptions versionsLocation(String containerName) {
+         UpdateContainerOptions options = new UpdateContainerOptions();
+         return options.versionsLocation(containerName);
+      }
+   }
+
+   private static final BindMetadataToHeaders bindMetadataToHeaders = new BindMetadataToHeaders(CONTAINER_METADATA_PREFIX);
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/8505539a/apis/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/reference/SwiftHeaders.java
----------------------------------------------------------------------
diff --git a/apis/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/reference/SwiftHeaders.java b/apis/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/reference/SwiftHeaders.java
new file mode 100644
index 0000000..caab5e7
--- /dev/null
+++ b/apis/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/reference/SwiftHeaders.java
@@ -0,0 +1,92 @@
+/*
+ * 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.reference;
+
+/**
+ * Common headers in Swift.
+ */
+public final class SwiftHeaders {
+
+   // Common Metadata Prefixes
+   public static final String ACCOUNT_METADATA_PREFIX = "X-Account-Meta-";
+   public static final String CONTAINER_METADATA_PREFIX = "X-Container-Meta-";
+   public static final String OBJECT_METADATA_PREFIX = "X-Object-Meta-";
+   public static final String USER_METADATA_PREFIX = OBJECT_METADATA_PREFIX;
+   
+   // Metadata Removal Prefixes
+   public static final String ACCOUNT_REMOVE_METADATA_PREFIX = "X-Remove-Account-Meta-";
+   public static final String CONTAINER_REMOVE_METADATA_PREFIX = "X-Remove-Container-Meta-";
+   public static final String OBJECT_REMOVE_METADATA_PREFIX = "X-Remove-Object-Meta-";
+   
+   // TempURL
+   public static final String ACCOUNT_TEMPORARY_URL_KEY = ACCOUNT_METADATA_PREFIX + "Temp-Url-Key";
+   public static final String ACCOUNT_TEMPORARY_URL_KEY_2 = ACCOUNT_TEMPORARY_URL_KEY + "-2";
+
+   // Account Headers
+   public static final String ACCOUNT_BYTES_USED = "X-Account-Bytes-Used";
+   public static final String ACCOUNT_CONTAINER_COUNT = "X-Account-Container-Count";
+   public static final String ACCOUNT_OBJECT_COUNT = "X-Account-Object-Count";
+
+   // Container Headers
+   public static final String CONTAINER_BYTES_USED = "X-Container-Bytes-Used";
+   public static final String CONTAINER_OBJECT_COUNT = "X-Container-Object-Count";
+
+   // Public access - not supported in all Swift Impls
+   public static final String CONTAINER_READ = "X-Container-Read";
+   public static final String CONTAINER_WRITE = "X-Container-Write";
+   public static final String CONTAINER_ACL_ANYBODY_READ = ".r:*,.rlistings";
+   
+   // CORS
+   public static final String CONTAINER_ACCESS_CONTROL_ALLOW_ORIGIN = CONTAINER_METADATA_PREFIX + "Access-Control-Allow-Origin";
+   public static final String CONTAINER_ACCESS_CONTROL_MAX_AGE = CONTAINER_METADATA_PREFIX + "Access-Control-Max-Age";
+   public static final String CONTAINER_ACCESS_CONTROL_EXPOSE_HEADERS = CONTAINER_METADATA_PREFIX + "Access-Control-Expose-Headers";
+
+   // Container Quota
+   public static final String CONTAINER_QUOTA_BYTES = CONTAINER_METADATA_PREFIX + "Quota-Bytes";
+   public static final String CONTAINER_QUOTA_COUNT = CONTAINER_METADATA_PREFIX + "Quota-Count";
+
+   // Container Sync
+   public static final String CONTAINER_SYNC_KEY = "X-Container-Sync-Key";
+   public static final String CONTAINER_SYNC_TO = "X-Container-Sync-To";
+
+   // Versioning
+   public static final String VERSIONS_LOCATION = "X-Versions-Location";
+
+   // Misc functionality
+   public static final String CONTAINER_WEB_MODE = "X-Web-Mode";
+
+   public static final String OBJECT_COPY_FROM = "X-Copy-From";
+   public static final String OBJECT_DELETE_AFTER = "X-Delete-After";
+   public static final String OBJECT_DELETE_AT = "X-Delete-At";
+   public static final String OBJECT_MANIFEST = "X-Object-Manifest";
+   /** Get the newest version of the object for GET and HEAD requests */
+   public static final String OBJECT_NEWEST = "X-Newest";
+
+   // Static Large Object
+   public static final String STATIC_LARGE_OBJECT = "X-Static-Large-Object";
+
+   // Static Web
+   public static final String STATIC_WEB_INDEX = CONTAINER_METADATA_PREFIX + "Web-Index";
+   public static final String STATIC_WEB_DIRECTORY_TYPE = CONTAINER_METADATA_PREFIX + "Web-Directory-Type";
+   public static final String STATIC_WEB_ERROR = CONTAINER_METADATA_PREFIX + "Web-Error";
+   public static final String STATIC_WEB_LISTINGS = CONTAINER_METADATA_PREFIX + "Web-Listings";
+   public static final String STATIC_WEB_LISTINGS_CSS = CONTAINER_METADATA_PREFIX + "Web-Listings-CSS";
+
+   private SwiftHeaders() {
+      throw new AssertionError("intentionally unimplemented");
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/8505539a/apis/openstack-swift/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata
----------------------------------------------------------------------
diff --git a/apis/openstack-swift/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata b/apis/openstack-swift/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata
new file mode 100644
index 0000000..c5b8017
--- /dev/null
+++ b/apis/openstack-swift/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata
@@ -0,0 +1,18 @@
+#
+# 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.
+#
+
+org.jclouds.openstack.swift.v1.SwiftApiMetadata

http://git-wip-us.apache.org/repos/asf/jclouds/blob/8505539a/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/AuthenticationMockTest.java
----------------------------------------------------------------------
diff --git a/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/AuthenticationMockTest.java b/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/AuthenticationMockTest.java
new file mode 100644
index 0000000..8b5e895
--- /dev/null
+++ b/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/AuthenticationMockTest.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;
+
+import static com.google.common.base.Charsets.UTF_8;
+import static org.jclouds.openstack.swift.v1.features.AccountApiMockTest.accountResponse;
+import static org.testng.Assert.assertEquals;
+
+import java.util.Properties;
+
+import org.jclouds.openstack.v2_0.internal.BaseOpenStackMockTest;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import com.squareup.okhttp.mockwebserver.MockResponse;
+import com.squareup.okhttp.mockwebserver.MockWebServer;
+import com.squareup.okhttp.mockwebserver.RecordedRequest;
+
+/**
+ * @see KeystoneProperties#CREDENTIAL_TYPE
+ */
+@Test
+public class AuthenticationMockTest extends BaseOpenStackMockTest<SwiftApi> {
+
+   @DataProvider(name = "jclouds.keystone.credential-type")
+   Object[][] credentialTypeToPostBody() {
+      Object[][] credentialTypeToPostBody = new Object[2][2];
+      credentialTypeToPostBody[0][0] = "apiAccessKeyCredentials";
+      credentialTypeToPostBody[0][1] = "{\"auth\":{\"apiAccessKeyCredentials\":{\"accessKey\":\"joe\",\"secretKey\":\"letmein\"},\"tenantName\":\"jclouds\"}}";
+      credentialTypeToPostBody[1][0] = "passwordCredentials";
+      credentialTypeToPostBody[1][1] = "{\"auth\":{\"passwordCredentials\":{\"username\":\"joe\",\"password\":\"letmein\"},\"tenantName\":\"jclouds\"}}";
+      return credentialTypeToPostBody;
+   }
+
+   @Test(dataProvider = "jclouds.keystone.credential-type")
+   public void authenticateCredentialType(String credentialType, String expectedPost) throws Exception {
+      MockWebServer server = mockOpenStackServer();
+      server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
+      server.enqueue(addCommonHeaders(accountResponse()));
+
+      try {
+         Properties overrides = new Properties();
+         overrides.setProperty("jclouds.keystone.credential-type", credentialType);
+
+         SwiftApi api = api(server.getUrl("/").toString(), "openstack-swift", overrides);
+
+         api.getAccountApi("DFW").get();
+
+         assertEquals(server.getRequestCount(), 2);
+         RecordedRequest authRequest = server.takeRequest();
+         assertEquals(authRequest.getRequestLine(), "POST /tokens HTTP/1.1");
+         assertEquals(new String(authRequest.getBody(), UTF_8), expectedPost);
+      } finally {
+         server.shutdown();
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/8505539a/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/SwiftApiMetadataTest.java
----------------------------------------------------------------------
diff --git a/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/SwiftApiMetadataTest.java b/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/SwiftApiMetadataTest.java
new file mode 100644
index 0000000..6fbe4d9
--- /dev/null
+++ b/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/SwiftApiMetadataTest.java
@@ -0,0 +1,32 @@
+/*
+ * 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;
+
+import org.jclouds.View;
+import org.jclouds.apis.internal.BaseApiMetadataTest;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.reflect.TypeToken;
+
+@Test(groups = "unit", testName = "SwiftApiMetadataTest")
+// public class SwiftApiMetadataTest extends BaseBlobStoreApiMetadataTest {
+public class SwiftApiMetadataTest extends BaseApiMetadataTest {
+   public SwiftApiMetadataTest() {
+      super(new SwiftApiMetadata(), ImmutableSet.<TypeToken<? extends View>> of());
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/8505539a/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/SwiftErrorHandlerTest.java
----------------------------------------------------------------------
diff --git a/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/SwiftErrorHandlerTest.java b/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/SwiftErrorHandlerTest.java
new file mode 100644
index 0000000..703da9b
--- /dev/null
+++ b/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/SwiftErrorHandlerTest.java
@@ -0,0 +1,117 @@
+/*
+ * 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;
+
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.reportMatcher;
+import static org.easymock.EasyMock.verify;
+import static org.jclouds.io.Payloads.newByteSourcePayload;
+
+import java.net.URI;
+
+import org.easymock.IArgumentMatcher;
+import org.jclouds.blobstore.ContainerNotFoundException;
+import org.jclouds.blobstore.KeyNotFoundException;
+import org.jclouds.http.HttpCommand;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.openstack.swift.v1.handlers.SwiftErrorHandler;
+import org.testng.annotations.Test;
+
+import com.google.common.io.ByteSource;
+
+/**
+ * Tests the {@link SwiftErrorHandler}
+ */
+@Test(groups = "unit", testName = "SwiftErrorHandlerTest")
+public class SwiftErrorHandlerTest {
+
+   @Test
+   public void test404SetsKeyNotFoundExceptionMosso() {
+      assertCodeMakes("HEAD", URI
+               .create("http://host/v1/MossoCloudFS_7064cdb1d49d4dcba3c899ac33e8409d/adriancole-blobstore1/key"), 404,
+               "Not Found", "", KeyNotFoundException.class);
+   }
+
+   @Test
+   public void test404SetsKeyNotFoundExceptionSwift() {
+      assertCodeMakes("HEAD", URI
+               .create("http://67.202.39.175:8080/v1/AUTH_7064cdb1d49d4dcba3c899ac33e8409d/adriancole-blobstore1/key"),
+               404, "Not Found", "", KeyNotFoundException.class);
+   }
+
+   @Test
+   public void test404SetsContainerNotFoundExceptionMosso() {
+      assertCodeMakes("HEAD", URI
+               .create("http://host/v1/MossoCloudFS_7064cdb1d49d4dcba3c899ac33e8409d/adriancole-blobstore1"), 404,
+               "Not Found", "", ContainerNotFoundException.class);
+   }
+
+   @Test
+   public void test404SetsContainerNotFoundExceptionSwift() {
+      assertCodeMakes("HEAD", URI
+               .create("http://67.202.39.175:8080/v1/AUTH_7064cdb1d49d4dcba3c899ac33e8409d/adriancole-blobstore1"),
+               404, "Not Found", "", ContainerNotFoundException.class);
+   }
+
+   private void assertCodeMakes(String method, URI uri, int statusCode, String message, String content,
+            Class<? extends Exception> expected) {
+      assertCodeMakes(method, uri, statusCode, message, "text/plain", content, expected);
+   }
+
+   private void assertCodeMakes(String method, URI uri, int statusCode, String message, String contentType,
+            String content, Class<? extends Exception> expected) {
+
+      SwiftErrorHandler function = new SwiftErrorHandler();
+
+      HttpCommand command = createMock(HttpCommand.class);
+      HttpRequest request = HttpRequest.builder().method(method).endpoint(uri).build();
+      HttpResponse response = HttpResponse.builder().statusCode(statusCode).message(message)
+                                 .payload(newByteSourcePayload(ByteSource.wrap(content.getBytes()))).build();
+      response.getPayload().getContentMetadata().setContentType(contentType);
+
+      expect(command.getCurrentRequest()).andReturn(request).atLeastOnce();
+      command.setException(classEq(expected));
+
+      replay(command);
+
+      function.handleError(command, response);
+
+      verify(command);
+   }
+
+   public static Exception classEq(final Class<? extends Exception> in) {
+      reportMatcher(new IArgumentMatcher() {
+
+         @Override
+         public void appendTo(StringBuffer buffer) {
+            buffer.append("classEq(");
+            buffer.append(in);
+            buffer.append(")");
+         }
+
+         @Override
+         public boolean matches(Object arg) {
+            return arg.getClass() == in;
+         }
+
+      });
+      return null;
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/8505539a/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/TemporaryUrlSignerLiveTest.java
----------------------------------------------------------------------
diff --git a/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/TemporaryUrlSignerLiveTest.java b/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/TemporaryUrlSignerLiveTest.java
new file mode 100644
index 0000000..dd65210
--- /dev/null
+++ b/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/TemporaryUrlSignerLiveTest.java
@@ -0,0 +1,90 @@
+/*
+ * 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;
+
+import static java.lang.String.format;
+import static org.jclouds.io.Payloads.newByteSourcePayload;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.fail;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.util.UUID;
+
+import org.jclouds.openstack.swift.v1.domain.SwiftObject;
+import org.jclouds.openstack.swift.v1.internal.BaseSwiftApiLiveTest;
+import org.jclouds.util.Strings2;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import com.google.common.io.ByteSource;
+
+@Test(groups = "live", testName = "TemporaryUrlSignerLiveTest")
+public class TemporaryUrlSignerLiveTest extends BaseSwiftApiLiveTest<SwiftApi> {
+
+   private String name = getClass().getSimpleName();
+   private String containerName = getClass().getSimpleName() + "Container";
+
+   public void signForPublicAccess() throws Exception {
+      for (String regionId : api.getConfiguredRegions()) {
+         SwiftObject object = api.getObjectApi(regionId, containerName).get(name);
+
+         long expires = System.currentTimeMillis() / 1000 + 5;
+         String signature = TemporaryUrlSigner.checkApiEvery(api.getAccountApi(regionId), 5)
+               .sign("GET", object.getUri().getPath(), expires);
+
+         URI signed = URI.create(format("%s?temp_url_sig=%s&temp_url_expires=%s", object.getUri(), signature, expires));
+
+         InputStream publicStream = signed.toURL().openStream();
+         assertEquals(Strings2.toStringAndClose(publicStream), "swifty");
+
+         // let it expire
+         Thread.sleep(5000);
+         try {
+            signed.toURL().openStream();
+            fail("should have expired!");
+         } catch (IOException e) {
+         }
+      }
+   }
+
+   @Override
+   @BeforeClass(groups = "live")
+   public void setup() {
+      super.setup();
+      String key = UUID.randomUUID().toString();
+      for (String regionId : api.getConfiguredRegions()) {
+         api.getAccountApi(regionId).updateTemporaryUrlKey(key);
+         api.getContainerApi(regionId).create(containerName);
+         api.getObjectApi(regionId, containerName)
+               .put(name, newByteSourcePayload(ByteSource.wrap("swifty".getBytes())));
+      }
+   }
+
+   @AfterMethod
+   @AfterClass(groups = "live")
+   public void tearDown() {
+      for (String regionId : api.getConfiguredRegions()) {
+         api.getObjectApi(regionId, containerName).delete(name);
+         api.getContainerApi(regionId).deleteIfEmpty(containerName);
+      }
+      super.tearDown();
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/8505539a/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/TemporaryUrlSignerMockTest.java
----------------------------------------------------------------------
diff --git a/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/TemporaryUrlSignerMockTest.java b/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/TemporaryUrlSignerMockTest.java
new file mode 100644
index 0000000..8f42cf3
--- /dev/null
+++ b/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/TemporaryUrlSignerMockTest.java
@@ -0,0 +1,76 @@
+/*
+ * 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;
+
+import static org.jclouds.openstack.swift.v1.features.AccountApiMockTest.accountResponse;
+import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.ACCOUNT_TEMPORARY_URL_KEY;
+import static org.testng.Assert.assertEquals;
+
+import org.jclouds.openstack.v2_0.internal.BaseOpenStackMockTest;
+import org.testng.annotations.Test;
+
+import com.squareup.okhttp.mockwebserver.MockResponse;
+import com.squareup.okhttp.mockwebserver.MockWebServer;
+
+@Test(groups = "unit", testName = "TemporaryUrlSignerMockTest")
+public class TemporaryUrlSignerMockTest extends BaseOpenStackMockTest<SwiftApi> {
+
+   @Test(expectedExceptions = NullPointerException.class, expectedExceptionsMessageRegExp = "accountApi")
+   public void whenAccountApiIsNull() {
+      TemporaryUrlSigner.checkApiEvery(null, 10000);
+   }
+
+   public void whenAccountApiHasKey() throws Exception {
+      MockWebServer server = mockOpenStackServer();
+      server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
+      server.enqueue(addCommonHeaders(accountResponse().addHeader(ACCOUNT_TEMPORARY_URL_KEY, "mykey")));
+
+      try {
+         SwiftApi api = api(server.getUrl("/").toString(), "openstack-swift");
+         String signature = TemporaryUrlSigner.checkApiEvery(api.getAccountApi("DFW"), 10000)
+               .sign("GET", "/v1/AUTH_account/container/object", 1323479485l);
+
+         assertEquals(signature, "d9fc2067e52b06598421664cf6610bfc8fc431f6");
+
+         assertEquals(server.getRequestCount(), 2);
+         assertEquals(server.takeRequest().getRequestLine(), "POST /tokens HTTP/1.1");
+         assertEquals(server.takeRequest().getRequestLine(),
+               "HEAD /v1/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9 HTTP/1.1");
+      } finally {
+         server.shutdown();
+      }
+   }
+
+   @Test(expectedExceptions = IllegalStateException.class, expectedExceptionsMessageRegExp = ".*returned a null temporaryUrlKey!")
+   public void whenAccountApiDoesntHaveKey() throws Exception {
+      MockWebServer server = mockOpenStackServer();
+      server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
+      server.enqueue(addCommonHeaders(accountResponse()));
+
+      try {
+         SwiftApi api = api(server.getUrl("/").toString(), "openstack-swift");
+         TemporaryUrlSigner.checkApiEvery(api.getAccountApi("DFW"), 10000)
+            .sign("GET", "/v1/AUTH_account/container/object", 1323479485l);
+      } finally {
+         assertEquals(server.getRequestCount(), 2);
+         assertEquals(server.takeRequest().getRequestLine(), "POST /tokens HTTP/1.1");
+         assertEquals(server.takeRequest().getRequestLine(),
+               "HEAD /v1/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9 HTTP/1.1");
+         server.shutdown();
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/8505539a/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/blobstore/RegionScopedBlobStoreContextLiveTest.java
----------------------------------------------------------------------
diff --git a/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/blobstore/RegionScopedBlobStoreContextLiveTest.java b/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/blobstore/RegionScopedBlobStoreContextLiveTest.java
new file mode 100644
index 0000000..97f5448
--- /dev/null
+++ b/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/blobstore/RegionScopedBlobStoreContextLiveTest.java
@@ -0,0 +1,159 @@
+/*
+ * 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 org.jclouds.openstack.keystone.v2_0.config.KeystoneProperties.CREDENTIAL_TYPE;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.fail;
+
+import java.io.IOException;
+import java.util.Properties;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.ExecutionException;
+
+import org.jclouds.blobstore.BlobRequestSigner;
+import org.jclouds.blobstore.BlobStore;
+import org.jclouds.blobstore.domain.Blob;
+import org.jclouds.blobstore.domain.PageSet;
+import org.jclouds.blobstore.domain.StorageMetadata;
+import org.jclouds.blobstore.integration.internal.BaseBlobStoreIntegrationTest;
+import org.jclouds.domain.Location;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.io.ByteStreams2;
+import org.jclouds.io.MutableContentMetadata;
+import org.jclouds.io.Payload;
+import org.jclouds.io.Payloads;
+import org.jclouds.rest.HttpClient;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.Iterables;
+import com.google.common.io.ByteSource;
+import com.google.common.hash.Hashing;
+import com.google.common.net.MediaType;
+import com.google.common.net.HttpHeaders;
+
+@Test(groups = "live")
+public class RegionScopedBlobStoreContextLiveTest extends BaseBlobStoreIntegrationTest {
+
+   public RegionScopedBlobStoreContextLiveTest() {
+      provider = "openstack-swift";
+   }
+
+   @Override
+   protected Properties setupProperties() {
+      Properties props = super.setupProperties();
+      setIfTestSystemPropertyPresent(props, CREDENTIAL_TYPE);
+      return props;
+   }
+
+   @Test
+   public void testRegionsAreNotEmpty() {
+      assertFalse(RegionScopedBlobStoreContext.class.cast(view).getConfiguredRegions().isEmpty());
+   }
+
+   @Test
+   public void testLocationsMatch() {
+      RegionScopedBlobStoreContext ctx = RegionScopedBlobStoreContext.class.cast(view);
+      for (String regionId : ctx.getConfiguredRegions()) {
+         Set<? extends Location> locations = ctx.getBlobStore(regionId).listAssignableLocations();
+         assertEquals(locations.size(), 1, "expected one region " + regionId + " " + locations);
+         Location location = locations.iterator().next();
+         assertEquals(location.getId(), regionId, "region id " + regionId + " didn't match getId(): " + location);
+      }
+   }
+
+   @Test
+   public void testListBlobs() throws InterruptedException, ExecutionException {
+      RegionScopedBlobStoreContext ctx = RegionScopedBlobStoreContext.class.cast(view);
+      for (String regionId : ctx.getConfiguredRegions()) {
+         ctx.getBlobStore(regionId).list();
+      }
+   }
+
+   @Test
+   public void testSign() throws InterruptedException, ExecutionException,
+         IOException {
+      RegionScopedBlobStoreContext ctx = RegionScopedBlobStoreContext.class.cast(view);
+      for (String regionId : ctx.getConfiguredRegions()) {
+         BlobStore region = ctx.getBlobStore(regionId);
+         PageSet<? extends StorageMetadata> containers = region.list();
+         if (containers.isEmpty()) {
+            continue;
+         }
+         String containerName = Iterables.getLast(containers).getName();
+
+         final ByteSource input = ByteSource.wrap("str".getBytes());
+         final HttpClient client = ctx.utils().http();
+
+         // test signed put
+         String blobName = "test-" + UUID.randomUUID();
+         Blob blob2 = region.blobBuilder(blobName).forSigning()
+               .contentLength(input.size())
+               .contentMD5(input.hash(Hashing.md5()).asBytes())
+               .contentType(MediaType.OCTET_STREAM.toString()).build();
+         BlobRequestSigner signer = ctx.getSigner(regionId);
+         HttpResponse response;
+         try {
+            HttpRequest putRequest;
+            putRequest = signer.signPutBlob(containerName, blob2, 600);
+            MutableContentMetadata metadata = blob2.getMetadata()
+                  .getContentMetadata();
+            HttpRequest.Builder<?> putRequestBuilder = putRequest.toBuilder()
+                  .addHeader(HttpHeaders.CONTENT_TYPE,
+                        metadata.getContentType());
+            putRequestBuilder.addHeader(HttpHeaders.CONTENT_LENGTH,
+                  String.valueOf(input.size()));
+            putRequestBuilder.payload(input);
+            putRequest = putRequestBuilder.build();
+            Payload payload = Payloads.newPayload(input.read());
+            putRequest.setPayload(payload);
+            assertNotNull(putRequest, "regionId=" + regionId + ", container="
+                  + containerName + ", blob=" + blobName);
+            response = client.invoke(putRequest);
+            if (response.getStatusCode() != 200
+                  && response.getStatusCode() != 201) {
+               fail("Signed PUT expected to return 200 or 201 but returned "
+                     + response.getStatusCode());
+            }
+         } catch (Exception e) {
+            fail("Failed signed put test: " + e);
+         }
+
+         // test signed get
+         try {
+            HttpRequest getRequest = signer.signGetBlob(containerName,
+                  blobName);
+            assertNotNull(getRequest, "regionId=" + regionId + ", container="
+                  + containerName + ", blob=" + blobName);
+            response = client.invoke(getRequest);
+            if (response.getStatusCode() != 200) {
+               fail("Signed GET expected to return 200 but returned "
+                     + response.getStatusCode());
+            }
+            Payload payload = response.getPayload();
+            assertEquals(ByteStreams2.toByteArrayAndClose(payload.openStream()), input.read(),
+                  "Data with signed GET not identical to what was put");
+         } catch (Exception e) {
+            fail("Failed signed GET test: " + e);
+         }
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/8505539a/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/blobstore/integration/SwiftBlobIntegrationLiveTest.java
----------------------------------------------------------------------
diff --git a/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/blobstore/integration/SwiftBlobIntegrationLiveTest.java b/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/blobstore/integration/SwiftBlobIntegrationLiveTest.java
new file mode 100644
index 0000000..6d26100
--- /dev/null
+++ b/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/blobstore/integration/SwiftBlobIntegrationLiveTest.java
@@ -0,0 +1,76 @@
+/*
+ * 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", testName = "SwiftBlobIntegrationLiveTest")
+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/jclouds/blob/8505539a/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/blobstore/integration/SwiftBlobLiveTest.java
----------------------------------------------------------------------
diff --git a/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/blobstore/integration/SwiftBlobLiveTest.java b/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/blobstore/integration/SwiftBlobLiveTest.java
new file mode 100644
index 0000000..d9996bf
--- /dev/null
+++ b/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/blobstore/integration/SwiftBlobLiveTest.java
@@ -0,0 +1,39 @@
+/*
+ * 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", testName = "SwiftBlobLiveTest")
+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/jclouds/blob/8505539a/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/blobstore/integration/SwiftBlobSignerLiveTest.java
----------------------------------------------------------------------
diff --git a/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/blobstore/integration/SwiftBlobSignerLiveTest.java b/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/blobstore/integration/SwiftBlobSignerLiveTest.java
new file mode 100644
index 0000000..9dd603f
--- /dev/null
+++ b/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/blobstore/integration/SwiftBlobSignerLiveTest.java
@@ -0,0 +1,39 @@
+/*
+ * 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", testName = "SwiftBlobSignerLiveTest")
+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/jclouds/blob/8505539a/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/blobstore/integration/SwiftContainerIntegrationLiveTest.java
----------------------------------------------------------------------
diff --git a/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/blobstore/integration/SwiftContainerIntegrationLiveTest.java b/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/blobstore/integration/SwiftContainerIntegrationLiveTest.java
new file mode 100644
index 0000000..d954867
--- /dev/null
+++ b/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/blobstore/integration/SwiftContainerIntegrationLiveTest.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.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", testName = "SwiftContainerIntegrationLiveTest")
+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/jclouds/blob/8505539a/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/blobstore/integration/SwiftContainerLiveTest.java
----------------------------------------------------------------------
diff --git a/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/blobstore/integration/SwiftContainerLiveTest.java b/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/blobstore/integration/SwiftContainerLiveTest.java
new file mode 100644
index 0000000..9bd85d6
--- /dev/null
+++ b/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/blobstore/integration/SwiftContainerLiveTest.java
@@ -0,0 +1,39 @@
+/*
+ * 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", testName = "SwiftContainerLiveTest")
+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/jclouds/blob/8505539a/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/blobstore/integration/SwiftServiceIntegrationLiveTest.java
----------------------------------------------------------------------
diff --git a/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/blobstore/integration/SwiftServiceIntegrationLiveTest.java b/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/blobstore/integration/SwiftServiceIntegrationLiveTest.java
new file mode 100644
index 0000000..1da1a68
--- /dev/null
+++ b/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/blobstore/integration/SwiftServiceIntegrationLiveTest.java
@@ -0,0 +1,39 @@
+/*
+ * 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", testName = "SwiftServiceIntegrationLiveTest")
+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/jclouds/blob/8505539a/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/config/SwiftTypeAdaptersTest.java
----------------------------------------------------------------------
diff --git a/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/config/SwiftTypeAdaptersTest.java b/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/config/SwiftTypeAdaptersTest.java
new file mode 100644
index 0000000..e4225fc
--- /dev/null
+++ b/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/config/SwiftTypeAdaptersTest.java
@@ -0,0 +1,90 @@
+/*
+ * 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.config;
+
+import static org.testng.Assert.assertEquals;
+
+import org.jclouds.openstack.swift.v1.config.SwiftTypeAdapters.BulkDeleteResponseAdapter;
+import org.jclouds.openstack.swift.v1.config.SwiftTypeAdapters.ExtractArchiveResponseAdapter;
+import org.jclouds.openstack.swift.v1.domain.BulkDeleteResponse;
+import org.jclouds.openstack.swift.v1.domain.ExtractArchiveResponse;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+@Test
+public class SwiftTypeAdaptersTest {
+   Gson gson = new GsonBuilder()
+         .registerTypeAdapter(ExtractArchiveResponse.class, new ExtractArchiveResponseAdapter())
+         .registerTypeAdapter(BulkDeleteResponse.class, new BulkDeleteResponseAdapter())
+         .create();
+
+   public void extractArchiveWithoutErrors() {
+      assertEquals(gson.fromJson(""
+            + "{\n"
+            + "  \"Response Status\": \"201 Created\",\n"
+            + "  \"Response Body\": \"\",\n"
+            + "  \"Errors\": [],\n"
+            + "  \"Number Files Created\": 10\n"
+            + "}", ExtractArchiveResponse.class), ExtractArchiveResponse.create(10, ImmutableMap.<String, String> of()));
+   }
+
+   public void extractArchiveWithErrorsAndDecodesPaths() {
+      assertEquals(
+            gson.fromJson(""
+                  + "{\n"
+                  + "  \"Response Status\": \"201 Created\",\n"
+                  + "  \"Response Body\": \"\",\n"
+                  + "  \"Errors\": [\n"
+                  + "    [\"/v1/12345678912345/mycontainer/home/xx%3Cyy\", \"400 Bad Request\"],\n"
+                  + "    [\"/v1/12345678912345/mycontainer/../image.gif\", \"400 Bad Request\"]\n"
+                  + "  ],\n"
+                  + "  \"Number Files Created\": 8\n"
+                  + "}", ExtractArchiveResponse.class),
+            ExtractArchiveResponse.create(
+                  8,
+                  ImmutableMap.<String, String> builder()
+                        .put("/v1/12345678912345/mycontainer/home/xx<yy", "400 Bad Request")
+                        .put("/v1/12345678912345/mycontainer/../image.gif", "400 Bad Request").build()));
+   }
+
+   public void bulkDeleteWithoutErrors() {
+      assertEquals(gson.fromJson(""
+            + "{\n"
+            + "  \"Response Status\": \"200 OK\",\n"
+            + "  \"Response Body\": \"\",\n"
+            + "  \"Errors\": [],\n"
+            + "  \"Number Not Found\": 1,\n"
+            + "  \"Number Deleted\": 9\n"
+            + "}", BulkDeleteResponse.class), BulkDeleteResponse.create(9, 1, ImmutableMap.<String, String> of()));
+   }
+
+   public void bulkDeleteWithErrorsAndDecodesPaths() {
+      assertEquals(gson.fromJson(""
+            + "{\n"
+            + "  \"Response Status\": \"400 Bad Request\",\n"
+            + "  \"Response Body\": \"\",\n"
+            + "  \"Errors\": [\n"
+            + "    [\"/v1/12345678912345/Not%20Empty\", \"409 Conflict\"]"
+            + "  ],\n"
+            + "  \"Number Deleted\": 0\n"
+            + "}", BulkDeleteResponse.class),
+            BulkDeleteResponse.create(0, 0, ImmutableMap.of("/v1/12345678912345/Not Empty", "409 Conflict")));
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/8505539a/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/AccountApiLiveTest.java
----------------------------------------------------------------------
diff --git a/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/AccountApiLiveTest.java b/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/AccountApiLiveTest.java
new file mode 100644
index 0000000..3841b33
--- /dev/null
+++ b/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/AccountApiLiveTest.java
@@ -0,0 +1,87 @@
+/*
+ * 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.features;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.jclouds.openstack.swift.v1.SwiftApi;
+import org.jclouds.openstack.swift.v1.domain.Account;
+import org.jclouds.openstack.swift.v1.internal.BaseSwiftApiLiveTest;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableMap;
+
+@Test(groups = "live", testName = "AccountApiLiveTest")
+public class AccountApiLiveTest extends BaseSwiftApiLiveTest<SwiftApi> {
+
+   public void testGet() throws Exception {
+      for (String regionId : regions) {
+         AccountApi accountApi = api.getAccountApi(regionId);
+         Account account = accountApi.get();
+
+         assertNotNull(account);
+         assertTrue(account.getContainerCount() >= 0);
+         assertTrue(account.getObjectCount() >= 0);
+         assertTrue(account.getBytesUsed() >= 0);
+      }
+   }
+
+   public void testUpdateMetadata() throws Exception {
+      for (String regionId : regions) {
+         AccountApi accountApi = api.getAccountApi(regionId);
+
+         Map<String, String> meta = ImmutableMap.of("MyAdd1", "foo", "MyAdd2", "bar");
+
+         assertTrue(accountApi.updateMetadata(meta));
+
+         accountHasMetadata(accountApi, meta);
+      }
+   }
+
+   public void testDeleteMetadata() throws Exception {
+      for (String regionId : regions) {
+         AccountApi accountApi = api.getAccountApi(regionId);
+
+         Map<String, String> meta = ImmutableMap.of("MyDelete1", "foo", "MyDelete2", "bar");
+
+         assertTrue(accountApi.updateMetadata(meta));
+         accountHasMetadata(accountApi, meta);
+
+         assertTrue(accountApi.deleteMetadata(meta));
+         Account account = accountApi.get();
+         for (Entry<String, String> entry : meta.entrySet()) {
+            // note keys are returned in lower-case!
+            assertFalse(account.getMetadata().containsKey(entry.getKey().toLowerCase()));
+         }
+      }
+   }
+
+   static void accountHasMetadata(AccountApi accountApi, Map<String, String> meta) {
+      Account account = accountApi.get();
+      for (Entry<String, String> entry : meta.entrySet()) {
+         // note keys are returned in lower-case!
+         assertEquals(account.getMetadata().get(entry.getKey().toLowerCase()), entry.getValue(),
+               account + " didn't have metadata: " + entry);
+      }
+   }
+}