mirror of https://github.com/apache/jclouds.git
Moved HP Storage Container Metadata parsing to Swift since it's a standard Swift feature.
Moved HP Storage createContainer(String, CreateContainerOptions) to CommonSwiftClient since it's a standard Swift feature. Added support for setting Swift Container Metadata. Added support for deleting Swift Container Metadata. Added copy object feature to Swift. Removed unnecessary TODO comment. Changed DeleteContainerMetadataOptions to just be a List. Changed CopyObjectOptions to be just be a String. Changed CommonSwiftClient.setContainerMetadata() to use just a Map for metadata. Added ExpectTests. Changed setContainerMetadata() to use Iterable instead of List for more generic type goodness. Changed copyObject() to use 4 String params instead of 2 String params to be similar to other such methods in jclouds.
This commit is contained in:
parent
196b5d7c55
commit
73746588f4
|
@ -96,6 +96,7 @@ public class CloudFilesClientLiveTest extends CommonSwiftClientLiveTest<CloudFil
|
|||
Set<ContainerCDNMetadata> cdnMetadataList = getApi().listCDNContainers();
|
||||
assertTrue(cdnMetadataList.size() >= 1);
|
||||
|
||||
cdnMetadata = getApi().getCDNMetadata(containerNameWithCDN);
|
||||
final long initialTTL = cdnMetadata.getTTL();
|
||||
assertTrue(cdnMetadataList.contains(new ContainerCDNMetadata(containerNameWithCDN, true, initialTTL, cdnUri)));
|
||||
|
||||
|
|
|
@ -35,10 +35,13 @@ import org.jclouds.blobstore.binders.BindMapToHeadersWithPrefix;
|
|||
import org.jclouds.blobstore.domain.PageSet;
|
||||
import org.jclouds.blobstore.functions.ReturnFalseOnContainerNotFound;
|
||||
import org.jclouds.blobstore.functions.ReturnFalseOnKeyNotFound;
|
||||
import org.jclouds.blobstore.functions.ReturnNullOnContainerNotFound;
|
||||
import org.jclouds.blobstore.functions.ReturnNullOnKeyNotFound;
|
||||
import org.jclouds.http.functions.ParseETagHeader;
|
||||
import org.jclouds.http.options.GetOptions;
|
||||
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.domain.AccountMetadata;
|
||||
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.functions.ObjectName;
|
||||
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.ParseObjectInfoFromHeaders;
|
||||
import org.jclouds.openstack.swift.functions.ParseObjectInfoListFromJsonResponse;
|
||||
import org.jclouds.openstack.swift.functions.ReturnTrueOn404FalseOn409;
|
||||
import org.jclouds.openstack.swift.options.CreateContainerOptions;
|
||||
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 com.google.common.annotations.Beta;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import com.google.inject.Provides;
|
||||
|
||||
|
@ -89,13 +104,50 @@ public interface CommonSwiftAsyncClient {
|
|||
@Path("/")
|
||||
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
|
||||
*/
|
||||
@POST
|
||||
@Path("/{container}/{name}")
|
||||
ListenableFuture<Boolean> setObjectInfo(@PathParam("container") String container, @PathParam("name") String name,
|
||||
@BinderParam(BindMapToHeadersWithPrefix.class) Map<String, String> userMetadata);
|
||||
ListenableFuture<Boolean> setObjectInfo(@PathParam("container") String container,
|
||||
@PathParam("name") String name,
|
||||
@BinderParam(BindMapToHeadersWithPrefix.class) Map<String, String> userMetadata);
|
||||
|
||||
/**
|
||||
* @see CommonSwiftClient#createContainer
|
||||
|
@ -120,7 +172,7 @@ public interface CommonSwiftAsyncClient {
|
|||
@ResponseParser(ParseObjectInfoListFromJsonResponse.class)
|
||||
@Path("/{container}")
|
||||
ListenableFuture<PageSet<ObjectInfo>> listObjects(@PathParam("container") String container,
|
||||
ListContainerOptions... options);
|
||||
ListContainerOptions... options);
|
||||
|
||||
/**
|
||||
* @see CommonSwiftClient#containerExists
|
||||
|
@ -136,9 +188,20 @@ public interface CommonSwiftAsyncClient {
|
|||
@PUT
|
||||
@Path("/{container}/{name}")
|
||||
@ResponseParser(ParseETagHeader.class)
|
||||
ListenableFuture<String> putObject(
|
||||
@PathParam("container") String container,
|
||||
@PathParam("name") @ParamParser(ObjectName.class) @BinderParam(BindSwiftObjectMetadataToRequest.class) SwiftObject object);
|
||||
ListenableFuture<String> putObject(@PathParam("container") String container,
|
||||
@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
|
||||
|
@ -147,8 +210,9 @@ public interface CommonSwiftAsyncClient {
|
|||
@ResponseParser(ParseObjectFromHeadersAndHttpContent.class)
|
||||
@ExceptionParser(ReturnNullOnKeyNotFound.class)
|
||||
@Path("/{container}/{name}")
|
||||
ListenableFuture<SwiftObject> getObject(@PathParam("container") String container, @PathParam("name") String name,
|
||||
GetOptions... options);
|
||||
ListenableFuture<SwiftObject> getObject(@PathParam("container") String container,
|
||||
@PathParam("name") String name,
|
||||
GetOptions... options);
|
||||
|
||||
/**
|
||||
* @see CommonSwiftClient#getObjectInfo
|
||||
|
@ -158,7 +222,7 @@ public interface CommonSwiftAsyncClient {
|
|||
@ExceptionParser(ReturnNullOnKeyNotFound.class)
|
||||
@Path("/{container}/{name}")
|
||||
ListenableFuture<MutableObjectInfoWithMetadata> getObjectInfo(@PathParam("container") String container,
|
||||
@PathParam("name") String name);
|
||||
@PathParam("name") String name);
|
||||
|
||||
/**
|
||||
* @see CommonSwiftClient#objectExists
|
||||
|
@ -166,7 +230,8 @@ public interface CommonSwiftAsyncClient {
|
|||
@HEAD
|
||||
@ExceptionParser(ReturnFalseOnKeyNotFound.class)
|
||||
@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
|
||||
|
@ -174,13 +239,14 @@ public interface CommonSwiftAsyncClient {
|
|||
@DELETE
|
||||
@ExceptionParser(ReturnVoidOnNotFoundOr404.class)
|
||||
@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
|
||||
@Path("/{container}/{name}")
|
||||
@ResponseParser(ParseETagHeader.class)
|
||||
@Headers(keys = "X-Object-Manifest", values="{container}/{name}")
|
||||
ListenableFuture<String> putObjectManifest(@PathParam("container") String container,
|
||||
@PathParam("name") String name);
|
||||
@PathParam("name") String name);
|
||||
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ import org.jclouds.openstack.swift.domain.ContainerMetadata;
|
|||
import org.jclouds.openstack.swift.domain.MutableObjectInfoWithMetadata;
|
||||
import org.jclouds.openstack.swift.domain.ObjectInfo;
|
||||
import org.jclouds.openstack.swift.domain.SwiftObject;
|
||||
import org.jclouds.openstack.swift.options.CreateContainerOptions;
|
||||
import org.jclouds.openstack.swift.options.ListContainerOptions;
|
||||
|
||||
import com.google.inject.Provides;
|
||||
|
@ -87,24 +88,38 @@ public interface CommonSwiftClient {
|
|||
*/
|
||||
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, CreateContainerOptions... options);
|
||||
|
||||
boolean deleteContainerIfEmpty(String container);
|
||||
|
||||
PageSet<ObjectInfo> listObjects(String container, ListContainerOptions... options);
|
||||
|
||||
boolean containerExists(String container);
|
||||
|
||||
@Timeout(duration = 5 * 1024 * 1024 / 128, timeUnit = TimeUnit.SECONDS)
|
||||
String putObject(String container, SwiftObject object);
|
||||
PageSet<ObjectInfo> listObjects(String container, ListContainerOptions... options);
|
||||
|
||||
@Timeout(duration = 5 * 1024 * 1024 / 512, timeUnit = TimeUnit.SECONDS)
|
||||
SwiftObject getObject(String container, String name, GetOptions... options);
|
||||
|
||||
boolean setObjectInfo(String container, String name, Map<String, String> userMetadata);
|
||||
|
||||
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);
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -16,7 +16,7 @@
|
|||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jclouds.hpcloud.objectstorage.functions;
|
||||
package org.jclouds.openstack.swift.functions;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
|
|
@ -32,6 +32,8 @@ import org.jclouds.http.HttpErrorHandler;
|
|||
import org.jclouds.http.HttpResponse;
|
||||
import org.jclouds.http.HttpResponseException;
|
||||
import org.jclouds.logging.Logger;
|
||||
import org.jclouds.openstack.swift.CopyObjectException;
|
||||
import org.jclouds.openstack.swift.reference.SwiftHeaders;
|
||||
import org.jclouds.rest.AuthorizationException;
|
||||
|
||||
/**
|
||||
|
@ -61,10 +63,21 @@ public class ParseSwiftErrorFromHttpResponse implements HttpErrorHandler {
|
|||
exception = new AuthorizationException(exception.getMessage(), exception);
|
||||
break;
|
||||
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();
|
||||
Matcher matcher = CONTAINER_PATH.matcher(path);
|
||||
Exception oldException = exception;
|
||||
|
||||
if (matcher.find()) {
|
||||
exception = new ContainerNotFoundException(matcher.group(1), message);
|
||||
exception.initCause(oldException);
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jclouds.hpcloud.objectstorage.options;
|
||||
package org.jclouds.openstack.swift.options;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
@ -61,17 +61,11 @@ public class CreateContainerOptions extends BaseHttpRequestOptions {
|
|||
|
||||
public static class Builder {
|
||||
|
||||
/**
|
||||
* @see org.jclouds.hpcloud.objectstorage.options.CreateContainerOptions#withPublicAccess
|
||||
*/
|
||||
public static CreateContainerOptions withPublicAccess() {
|
||||
CreateContainerOptions options = new CreateContainerOptions();
|
||||
return options.withPublicAccess();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.jclouds.hpcloud.objectstorage.options.CreateContainerOptions#withMetadata(Multimap<String, String>)
|
||||
*/
|
||||
public static CreateContainerOptions withMetadata(Map<String, String> metadata) {
|
||||
CreateContainerOptions options = new CreateContainerOptions();
|
||||
return (CreateContainerOptions) options.withMetadata(metadata);
|
|
@ -29,7 +29,9 @@ public interface SwiftHeaders {
|
|||
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_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 OBJECT_COPY_FROM = "X-Copy-From";
|
||||
|
||||
public static final String CONTAINER_READ = "X-Container-Read";
|
||||
public static final String CONTAINER_WRITE = "X-Container-Write";
|
||||
|
|
|
@ -20,8 +20,10 @@ package org.jclouds.openstack.swift;
|
|||
|
||||
import static org.jclouds.openstack.swift.options.ListContainerOptions.Builder.underPath;
|
||||
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 static org.testng.Assert.fail;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
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) {
|
||||
String contentType = getBlob.getPayload().getContentMetadata().getContentType();
|
||||
assert contentType.startsWith("text/plain") || "application/x-www-form-urlencoded".equals(contentType): contentType;
|
||||
|
|
|
@ -23,9 +23,14 @@ import static org.testng.Assert.assertTrue;
|
|||
|
||||
import org.jclouds.http.HttpRequest;
|
||||
import org.jclouds.http.HttpResponse;
|
||||
import org.jclouds.http.HttpResponseException;
|
||||
import org.jclouds.openstack.swift.internal.BaseSwiftExpectTest;
|
||||
import org.jclouds.openstack.swift.reference.SwiftHeaders;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Adrian Cole
|
||||
|
@ -33,7 +38,8 @@ import org.testng.annotations.Test;
|
|||
@Test(testName = "SwiftClientExpectTest")
|
||||
public class SwiftClientExpectTest extends BaseSwiftExpectTest<SwiftClient> {
|
||||
|
||||
public void testContainerExistsWhenResponseIs2xxReturnsTrue() throws Exception {
|
||||
@Test
|
||||
public void testContainerExistsWhenResponseIs2xxReturnsTrue() {
|
||||
HttpRequest headContainer = HttpRequest.builder()
|
||||
.method("HEAD")
|
||||
.endpoint(swiftEndpointWithHostReplaced + "/foo")
|
||||
|
@ -47,7 +53,8 @@ public class SwiftClientExpectTest extends BaseSwiftExpectTest<SwiftClient> {
|
|||
assertTrue(clientWhenContainerExists.containerExists("foo"));
|
||||
}
|
||||
|
||||
public void testContainerExistsWhenResponseIs404ReturnsFalse() throws Exception {
|
||||
@Test
|
||||
public void testContainerExistsWhenResponseIs404ReturnsFalse() {
|
||||
HttpRequest headContainer = HttpRequest.builder()
|
||||
.method("HEAD")
|
||||
.endpoint(swiftEndpointWithHostReplaced + "/foo")
|
||||
|
@ -59,7 +66,150 @@ public class SwiftClientExpectTest extends BaseSwiftExpectTest<SwiftClient> {
|
|||
authResponse, headContainer, headContainerResponse);
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -18,15 +18,28 @@
|
|||
*/
|
||||
package org.jclouds.openstack.swift.blobstore.integration;
|
||||
|
||||
import static org.testng.Assert.assertEquals;
|
||||
import static org.testng.Assert.assertTrue;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import org.jclouds.blobstore.BlobStore;
|
||||
import org.jclouds.blobstore.integration.internal.BaseContainerIntegrationTest;
|
||||
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 com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
/**
|
||||
* @author James Murty
|
||||
* @author Adrian Cole
|
||||
* @author Everett Toews
|
||||
*/
|
||||
@Test(groups = "live")
|
||||
public class SwiftContainerIntegrationLiveTest extends BaseContainerIntegrationTest {
|
||||
|
@ -40,4 +53,53 @@ public class SwiftContainerIntegrationLiveTest extends BaseContainerIntegrationT
|
|||
public SwiftContainerIntegrationLiveTest() {
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,5 +46,4 @@ public class SwiftContainerLiveTest extends BaseContainerLiveTest {
|
|||
public void testPublicAccess() throws MalformedURLException, InterruptedException, IOException {
|
||||
super.testPublicAccess();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -31,9 +31,14 @@ import org.jclouds.blobstore.ContainerNotFoundException;
|
|||
import org.jclouds.blobstore.KeyNotFoundException;
|
||||
import org.jclouds.http.HttpCommand;
|
||||
import org.jclouds.http.HttpRequest;
|
||||
import org.jclouds.http.HttpRequest.Builder;
|
||||
import org.jclouds.http.HttpResponse;
|
||||
import org.jclouds.openstack.swift.CopyObjectException;
|
||||
import org.jclouds.openstack.swift.reference.SwiftHeaders;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Adrian Cole
|
||||
|
@ -41,6 +46,13 @@ import org.testng.annotations.Test;
|
|||
@Test(groups = { "unit" })
|
||||
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
|
||||
public void test404SetsKeyNotFoundExceptionMosso() {
|
||||
assertCodeMakes("HEAD",
|
||||
|
@ -76,24 +88,34 @@ public class ParseSwiftErrorFromHttpResponseTest {
|
|||
|
||||
private void assertCodeMakes(String method, URI uri, int statusCode, String message, String contentType,
|
||||
String content, Class<? extends Exception> 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);
|
||||
assertCodeMakes(method, uri, statusCode, message, contentType, content, "", expected);
|
||||
}
|
||||
|
||||
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) {
|
||||
reportMatcher(new IArgumentMatcher() {
|
||||
|
||||
|
|
|
@ -53,6 +53,7 @@ import org.jclouds.openstack.swift.domain.ContainerMetadata;
|
|||
import org.jclouds.openstack.swift.domain.MutableObjectInfoWithMetadata;
|
||||
import org.jclouds.openstack.swift.domain.ObjectInfo;
|
||||
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.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,
|
||||
org.jclouds.openstack.swift.options.ListContainerOptions... optionsList) {
|
||||
ListContainerOptions options = container2ContainerListOptions.apply(optionsList);
|
||||
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) {
|
||||
return blobStore.putBlob(container, object2Blob.apply(object));
|
||||
}
|
||||
|
@ -195,5 +217,4 @@ public class StubSwiftAsyncClient implements CommonSwiftAsyncClient {
|
|||
public ListenableFuture<Boolean> objectExists(String bucketName, String key) {
|
||||
return blobStore.blobExists(bucketName, key);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -16,10 +16,11 @@
|
|||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jclouds.hpcloud.objectstorage.options;
|
||||
package org.jclouds.openstack.swift.options;
|
||||
|
||||
import static org.testng.Assert.assertEquals;
|
||||
|
||||
import org.jclouds.openstack.swift.options.CreateContainerOptions;
|
||||
import org.jclouds.openstack.swift.reference.SwiftHeaders;
|
||||
import org.testng.annotations.Test;
|
||||
|
|
@ -23,16 +23,11 @@ import java.util.Set;
|
|||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.DELETE;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.HEAD;
|
||||
import javax.ws.rs.PUT;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
|
||||
import org.jclouds.blobstore.functions.ReturnNullOnContainerNotFound;
|
||||
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.swift.CommonSwiftAsyncClient;
|
||||
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.QueryParams;
|
||||
import org.jclouds.rest.annotations.RequestFilters;
|
||||
import org.jclouds.rest.annotations.ResponseParser;
|
||||
import org.jclouds.rest.annotations.SkipEncoding;
|
||||
|
||||
import com.google.common.annotations.Beta;
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
|
||||
|
@ -69,24 +62,6 @@ import com.google.common.util.concurrent.ListenableFuture;
|
|||
@Endpoint(Storage.class)
|
||||
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
|
||||
*/
|
||||
|
|
|
@ -22,9 +22,7 @@ import java.util.concurrent.TimeUnit;
|
|||
|
||||
import org.jclouds.concurrent.Timeout;
|
||||
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.domain.ContainerMetadata;
|
||||
import org.jclouds.rest.annotations.Delegate;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
|
@ -46,10 +44,6 @@ import com.google.common.util.concurrent.ListenableFuture;
|
|||
@Timeout(duration = 120, timeUnit = TimeUnit.SECONDS)
|
||||
public interface HPCloudObjectStorageClient extends CommonSwiftClient {
|
||||
|
||||
boolean createContainer(String container, CreateContainerOptions... options);
|
||||
|
||||
ContainerMetadata getContainerMetadata(String container);
|
||||
|
||||
/**
|
||||
* Provides synchronous access to CDN features.
|
||||
*/
|
||||
|
|
Loading…
Reference in New Issue