Merge pull request #827 from everett-toews/swift-copy-object-squashed-4

Squashed commits of my previous swift-copy-object pull request
This commit is contained in:
Adrian Cole 2012-09-08 14:35:25 -07:00
commit b0a12bf303
22 changed files with 829 additions and 81 deletions

View File

@ -96,6 +96,7 @@ public class CloudFilesClientLiveTest extends CommonSwiftClientLiveTest<CloudFil
Set<ContainerCDNMetadata> cdnMetadataList = getApi().listCDNContainers(); Set<ContainerCDNMetadata> cdnMetadataList = getApi().listCDNContainers();
assertTrue(cdnMetadataList.size() >= 1); assertTrue(cdnMetadataList.size() >= 1);
cdnMetadata = getApi().getCDNMetadata(containerNameWithCDN);
final long initialTTL = cdnMetadata.getTTL(); final long initialTTL = cdnMetadata.getTTL();
assertTrue(cdnMetadataList.contains(new ContainerCDNMetadata(containerNameWithCDN, true, initialTTL, cdnUri))); assertTrue(cdnMetadataList.contains(new ContainerCDNMetadata(containerNameWithCDN, true, initialTTL, cdnUri)));

View File

@ -35,10 +35,13 @@ import org.jclouds.blobstore.binders.BindMapToHeadersWithPrefix;
import org.jclouds.blobstore.domain.PageSet; import org.jclouds.blobstore.domain.PageSet;
import org.jclouds.blobstore.functions.ReturnFalseOnContainerNotFound; import org.jclouds.blobstore.functions.ReturnFalseOnContainerNotFound;
import org.jclouds.blobstore.functions.ReturnFalseOnKeyNotFound; import org.jclouds.blobstore.functions.ReturnFalseOnKeyNotFound;
import org.jclouds.blobstore.functions.ReturnNullOnContainerNotFound;
import org.jclouds.blobstore.functions.ReturnNullOnKeyNotFound; import org.jclouds.blobstore.functions.ReturnNullOnKeyNotFound;
import org.jclouds.http.functions.ParseETagHeader; import org.jclouds.http.functions.ParseETagHeader;
import org.jclouds.http.options.GetOptions; import org.jclouds.http.options.GetOptions;
import org.jclouds.openstack.filters.AuthenticateRequest; import org.jclouds.openstack.filters.AuthenticateRequest;
import org.jclouds.openstack.swift.binders.BindIterableToHeadersWithContainerDeleteMetadataPrefix;
import org.jclouds.openstack.swift.binders.BindMapToHeadersWithContainerMetadataPrefix;
import org.jclouds.openstack.swift.binders.BindSwiftObjectMetadataToRequest; import org.jclouds.openstack.swift.binders.BindSwiftObjectMetadataToRequest;
import org.jclouds.openstack.swift.domain.AccountMetadata; import org.jclouds.openstack.swift.domain.AccountMetadata;
import org.jclouds.openstack.swift.domain.ContainerMetadata; import org.jclouds.openstack.swift.domain.ContainerMetadata;
@ -47,14 +50,26 @@ import org.jclouds.openstack.swift.domain.ObjectInfo;
import org.jclouds.openstack.swift.domain.SwiftObject; import org.jclouds.openstack.swift.domain.SwiftObject;
import org.jclouds.openstack.swift.functions.ObjectName; import org.jclouds.openstack.swift.functions.ObjectName;
import org.jclouds.openstack.swift.functions.ParseAccountMetadataResponseFromHeaders; import org.jclouds.openstack.swift.functions.ParseAccountMetadataResponseFromHeaders;
import org.jclouds.openstack.swift.functions.ParseContainerMetadataFromHeaders;
import org.jclouds.openstack.swift.functions.ParseObjectFromHeadersAndHttpContent; import org.jclouds.openstack.swift.functions.ParseObjectFromHeadersAndHttpContent;
import org.jclouds.openstack.swift.functions.ParseObjectInfoFromHeaders; import org.jclouds.openstack.swift.functions.ParseObjectInfoFromHeaders;
import org.jclouds.openstack.swift.functions.ParseObjectInfoListFromJsonResponse; import org.jclouds.openstack.swift.functions.ParseObjectInfoListFromJsonResponse;
import org.jclouds.openstack.swift.functions.ReturnTrueOn404FalseOn409; import org.jclouds.openstack.swift.functions.ReturnTrueOn404FalseOn409;
import org.jclouds.openstack.swift.options.CreateContainerOptions;
import org.jclouds.openstack.swift.options.ListContainerOptions; import org.jclouds.openstack.swift.options.ListContainerOptions;
import org.jclouds.rest.annotations.*; import org.jclouds.openstack.swift.reference.SwiftHeaders;
import org.jclouds.rest.annotations.BinderParam;
import org.jclouds.rest.annotations.Endpoint;
import org.jclouds.rest.annotations.ExceptionParser;
import org.jclouds.rest.annotations.Headers;
import org.jclouds.rest.annotations.ParamParser;
import org.jclouds.rest.annotations.QueryParams;
import org.jclouds.rest.annotations.RequestFilters;
import org.jclouds.rest.annotations.ResponseParser;
import org.jclouds.rest.annotations.SkipEncoding;
import org.jclouds.rest.functions.ReturnVoidOnNotFoundOr404; import org.jclouds.rest.functions.ReturnVoidOnNotFoundOr404;
import com.google.common.annotations.Beta;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import com.google.inject.Provides; import com.google.inject.Provides;
@ -89,13 +104,50 @@ public interface CommonSwiftAsyncClient {
@Path("/") @Path("/")
ListenableFuture<? extends Set<ContainerMetadata>> listContainers(ListContainerOptions... options); ListenableFuture<? extends Set<ContainerMetadata>> listContainers(ListContainerOptions... options);
/**
* @see CommonSwiftClient#getContainerMetadata
*/
@Beta
@HEAD
@ResponseParser(ParseContainerMetadataFromHeaders.class)
@ExceptionParser(ReturnNullOnContainerNotFound.class)
@Path("/{container}")
ListenableFuture<ContainerMetadata> getContainerMetadata(@PathParam("container") String container);
/**
* @see CommonSwiftClient#setContainerMetadata
*/
@POST
@Path("/{container}")
@ExceptionParser(ReturnFalseOnContainerNotFound.class)
ListenableFuture<Boolean> setContainerMetadata(@PathParam("container") String container,
@BinderParam(BindMapToHeadersWithContainerMetadataPrefix.class) Map<String, String> containerMetadata);
/**
* @see CommonSwiftClient#deleteContainerMetadata
*/
@POST
@Path("/{container}")
@ExceptionParser(ReturnFalseOnContainerNotFound.class)
ListenableFuture<Boolean> deleteContainerMetadata(@PathParam("container") String container,
@BinderParam(BindIterableToHeadersWithContainerDeleteMetadataPrefix.class) Iterable<String> metadataKeys);
/**
* @see CommonSwiftClient#createContainer
*/
@PUT
@Path("/{container}")
ListenableFuture<Boolean> createContainer(@PathParam("container") String container,
CreateContainerOptions... options);
/** /**
* @see CommonSwiftClient#setObjectInfo * @see CommonSwiftClient#setObjectInfo
*/ */
@POST @POST
@Path("/{container}/{name}") @Path("/{container}/{name}")
ListenableFuture<Boolean> setObjectInfo(@PathParam("container") String container, @PathParam("name") String name, ListenableFuture<Boolean> setObjectInfo(@PathParam("container") String container,
@BinderParam(BindMapToHeadersWithPrefix.class) Map<String, String> userMetadata); @PathParam("name") String name,
@BinderParam(BindMapToHeadersWithPrefix.class) Map<String, String> userMetadata);
/** /**
* @see CommonSwiftClient#createContainer * @see CommonSwiftClient#createContainer
@ -120,7 +172,7 @@ public interface CommonSwiftAsyncClient {
@ResponseParser(ParseObjectInfoListFromJsonResponse.class) @ResponseParser(ParseObjectInfoListFromJsonResponse.class)
@Path("/{container}") @Path("/{container}")
ListenableFuture<PageSet<ObjectInfo>> listObjects(@PathParam("container") String container, ListenableFuture<PageSet<ObjectInfo>> listObjects(@PathParam("container") String container,
ListContainerOptions... options); ListContainerOptions... options);
/** /**
* @see CommonSwiftClient#containerExists * @see CommonSwiftClient#containerExists
@ -136,9 +188,20 @@ public interface CommonSwiftAsyncClient {
@PUT @PUT
@Path("/{container}/{name}") @Path("/{container}/{name}")
@ResponseParser(ParseETagHeader.class) @ResponseParser(ParseETagHeader.class)
ListenableFuture<String> putObject( ListenableFuture<String> putObject(@PathParam("container") String container,
@PathParam("container") String container, @PathParam("name") @ParamParser(ObjectName.class) @BinderParam(BindSwiftObjectMetadataToRequest.class) SwiftObject object);
@PathParam("name") @ParamParser(ObjectName.class) @BinderParam(BindSwiftObjectMetadataToRequest.class) SwiftObject object);
/**
* @see CommonSwiftClient#copyObject
*/
@PUT
@Path("/{destinationContainer}/{destinationObject}")
@Headers(keys = SwiftHeaders.OBJECT_COPY_FROM, values = "/{sourceContainer}/{sourceObject}")
@ExceptionParser(ReturnFalseOnContainerNotFound.class)
ListenableFuture<Boolean> copyObject(@PathParam("sourceContainer") String sourceContainer,
@PathParam("sourceObject") String sourceObject,
@PathParam("destinationContainer") String destinationContainer,
@PathParam("destinationObject") String destinationObject);
/** /**
* @see CommonSwiftClient#getObject * @see CommonSwiftClient#getObject
@ -147,8 +210,9 @@ public interface CommonSwiftAsyncClient {
@ResponseParser(ParseObjectFromHeadersAndHttpContent.class) @ResponseParser(ParseObjectFromHeadersAndHttpContent.class)
@ExceptionParser(ReturnNullOnKeyNotFound.class) @ExceptionParser(ReturnNullOnKeyNotFound.class)
@Path("/{container}/{name}") @Path("/{container}/{name}")
ListenableFuture<SwiftObject> getObject(@PathParam("container") String container, @PathParam("name") String name, ListenableFuture<SwiftObject> getObject(@PathParam("container") String container,
GetOptions... options); @PathParam("name") String name,
GetOptions... options);
/** /**
* @see CommonSwiftClient#getObjectInfo * @see CommonSwiftClient#getObjectInfo
@ -158,7 +222,7 @@ public interface CommonSwiftAsyncClient {
@ExceptionParser(ReturnNullOnKeyNotFound.class) @ExceptionParser(ReturnNullOnKeyNotFound.class)
@Path("/{container}/{name}") @Path("/{container}/{name}")
ListenableFuture<MutableObjectInfoWithMetadata> getObjectInfo(@PathParam("container") String container, ListenableFuture<MutableObjectInfoWithMetadata> getObjectInfo(@PathParam("container") String container,
@PathParam("name") String name); @PathParam("name") String name);
/** /**
* @see CommonSwiftClient#objectExists * @see CommonSwiftClient#objectExists
@ -166,7 +230,8 @@ public interface CommonSwiftAsyncClient {
@HEAD @HEAD
@ExceptionParser(ReturnFalseOnKeyNotFound.class) @ExceptionParser(ReturnFalseOnKeyNotFound.class)
@Path("/{container}/{name}") @Path("/{container}/{name}")
ListenableFuture<Boolean> objectExists(@PathParam("container") String container, @PathParam("name") String name); ListenableFuture<Boolean> objectExists(@PathParam("container") String container,
@PathParam("name") String name);
/** /**
* @see CommonSwiftClient#removeObject * @see CommonSwiftClient#removeObject
@ -174,13 +239,14 @@ public interface CommonSwiftAsyncClient {
@DELETE @DELETE
@ExceptionParser(ReturnVoidOnNotFoundOr404.class) @ExceptionParser(ReturnVoidOnNotFoundOr404.class)
@Path("/{container}/{name}") @Path("/{container}/{name}")
ListenableFuture<Void> removeObject(@PathParam("container") String container, @PathParam("name") String name); ListenableFuture<Void> removeObject(@PathParam("container") String container,
@PathParam("name") String name);
@PUT @PUT
@Path("/{container}/{name}") @Path("/{container}/{name}")
@ResponseParser(ParseETagHeader.class) @ResponseParser(ParseETagHeader.class)
@Headers(keys = "X-Object-Manifest", values="{container}/{name}") @Headers(keys = "X-Object-Manifest", values="{container}/{name}")
ListenableFuture<String> putObjectManifest(@PathParam("container") String container, ListenableFuture<String> putObjectManifest(@PathParam("container") String container,
@PathParam("name") String name); @PathParam("name") String name);
} }

View File

@ -31,6 +31,7 @@ import org.jclouds.openstack.swift.domain.ContainerMetadata;
import org.jclouds.openstack.swift.domain.MutableObjectInfoWithMetadata; import org.jclouds.openstack.swift.domain.MutableObjectInfoWithMetadata;
import org.jclouds.openstack.swift.domain.ObjectInfo; import org.jclouds.openstack.swift.domain.ObjectInfo;
import org.jclouds.openstack.swift.domain.SwiftObject; import org.jclouds.openstack.swift.domain.SwiftObject;
import org.jclouds.openstack.swift.options.CreateContainerOptions;
import org.jclouds.openstack.swift.options.ListContainerOptions; import org.jclouds.openstack.swift.options.ListContainerOptions;
import com.google.inject.Provides; import com.google.inject.Provides;
@ -87,24 +88,38 @@ public interface CommonSwiftClient {
*/ */
Set<ContainerMetadata> listContainers(ListContainerOptions... options); Set<ContainerMetadata> listContainers(ListContainerOptions... options);
boolean setObjectInfo(String container, String name, Map<String, String> userMetadata); ContainerMetadata getContainerMetadata(String container);
boolean setContainerMetadata(String container, Map<String, String> containerMetadata);
boolean deleteContainerMetadata(String container, Iterable<String> metadataKeys);
boolean createContainer(String container); boolean createContainer(String container);
boolean createContainer(String container, CreateContainerOptions... options);
boolean deleteContainerIfEmpty(String container); boolean deleteContainerIfEmpty(String container);
PageSet<ObjectInfo> listObjects(String container, ListContainerOptions... options);
boolean containerExists(String container); boolean containerExists(String container);
@Timeout(duration = 5 * 1024 * 1024 / 128, timeUnit = TimeUnit.SECONDS) PageSet<ObjectInfo> listObjects(String container, ListContainerOptions... options);
String putObject(String container, SwiftObject object);
@Timeout(duration = 5 * 1024 * 1024 / 512, timeUnit = TimeUnit.SECONDS) @Timeout(duration = 5 * 1024 * 1024 / 512, timeUnit = TimeUnit.SECONDS)
SwiftObject getObject(String container, String name, GetOptions... options); SwiftObject getObject(String container, String name, GetOptions... options);
boolean setObjectInfo(String container, String name, Map<String, String> userMetadata);
MutableObjectInfoWithMetadata getObjectInfo(String container, String name); MutableObjectInfoWithMetadata getObjectInfo(String container, String name);
@Timeout(duration = 5 * 1024 * 1024 / 128, timeUnit = TimeUnit.SECONDS)
String putObject(String container, SwiftObject object);
/**
* @return True If the object was copied
* @throws CopyObjectException If the object was not copied
*/
boolean copyObject(String sourceContainer, String sourceObject, String destinationContainer, String destinationObject);
void removeObject(String container, String name); void removeObject(String container, String name);
/** /**

View File

@ -0,0 +1,59 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jclouds.openstack.swift;
import org.jclouds.rest.ResourceNotFoundException;
/**
* Thrown when an object cannot be copied.
*
* @author Everett Toews
*/
public class CopyObjectException extends ResourceNotFoundException {
private String sourcePath;
private String destinationPath;
public CopyObjectException() {
super();
}
public CopyObjectException(String sourcePath, String destinationPath, String message) {
super(String.format("Either the source path %s or the destination path %s was not found. " +
"(message: %s)", sourcePath, destinationPath, message));
this.sourcePath = sourcePath;
this.destinationPath = destinationPath;
}
public CopyObjectException(Exception from) {
super(from);
}
public String getSourcePath() {
return sourcePath;
}
public String getDestinationPath() {
return destinationPath;
}
/** The serialVersionUID */
private static final long serialVersionUID = -2272965726680721281L;
}

View File

@ -0,0 +1,72 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jclouds.openstack.swift.binders;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import javax.inject.Singleton;
import org.jclouds.http.HttpRequest;
import org.jclouds.openstack.swift.reference.SwiftHeaders;
import org.jclouds.rest.Binder;
import com.google.common.base.Function;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Iterables;
/**
* @author Everett Toews
*/
@Singleton
public class BindIterableToHeadersWithContainerDeleteMetadataPrefix implements Binder {
private final Function<String, String> FN;
public BindIterableToHeadersWithContainerDeleteMetadataPrefix() {
FN = new Function<String, String>() {
@Override
public String apply(String element) {
String inLowercase = element.toLowerCase();
return (inLowercase.startsWith(SwiftHeaders.CONTAINER_DELETE_METADATA_PREFIX)) ? inLowercase : SwiftHeaders.CONTAINER_DELETE_METADATA_PREFIX + inLowercase;
}
@Override
public String toString() {
return "prefix: " + SwiftHeaders.CONTAINER_DELETE_METADATA_PREFIX;
}
};
}
@SuppressWarnings("unchecked")
@Override
public <R extends HttpRequest> R bindToRequest(R request, Object input) {
checkArgument(checkNotNull(input, "input") instanceof Iterable<?>, "this binder is only valid for Iterable!");
checkNotNull(request, "request");
Iterable<String> metadataKeys = Iterables.transform((Iterable<String>) input, FN);
HashMultimap<String, String> headers = HashMultimap.create();
for (String metadataKey: metadataKeys) {
headers.put(metadataKey, "");
}
return (R) request.toBuilder().replaceHeaders(headers).build();
}
}

View File

@ -0,0 +1,11 @@
package org.jclouds.openstack.swift.binders;
import org.jclouds.blobstore.binders.BindMapToHeadersWithPrefix;
import org.jclouds.openstack.swift.reference.SwiftHeaders;
public class BindMapToHeadersWithContainerMetadataPrefix extends BindMapToHeadersWithPrefix {
public BindMapToHeadersWithContainerMetadataPrefix() {
super(SwiftHeaders.CONTAINER_METADATA_PREFIX);
}
}

View File

@ -16,7 +16,7 @@
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
package org.jclouds.hpcloud.objectstorage.functions; package org.jclouds.openstack.swift.functions;
import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkArgument;

View File

@ -32,6 +32,8 @@ import org.jclouds.http.HttpErrorHandler;
import org.jclouds.http.HttpResponse; import org.jclouds.http.HttpResponse;
import org.jclouds.http.HttpResponseException; import org.jclouds.http.HttpResponseException;
import org.jclouds.logging.Logger; import org.jclouds.logging.Logger;
import org.jclouds.openstack.swift.CopyObjectException;
import org.jclouds.openstack.swift.reference.SwiftHeaders;
import org.jclouds.rest.AuthorizationException; import org.jclouds.rest.AuthorizationException;
/** /**
@ -61,10 +63,21 @@ public class ParseSwiftErrorFromHttpResponse implements HttpErrorHandler {
exception = new AuthorizationException(exception.getMessage(), exception); exception = new AuthorizationException(exception.getMessage(), exception);
break; break;
case 404: case 404:
if (!command.getCurrentRequest().getMethod().equals("DELETE")) { String sourcePath = command.getCurrentRequest().getFirstHeaderOrNull(SwiftHeaders.OBJECT_COPY_FROM);
Exception oldException = exception;
if (sourcePath != null) {
String path = command.getCurrentRequest().getEndpoint().getPath();
int startOfDestinationPath = path.lastIndexOf("/", path.lastIndexOf("/")-1);
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(); String path = command.getCurrentRequest().getEndpoint().getPath();
Matcher matcher = CONTAINER_PATH.matcher(path); Matcher matcher = CONTAINER_PATH.matcher(path);
Exception oldException = exception;
if (matcher.find()) { if (matcher.find()) {
exception = new ContainerNotFoundException(matcher.group(1), message); exception = new ContainerNotFoundException(matcher.group(1), message);
exception.initCause(oldException); exception.initCause(oldException);

View File

@ -16,7 +16,7 @@
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
package org.jclouds.hpcloud.objectstorage.options; package org.jclouds.openstack.swift.options;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
@ -61,17 +61,11 @@ public class CreateContainerOptions extends BaseHttpRequestOptions {
public static class Builder { public static class Builder {
/**
* @see org.jclouds.hpcloud.objectstorage.options.CreateContainerOptions#withPublicAccess
*/
public static CreateContainerOptions withPublicAccess() { public static CreateContainerOptions withPublicAccess() {
CreateContainerOptions options = new CreateContainerOptions(); CreateContainerOptions options = new CreateContainerOptions();
return options.withPublicAccess(); return options.withPublicAccess();
} }
/**
* @see org.jclouds.hpcloud.objectstorage.options.CreateContainerOptions#withMetadata(Multimap<String, String>)
*/
public static CreateContainerOptions withMetadata(Map<String, String> metadata) { public static CreateContainerOptions withMetadata(Map<String, String> metadata) {
CreateContainerOptions options = new CreateContainerOptions(); CreateContainerOptions options = new CreateContainerOptions();
return (CreateContainerOptions) options.withMetadata(metadata); return (CreateContainerOptions) options.withMetadata(metadata);

View File

@ -29,7 +29,9 @@ public interface SwiftHeaders {
public static final String CONTAINER_BYTES_USED = "X-Container-Bytes-Used"; public static final String CONTAINER_BYTES_USED = "X-Container-Bytes-Used";
public static final String CONTAINER_OBJECT_COUNT = "X-Container-Object-Count"; public static final String CONTAINER_OBJECT_COUNT = "X-Container-Object-Count";
public static final String CONTAINER_METADATA_PREFIX = "X-Container-Meta-"; public static final String CONTAINER_METADATA_PREFIX = "X-Container-Meta-";
public static final String CONTAINER_DELETE_METADATA_PREFIX = "X-Remove-Container-Meta-";
public static final String USER_METADATA_PREFIX = "X-Object-Meta-"; public static final String USER_METADATA_PREFIX = "X-Object-Meta-";
public static final String OBJECT_COPY_FROM = "X-Copy-From";
public static final String CONTAINER_READ = "X-Container-Read"; public static final String CONTAINER_READ = "X-Container-Read";
public static final String CONTAINER_WRITE = "X-Container-Write"; public static final String CONTAINER_WRITE = "X-Container-Write";

View File

@ -20,8 +20,10 @@ package org.jclouds.openstack.swift;
import static org.jclouds.openstack.swift.options.ListContainerOptions.Builder.underPath; import static org.jclouds.openstack.swift.options.ListContainerOptions.Builder.underPath;
import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertNotNull; import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue; import static org.testng.Assert.assertTrue;
import static org.testng.Assert.fail;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
@ -274,6 +276,64 @@ public abstract class CommonSwiftClientLiveTest<C extends CommonSwiftClient> ext
} }
} }
@Test
public void testCopyObjectOperations() throws Exception {
String sourceContainer = getContainerName();
String sourceObject = "original.txt";
String sourcePath = "/" + sourceContainer + "/" + sourceObject;
String badSource = "badsource";
String destinationContainer = getContainerName();
String destinationObject = "copy.txt";
String destinationPath = "/" + destinationContainer + "/" + destinationObject;
String badDestination = "baddestination";
String data = "Hello World";
SwiftObject sourceSwiftObject = newSwiftObject(data, sourceObject);
getApi().putObject(sourceContainer, sourceSwiftObject);
// test that not giving a destination name *doesn't* copy source name to the destination container with
// the source name but copy still returns success :(
assertTrue(getApi().copyObject(sourceContainer, sourceObject, destinationContainer, ""));
assertTrue(!getApi().objectExists(destinationContainer, sourceObject));
// test copy works
assertTrue(getApi().copyObject(sourceContainer, sourceObject, destinationContainer, destinationObject));
assertTrue(getApi().objectExists(destinationContainer, destinationObject));
SwiftObject destinationSwiftObject = getApi().getObject(destinationContainer, destinationObject);
assertEquals(Strings2.toString(destinationSwiftObject.getPayload()), data);
// test exception thrown on bad destination container
try {
assertFalse(getApi().copyObject(sourceContainer, sourceObject, badDestination, destinationObject));
fail("Expected CopyObjectException");
}
catch (CopyObjectException e) {
assertEquals(e.getSourcePath(), sourcePath);
assertEquals(e.getDestinationPath(), "/" + badDestination + "/" + destinationObject);
}
// test exception thrown on bad source container
try {
assertFalse(getApi().copyObject(badSource, sourceObject, destinationContainer, destinationObject));
fail("Expected CopyObjectException");
}
catch (CopyObjectException e) {
assertEquals(e.getSourcePath(), "/" + badSource + "/" + sourceObject);
assertEquals(e.getDestinationPath(), destinationPath);
}
// test exception thrown on bad source name
try {
assertFalse(getApi().copyObject(sourceContainer, badSource, destinationContainer, destinationObject));
fail("Expected CopyObjectException");
}
catch (CopyObjectException e) {
assertEquals(e.getSourcePath(), "/" + sourceContainer + "/" + badSource);
assertEquals(e.getDestinationPath(), destinationPath);
}
}
protected void testGetObjectContentType(SwiftObject getBlob) { protected void testGetObjectContentType(SwiftObject getBlob) {
String contentType = getBlob.getPayload().getContentMetadata().getContentType(); String contentType = getBlob.getPayload().getContentMetadata().getContentType();
assert contentType.startsWith("text/plain") || "application/x-www-form-urlencoded".equals(contentType): contentType; assert contentType.startsWith("text/plain") || "application/x-www-form-urlencoded".equals(contentType): contentType;

View File

@ -23,9 +23,14 @@ import static org.testng.Assert.assertTrue;
import org.jclouds.http.HttpRequest; import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse; import org.jclouds.http.HttpResponse;
import org.jclouds.http.HttpResponseException;
import org.jclouds.openstack.swift.internal.BaseSwiftExpectTest; import org.jclouds.openstack.swift.internal.BaseSwiftExpectTest;
import org.jclouds.openstack.swift.reference.SwiftHeaders;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
/** /**
* *
* @author Adrian Cole * @author Adrian Cole
@ -33,7 +38,8 @@ import org.testng.annotations.Test;
@Test(testName = "SwiftClientExpectTest") @Test(testName = "SwiftClientExpectTest")
public class SwiftClientExpectTest extends BaseSwiftExpectTest<SwiftClient> { public class SwiftClientExpectTest extends BaseSwiftExpectTest<SwiftClient> {
public void testContainerExistsWhenResponseIs2xxReturnsTrue() throws Exception { @Test
public void testContainerExistsWhenResponseIs2xxReturnsTrue() {
HttpRequest headContainer = HttpRequest.builder() HttpRequest headContainer = HttpRequest.builder()
.method("HEAD") .method("HEAD")
.endpoint(swiftEndpointWithHostReplaced + "/foo") .endpoint(swiftEndpointWithHostReplaced + "/foo")
@ -47,7 +53,8 @@ public class SwiftClientExpectTest extends BaseSwiftExpectTest<SwiftClient> {
assertTrue(clientWhenContainerExists.containerExists("foo")); assertTrue(clientWhenContainerExists.containerExists("foo"));
} }
public void testContainerExistsWhenResponseIs404ReturnsFalse() throws Exception { @Test
public void testContainerExistsWhenResponseIs404ReturnsFalse() {
HttpRequest headContainer = HttpRequest.builder() HttpRequest headContainer = HttpRequest.builder()
.method("HEAD") .method("HEAD")
.endpoint(swiftEndpointWithHostReplaced + "/foo") .endpoint(swiftEndpointWithHostReplaced + "/foo")
@ -59,7 +66,150 @@ public class SwiftClientExpectTest extends BaseSwiftExpectTest<SwiftClient> {
authResponse, headContainer, headContainerResponse); authResponse, headContainer, headContainerResponse);
assertFalse(clientWhenContainerDoesntExist.containerExists("foo")); assertFalse(clientWhenContainerDoesntExist.containerExists("foo"));
} }
@Test
public void testSetContainerMetadataWhenResponseIs2xxReturnsTrue() {
HttpRequest setContainerMetadataRequest = HttpRequest.builder()
.method("POST")
.endpoint(swiftEndpointWithHostReplaced + "/foo")
.addHeader(SwiftHeaders.CONTAINER_METADATA_PREFIX + "key", "value")
.addHeader("X-Auth-Token", authToken).build();
HttpResponse setContainerMetadataResponse = HttpResponse.builder().statusCode(204).build();
SwiftClient clientSetContainerMetadata = requestsSendResponses(authRequest,
authResponse, setContainerMetadataRequest, setContainerMetadataResponse);
assertTrue(clientSetContainerMetadata.setContainerMetadata("foo", ImmutableMap.<String, String> of("key", "value")));
}
@Test(expectedExceptions = HttpResponseException.class)
public void testSetContainerMetadataWhenResponseIs400ThrowsException() {
HttpRequest setContainerMetadataRequest = HttpRequest.builder()
.method("POST")
.endpoint(swiftEndpointWithHostReplaced + "/foo")
.addHeader(SwiftHeaders.CONTAINER_METADATA_PREFIX, "value")
.addHeader("X-Auth-Token", authToken).build();
HttpResponse setContainerMetadataResponse = HttpResponse.builder()
.statusCode(400)
.message("Metadata name cannot be empty").build();
SwiftClient clientSetContainerMetadata = requestsSendResponses(authRequest,
authResponse, setContainerMetadataRequest, setContainerMetadataResponse);
clientSetContainerMetadata.setContainerMetadata("foo", ImmutableMap.<String, String> of("", "value"));
}
@Test
public void testSetContainerMetadataWhenResponseIs404ReturnsFalse() {
HttpRequest setContainerMetadataRequest = HttpRequest.builder()
.method("POST")
.endpoint(swiftEndpointWithHostReplaced + "/foo")
.addHeader(SwiftHeaders.CONTAINER_METADATA_PREFIX + "key", "value")
.addHeader("X-Auth-Token", authToken).build();
HttpResponse setContainerMetadataResponse = HttpResponse.builder()
.statusCode(404).build();
SwiftClient clientSetContainerMetadata = requestsSendResponses(authRequest,
authResponse, setContainerMetadataRequest, setContainerMetadataResponse);
assertFalse(clientSetContainerMetadata.setContainerMetadata("foo", ImmutableMap.<String, String> of("key", "value")));
}
@Test
public void testDeleteContainerMetadataWhenResponseIs2xxReturnsTrue() {
HttpRequest deleteContainerMetadataRequest = HttpRequest.builder()
.method("POST")
.endpoint(swiftEndpointWithHostReplaced + "/foo")
.addHeader(SwiftHeaders.CONTAINER_DELETE_METADATA_PREFIX + "bar", "")
.addHeader("X-Auth-Token", authToken).build();
HttpResponse deleteContainerMetadataResponse = HttpResponse.builder().statusCode(204).build();
SwiftClient clientDeleteContainerMetadata = requestsSendResponses(authRequest,
authResponse, deleteContainerMetadataRequest, deleteContainerMetadataResponse);
assertTrue(clientDeleteContainerMetadata.deleteContainerMetadata("foo", ImmutableList.<String> of("bar")));
}
@Test
public void testDeleteContainerMetadataEmptyWhenResponseIs2xxReturnsTrue() {
HttpRequest deleteContainerMetadataRequest = HttpRequest.builder()
.method("POST")
.endpoint(swiftEndpointWithHostReplaced + "/foo")
.addHeader(SwiftHeaders.CONTAINER_DELETE_METADATA_PREFIX, "")
.addHeader("X-Auth-Token", authToken).build();
HttpResponse deleteContainerMetadataResponse = HttpResponse.builder().statusCode(204).build();
SwiftClient clientDeleteContainerMetadata = requestsSendResponses(authRequest,
authResponse, deleteContainerMetadataRequest, deleteContainerMetadataResponse);
assertTrue(clientDeleteContainerMetadata.deleteContainerMetadata("foo", ImmutableList.<String> of("")));
}
@Test
public void testDeleteContainerMetadataWhenResponseIs404ReturnsFalse() {
HttpRequest deleteContainerMetadataRequest = HttpRequest.builder()
.method("POST")
.endpoint(swiftEndpointWithHostReplaced + "/foo")
.addHeader(SwiftHeaders.CONTAINER_DELETE_METADATA_PREFIX + "bar", "")
.addHeader("X-Auth-Token", authToken).build();
HttpResponse deleteContainerMetadataResponse = HttpResponse.builder().statusCode(404).build();
SwiftClient clientDeleteContainerMetadata = requestsSendResponses(authRequest,
authResponse, deleteContainerMetadataRequest, deleteContainerMetadataResponse);
assertFalse(clientDeleteContainerMetadata.deleteContainerMetadata("foo", ImmutableList.<String> of("bar")));
}
@Test
public void testCopyObjectWhenResponseIs2xxReturnsTrue() {
String sourceContainer = "bar";
String sourceObject = "foo.txt";
String sourcePath = "/" + sourceContainer + "/" + sourceObject;
String destinationContainer = "foo";
String destinationObject = "bar.txt";
String destinationPath = "/" + destinationContainer + "/" + destinationObject;
HttpRequest copyObjectRequest = HttpRequest.builder()
.method("PUT")
.endpoint(swiftEndpointWithHostReplaced + destinationPath)
.addHeader(SwiftHeaders.OBJECT_COPY_FROM, sourcePath)
.addHeader("X-Auth-Token", authToken).build();
HttpResponse copyObjectResponse = HttpResponse.builder().statusCode(201).build();
SwiftClient clientCopyObject = requestsSendResponses(authRequest,
authResponse, copyObjectRequest, copyObjectResponse);
assertTrue(clientCopyObject.copyObject(sourceContainer, sourceObject, destinationContainer, destinationObject));
}
@Test(expectedExceptions = CopyObjectException.class)
public void testCopyObjectWhenResponseIs404ThrowsException() {
String sourceContainer = "bar";
String sourceObject = "foo.txt";
String sourcePath = "/" + sourceContainer + "/" + sourceObject;
String destinationContainer = "foo";
String destinationObject = "bar.txt";
String destinationPath = "/" + destinationContainer + "/" + destinationObject;
HttpRequest copyObjectRequest = HttpRequest.builder()
.method("PUT")
.endpoint(swiftEndpointWithHostReplaced + destinationPath)
.addHeader(SwiftHeaders.OBJECT_COPY_FROM, sourcePath)
.addHeader("X-Auth-Token", authToken).build();
HttpResponse copyObjectResponse = HttpResponse.builder().statusCode(404).build();
SwiftClient clientCopyObject = requestsSendResponses(authRequest,
authResponse, copyObjectRequest, copyObjectResponse);
assertTrue(clientCopyObject.copyObject(sourceContainer, sourceObject, destinationContainer, destinationObject));
}
} }

View File

@ -0,0 +1,77 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jclouds.openstack.swift.binders;
import static org.testng.Assert.assertEquals;
import java.util.List;
import org.jclouds.http.HttpRequest;
import org.jclouds.openstack.swift.CommonSwiftClientTest;
import org.jclouds.openstack.swift.reference.SwiftHeaders;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableList;
/**
* Tests behavior of {@code BindIterableToHeadersWithContainerDeleteMetadataPrefix}
*
* @author Everett Toews
*/
// NOTE:without testName, this will not call @Before* and fail w/NPE during surefire
@Test(groups = "unit", testName = "BindIterableToHeadersWithContainerDeleteMetadataPrefixTest")
public class BindIterableToHeadersWithContainerDeleteMetadataPrefixTest extends CommonSwiftClientTest {
@Test
public void testMetadataKeysBind() {
List<String> metadataKeys = ImmutableList.of("foo", "bar");
HttpRequest request = HttpRequest.builder().method("PUT").endpoint("http://localhost").build();
BindIterableToHeadersWithContainerDeleteMetadataPrefix binder =
injector.getInstance(BindIterableToHeadersWithContainerDeleteMetadataPrefix.class);
HttpRequest actualRequest = binder.bindToRequest(request, metadataKeys);
HttpRequest expectedRequest = HttpRequest.builder()
.method("PUT")
.endpoint("http://localhost")
.addHeader(SwiftHeaders.CONTAINER_DELETE_METADATA_PREFIX + "foo", "")
.addHeader(SwiftHeaders.CONTAINER_DELETE_METADATA_PREFIX + "bar", "")
.build();
assertEquals(actualRequest, expectedRequest);
}
@Test(expectedExceptions = NullPointerException.class)
public void testNullListIsBad() {
HttpRequest request = HttpRequest.builder().method("PUT").endpoint("http://localhost").build();
BindIterableToHeadersWithContainerDeleteMetadataPrefix binder =
injector.getInstance(BindIterableToHeadersWithContainerDeleteMetadataPrefix.class);
binder.bindToRequest(request, null);
}
@Test(expectedExceptions = NullPointerException.class)
public void testNullRequestIsBad() {
List<String> metadataKeys = ImmutableList.of("foo", "bar");
BindIterableToHeadersWithContainerDeleteMetadataPrefix binder =
injector.getInstance(BindIterableToHeadersWithContainerDeleteMetadataPrefix.class);
binder.bindToRequest(null, metadataKeys);
}
}

View File

@ -0,0 +1,77 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jclouds.openstack.swift.binders;
import static org.testng.Assert.assertEquals;
import java.util.List;
import org.jclouds.http.HttpRequest;
import org.jclouds.openstack.swift.CommonSwiftClientTest;
import org.jclouds.openstack.swift.reference.SwiftHeaders;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableList;
/**
* Tests behavior of {@code BindListToHeadersWithContainerDeleteMetadataPrefix}
*
* @author Everett Toews
*/
// NOTE:without testName, this will not call @Before* and fail w/NPE during surefire
@Test(groups = "unit", testName = "BindListToHeadersWithContainerDeleteMetadataPrefixTest")
public class BindMapToHeadersWithContainerMetadataPrefixTest extends CommonSwiftClientTest {
@Test
public void testMetadataKeysBind() {
List<String> metadataKeys = ImmutableList.of("foo", "bar");
HttpRequest request = HttpRequest.builder().method("PUT").endpoint("http://localhost").build();
BindIterableToHeadersWithContainerDeleteMetadataPrefix binder =
injector.getInstance(BindIterableToHeadersWithContainerDeleteMetadataPrefix.class);
HttpRequest actualRequest = binder.bindToRequest(request, metadataKeys);
HttpRequest expectedRequest = HttpRequest.builder()
.method("PUT")
.endpoint("http://localhost")
.addHeader(SwiftHeaders.CONTAINER_DELETE_METADATA_PREFIX + "foo", "")
.addHeader(SwiftHeaders.CONTAINER_DELETE_METADATA_PREFIX + "bar", "")
.build();
assertEquals(actualRequest, expectedRequest);
}
@Test(expectedExceptions = NullPointerException.class)
public void testNullListIsBad() {
HttpRequest request = HttpRequest.builder().method("PUT").endpoint("http://localhost").build();
BindIterableToHeadersWithContainerDeleteMetadataPrefix binder =
injector.getInstance(BindIterableToHeadersWithContainerDeleteMetadataPrefix.class);
binder.bindToRequest(request, null);
}
@Test(expectedExceptions = NullPointerException.class)
public void testNullRequestIsBad() {
List<String> metadataKeys = ImmutableList.of("foo", "bar");
BindIterableToHeadersWithContainerDeleteMetadataPrefix binder =
injector.getInstance(BindIterableToHeadersWithContainerDeleteMetadataPrefix.class);
binder.bindToRequest(null, metadataKeys);
}
}

View File

@ -18,15 +18,28 @@
*/ */
package org.jclouds.openstack.swift.blobstore.integration; package org.jclouds.openstack.swift.blobstore.integration;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
import java.util.Properties; import java.util.Properties;
import org.jclouds.blobstore.BlobStore;
import org.jclouds.blobstore.integration.internal.BaseContainerIntegrationTest; import org.jclouds.blobstore.integration.internal.BaseContainerIntegrationTest;
import org.jclouds.openstack.keystone.v2_0.config.KeystoneProperties; import org.jclouds.openstack.keystone.v2_0.config.KeystoneProperties;
import org.jclouds.openstack.swift.CommonSwiftAsyncClient;
import org.jclouds.openstack.swift.CommonSwiftClient;
import org.jclouds.openstack.swift.domain.ContainerMetadata;
import org.jclouds.openstack.swift.options.CreateContainerOptions;
import org.jclouds.rest.RestContext;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
/** /**
* @author James Murty * @author James Murty
* @author Adrian Cole * @author Adrian Cole
* @author Everett Toews
*/ */
@Test(groups = "live") @Test(groups = "live")
public class SwiftContainerIntegrationLiveTest extends BaseContainerIntegrationTest { public class SwiftContainerIntegrationLiveTest extends BaseContainerIntegrationTest {
@ -40,4 +53,53 @@ public class SwiftContainerIntegrationLiveTest extends BaseContainerIntegrationT
public SwiftContainerIntegrationLiveTest() { public SwiftContainerIntegrationLiveTest() {
provider = System.getProperty("test.swift.provider", "swift"); provider = System.getProperty("test.swift.provider", "swift");
} }
@Test(groups = "live")
public void testSetGetContainerMetadata() throws InterruptedException {
BlobStore blobStore = view.getBlobStore();
RestContext<CommonSwiftClient, CommonSwiftAsyncClient> swift = blobStore.getContext().unwrap();
String containerName = getContainerName();
assertTrue(swift.getApi().createContainer(containerName));
ImmutableMap<String, String> metadata = ImmutableMap.<String, String> of(
"key1", "value1",
"key2", "value2");
assertTrue(swift.getApi().setContainerMetadata(containerName, metadata));
ContainerMetadata containerMetadata = swift.getApi().getContainerMetadata(containerName);
assertEquals(containerMetadata.getMetadata().get("key1"), "value1");
assertEquals(containerMetadata.getMetadata().get("key2"), "value2");
}
@Test(groups = "live")
public void testCreateDeleteContainerMetadata() throws InterruptedException {
BlobStore blobStore = view.getBlobStore();
RestContext<CommonSwiftClient, CommonSwiftAsyncClient> swift = blobStore.getContext().unwrap();
String containerName = getContainerName();
CreateContainerOptions options = CreateContainerOptions.Builder
.withPublicAccess()
.withMetadata(ImmutableMap.<String, String> of(
"key1", "value1",
"key2", "value2",
"key3", "value3"));
assertTrue(swift.getApi().createContainer(containerName, options));
ContainerMetadata containerMetadata = swift.getApi().getContainerMetadata(containerName);
assertEquals(containerMetadata.getMetadata().size(), 3);
assertEquals(containerMetadata.getMetadata().get("key1"), "value1");
assertEquals(containerMetadata.getMetadata().get("key2"), "value2");
assertEquals(containerMetadata.getMetadata().get("key3"), "value3");
assertTrue(swift.getApi().deleteContainerMetadata(containerName, ImmutableList.<String> of("key2","key3")));
containerMetadata = swift.getApi().getContainerMetadata(containerName);
assertEquals(containerMetadata.getMetadata().size(), 1);
assertEquals(containerMetadata.getMetadata().get("key1"), "value1");
}
} }

View File

@ -46,5 +46,4 @@ public class SwiftContainerLiveTest extends BaseContainerLiveTest {
public void testPublicAccess() throws MalformedURLException, InterruptedException, IOException { public void testPublicAccess() throws MalformedURLException, InterruptedException, IOException {
super.testPublicAccess(); super.testPublicAccess();
} }
} }

View File

@ -0,0 +1,77 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jclouds.openstack.swift.functions;
import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.replay;
import static org.testng.Assert.assertEquals;
import java.net.URI;
import org.jclouds.http.HttpResponse;
import org.jclouds.openstack.swift.domain.ContainerMetadata;
import org.jclouds.openstack.swift.functions.ParseContainerMetadataFromHeaders;
import org.jclouds.openstack.swift.reference.SwiftHeaders;
import org.jclouds.rest.internal.GeneratedHttpRequest;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableList;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
/**
* Tests behavior of {@code ParseContainerMetadataFromHeaders}
*
* @author Everett Toews
*/
@Test(groups = "unit")
public class ParseContainerMetadataFromHeadersTest {
Injector i = Guice.createInjector(new AbstractModule() {
@Override
protected void configure() {
}
});
public void testParseContainerMetadataHeaders() {
ParseContainerMetadataFromHeaders parser = i.getInstance(ParseContainerMetadataFromHeaders.class);
GeneratedHttpRequest request = createMock(GeneratedHttpRequest.class);
expect(request.getArgs()).andReturn(ImmutableList.<Object> of("container", "key")).atLeastOnce();
expect(request.getEndpoint()).andReturn(URI.create("http://localhost/test")).atLeastOnce();
replay(request);
parser.setContext(request);
HttpResponse response = HttpResponse.builder().statusCode(204).message("No Content").payload("")
.addHeader(SwiftHeaders.CONTAINER_BYTES_USED, "42")
.addHeader(SwiftHeaders.CONTAINER_OBJECT_COUNT, "1")
.addHeader(SwiftHeaders.CONTAINER_METADATA_PREFIX + "label1", "test1")
.addHeader(SwiftHeaders.CONTAINER_METADATA_PREFIX + "label2", "test2").build();
response.getPayload().getContentMetadata().setContentType("text/plain");
ContainerMetadata containerMetadata = parser.apply(response);
assertEquals(containerMetadata.getBytes(), 42);
assertEquals(containerMetadata.getCount(), 1);
assertEquals(containerMetadata.getMetadata().get("label1"), "test1");
assertEquals(containerMetadata.getMetadata().get("label2"), "test2");
}
}

View File

@ -31,9 +31,14 @@ import org.jclouds.blobstore.ContainerNotFoundException;
import org.jclouds.blobstore.KeyNotFoundException; import org.jclouds.blobstore.KeyNotFoundException;
import org.jclouds.http.HttpCommand; import org.jclouds.http.HttpCommand;
import org.jclouds.http.HttpRequest; import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpRequest.Builder;
import org.jclouds.http.HttpResponse; import org.jclouds.http.HttpResponse;
import org.jclouds.openstack.swift.CopyObjectException;
import org.jclouds.openstack.swift.reference.SwiftHeaders;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import com.google.common.base.Strings;
/** /**
* *
* @author Adrian Cole * @author Adrian Cole
@ -41,6 +46,13 @@ import org.testng.annotations.Test;
@Test(groups = { "unit" }) @Test(groups = { "unit" })
public class ParseSwiftErrorFromHttpResponseTest { public class ParseSwiftErrorFromHttpResponseTest {
@Test
public void test404SetsCopyObjectException() {
assertCodeMakes("HEAD",
URI.create("http://host/v1/MossoCloudFS_7064cdb1d49d4dcba3c899ac33e8409d/adriancole-blobstore1/key"), 404,
"Not Found", "text/plain", "", "/bad/source/path", CopyObjectException.class);
}
@Test @Test
public void test404SetsKeyNotFoundExceptionMosso() { public void test404SetsKeyNotFoundExceptionMosso() {
assertCodeMakes("HEAD", assertCodeMakes("HEAD",
@ -76,24 +88,34 @@ public class ParseSwiftErrorFromHttpResponseTest {
private void assertCodeMakes(String method, URI uri, int statusCode, String message, String contentType, private void assertCodeMakes(String method, URI uri, int statusCode, String message, String contentType,
String content, Class<? extends Exception> expected) { String content, Class<? extends Exception> expected) {
assertCodeMakes(method, uri, statusCode, message, contentType, content, "", expected);
ParseSwiftErrorFromHttpResponse function = new ParseSwiftErrorFromHttpResponse();
HttpCommand command = createMock(HttpCommand.class);
HttpRequest request = HttpRequest.builder().method(method).endpoint(uri).build();
HttpResponse response = HttpResponse.builder().statusCode(statusCode).message(message).payload(content).build();
response.getPayload().getContentMetadata().setContentType(contentType);
expect(command.getCurrentRequest()).andReturn(request).atLeastOnce();
command.setException(classEq(expected));
replay(command);
function.handleError(command, response);
verify(command);
} }
private void assertCodeMakes(String method, URI uri, int statusCode, String message, String contentType,
String content, String copyObjectSourcePath, Class<? extends Exception> expected) {
ParseSwiftErrorFromHttpResponse function = new ParseSwiftErrorFromHttpResponse();
HttpCommand command = createMock(HttpCommand.class);
Builder<?> requestBuilder = HttpRequest.builder().method(method).endpoint(uri);
if (!Strings.isNullOrEmpty(copyObjectSourcePath)) {
requestBuilder.addHeader(SwiftHeaders.OBJECT_COPY_FROM, copyObjectSourcePath);
}
HttpRequest request = requestBuilder.build();
HttpResponse response = HttpResponse.builder().statusCode(statusCode).message(message).payload(content).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) { public static Exception classEq(final Class<? extends Exception> in) {
reportMatcher(new IArgumentMatcher() { reportMatcher(new IArgumentMatcher() {

View File

@ -53,6 +53,7 @@ import org.jclouds.openstack.swift.domain.ContainerMetadata;
import org.jclouds.openstack.swift.domain.MutableObjectInfoWithMetadata; import org.jclouds.openstack.swift.domain.MutableObjectInfoWithMetadata;
import org.jclouds.openstack.swift.domain.ObjectInfo; import org.jclouds.openstack.swift.domain.ObjectInfo;
import org.jclouds.openstack.swift.domain.SwiftObject; import org.jclouds.openstack.swift.domain.SwiftObject;
import org.jclouds.openstack.swift.options.CreateContainerOptions;
import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.common.base.Throwables; import com.google.common.base.Throwables;
@ -160,12 +161,33 @@ public class StubSwiftAsyncClient implements CommonSwiftAsyncClient {
}))); })));
} }
@Override
public ListenableFuture<ContainerMetadata> getContainerMetadata(String container) {
throw new UnsupportedOperationException();
}
public ListenableFuture<Boolean> setContainerMetadata(String container, Map<String, String> containerMetadata) {
throw new UnsupportedOperationException();
}
public ListenableFuture<Boolean> deleteContainerMetadata(String container, Iterable<String> metadataKeys) {
throw new UnsupportedOperationException();
}
public ListenableFuture<Boolean> createContainer(String container, CreateContainerOptions... options) {
throw new UnsupportedOperationException();
}
public ListenableFuture<PageSet<ObjectInfo>> listObjects(String container, public ListenableFuture<PageSet<ObjectInfo>> listObjects(String container,
org.jclouds.openstack.swift.options.ListContainerOptions... optionsList) { org.jclouds.openstack.swift.options.ListContainerOptions... optionsList) {
ListContainerOptions options = container2ContainerListOptions.apply(optionsList); ListContainerOptions options = container2ContainerListOptions.apply(optionsList);
return Futures.compose(blobStore.list(container, options), resource2ObjectList, service); return Futures.compose(blobStore.list(container, options), resource2ObjectList, service);
} }
public ListenableFuture<Boolean> copyObject(String sourceContainer, String sourceObject, String destinationContainer, String destinationObject) {
throw new UnsupportedOperationException();
}
public ListenableFuture<String> putObject(String container, SwiftObject object) { public ListenableFuture<String> putObject(String container, SwiftObject object) {
return blobStore.putBlob(container, object2Blob.apply(object)); return blobStore.putBlob(container, object2Blob.apply(object));
} }
@ -195,5 +217,4 @@ public class StubSwiftAsyncClient implements CommonSwiftAsyncClient {
public ListenableFuture<Boolean> objectExists(String bucketName, String key) { public ListenableFuture<Boolean> objectExists(String bucketName, String key) {
return blobStore.blobExists(bucketName, key); return blobStore.blobExists(bucketName, key);
} }
} }

View File

@ -16,10 +16,11 @@
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
package org.jclouds.hpcloud.objectstorage.options; package org.jclouds.openstack.swift.options;
import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertEquals;
import org.jclouds.openstack.swift.options.CreateContainerOptions;
import org.jclouds.openstack.swift.reference.SwiftHeaders; import org.jclouds.openstack.swift.reference.SwiftHeaders;
import org.testng.annotations.Test; import org.testng.annotations.Test;

View File

@ -23,16 +23,11 @@ import java.util.Set;
import javax.ws.rs.Consumes; import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE; import javax.ws.rs.DELETE;
import javax.ws.rs.GET; import javax.ws.rs.GET;
import javax.ws.rs.HEAD;
import javax.ws.rs.PUT;
import javax.ws.rs.Path; import javax.ws.rs.Path;
import javax.ws.rs.PathParam; import javax.ws.rs.PathParam;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
import org.jclouds.blobstore.functions.ReturnNullOnContainerNotFound;
import org.jclouds.hpcloud.objectstorage.extensions.HPCloudCDNAsyncClient; import org.jclouds.hpcloud.objectstorage.extensions.HPCloudCDNAsyncClient;
import org.jclouds.hpcloud.objectstorage.functions.ParseContainerMetadataFromHeaders;
import org.jclouds.hpcloud.objectstorage.options.CreateContainerOptions;
import org.jclouds.openstack.keystone.v2_0.filters.AuthenticateRequest; import org.jclouds.openstack.keystone.v2_0.filters.AuthenticateRequest;
import org.jclouds.openstack.swift.CommonSwiftAsyncClient; import org.jclouds.openstack.swift.CommonSwiftAsyncClient;
import org.jclouds.openstack.swift.Storage; import org.jclouds.openstack.swift.Storage;
@ -44,10 +39,8 @@ import org.jclouds.rest.annotations.Endpoint;
import org.jclouds.rest.annotations.ExceptionParser; import org.jclouds.rest.annotations.ExceptionParser;
import org.jclouds.rest.annotations.QueryParams; import org.jclouds.rest.annotations.QueryParams;
import org.jclouds.rest.annotations.RequestFilters; import org.jclouds.rest.annotations.RequestFilters;
import org.jclouds.rest.annotations.ResponseParser;
import org.jclouds.rest.annotations.SkipEncoding; import org.jclouds.rest.annotations.SkipEncoding;
import com.google.common.annotations.Beta;
import com.google.common.base.Optional; import com.google.common.base.Optional;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
@ -69,24 +62,6 @@ import com.google.common.util.concurrent.ListenableFuture;
@Endpoint(Storage.class) @Endpoint(Storage.class)
public interface HPCloudObjectStorageAsyncClient extends CommonSwiftAsyncClient { public interface HPCloudObjectStorageAsyncClient extends CommonSwiftAsyncClient {
/**
* @see HPCloudObjectStorageClient#getCDNMetadata(String)
*/
@Beta
@HEAD
@ResponseParser(ParseContainerMetadataFromHeaders.class)
@ExceptionParser(ReturnNullOnContainerNotFound.class)
@Path("/{container}")
ListenableFuture<ContainerMetadata> getContainerMetadata(@PathParam("container") String container);
/**
* @see HPCloudObjectStorageClient#createContainer
*/
@PUT
@Path("/{container}")
ListenableFuture<Boolean> createContainer(@PathParam("container") String container,
CreateContainerOptions... options);
/** /**
* @see org.jclouds.openstack.swift.CommonSwiftClient#listContainers * @see org.jclouds.openstack.swift.CommonSwiftClient#listContainers
*/ */

View File

@ -22,9 +22,7 @@ import java.util.concurrent.TimeUnit;
import org.jclouds.concurrent.Timeout; import org.jclouds.concurrent.Timeout;
import org.jclouds.hpcloud.objectstorage.extensions.HPCloudCDNClient; import org.jclouds.hpcloud.objectstorage.extensions.HPCloudCDNClient;
import org.jclouds.hpcloud.objectstorage.options.CreateContainerOptions;
import org.jclouds.openstack.swift.CommonSwiftClient; import org.jclouds.openstack.swift.CommonSwiftClient;
import org.jclouds.openstack.swift.domain.ContainerMetadata;
import org.jclouds.rest.annotations.Delegate; import org.jclouds.rest.annotations.Delegate;
import com.google.common.base.Optional; import com.google.common.base.Optional;
@ -46,10 +44,6 @@ import com.google.common.util.concurrent.ListenableFuture;
@Timeout(duration = 120, timeUnit = TimeUnit.SECONDS) @Timeout(duration = 120, timeUnit = TimeUnit.SECONDS)
public interface HPCloudObjectStorageClient extends CommonSwiftClient { public interface HPCloudObjectStorageClient extends CommonSwiftClient {
boolean createContainer(String container, CreateContainerOptions... options);
ContainerMetadata getContainerMetadata(String container);
/** /**
* Provides synchronous access to CDN features. * Provides synchronous access to CDN features.
*/ */