mirror of https://github.com/apache/jclouds.git
Issue 75: cloud files now uses native rackspace object model
git-svn-id: http://jclouds.googlecode.com/svn/trunk@2005 3d8758e0-26b5-11de-8745-db77d3ebf521
This commit is contained in:
parent
1bc88f36f2
commit
903254a6f9
|
@ -38,10 +38,7 @@ import javax.ws.rs.Path;
|
||||||
import javax.ws.rs.PathParam;
|
import javax.ws.rs.PathParam;
|
||||||
|
|
||||||
import org.jclouds.blobstore.binders.BindMapToHeadersWithPrefix;
|
import org.jclouds.blobstore.binders.BindMapToHeadersWithPrefix;
|
||||||
import org.jclouds.blobstore.domain.Blob;
|
|
||||||
import org.jclouds.blobstore.domain.BlobMetadata;
|
|
||||||
import org.jclouds.blobstore.domain.BoundedSortedSet;
|
import org.jclouds.blobstore.domain.BoundedSortedSet;
|
||||||
import org.jclouds.blobstore.functions.BlobName;
|
|
||||||
import org.jclouds.blobstore.functions.ReturnVoidOnNotFoundOr404;
|
import org.jclouds.blobstore.functions.ReturnVoidOnNotFoundOr404;
|
||||||
import org.jclouds.blobstore.functions.ThrowContainerNotFoundOn404;
|
import org.jclouds.blobstore.functions.ThrowContainerNotFoundOn404;
|
||||||
import org.jclouds.blobstore.functions.ThrowKeyNotFoundOn404;
|
import org.jclouds.blobstore.functions.ThrowKeyNotFoundOn404;
|
||||||
|
@ -50,17 +47,21 @@ import org.jclouds.http.functions.ReturnFalseOn404;
|
||||||
import org.jclouds.http.options.GetOptions;
|
import org.jclouds.http.options.GetOptions;
|
||||||
import org.jclouds.rackspace.CloudFiles;
|
import org.jclouds.rackspace.CloudFiles;
|
||||||
import org.jclouds.rackspace.CloudFilesCDN;
|
import org.jclouds.rackspace.CloudFilesCDN;
|
||||||
import org.jclouds.rackspace.cloudfiles.binders.BindCFObjectAsEntity;
|
import org.jclouds.rackspace.cloudfiles.binders.BindCFObjectToEntity;
|
||||||
import org.jclouds.rackspace.cloudfiles.domain.AccountMetadata;
|
import org.jclouds.rackspace.cloudfiles.domain.AccountMetadata;
|
||||||
|
import org.jclouds.rackspace.cloudfiles.domain.CFObject;
|
||||||
import org.jclouds.rackspace.cloudfiles.domain.ContainerCDNMetadata;
|
import org.jclouds.rackspace.cloudfiles.domain.ContainerCDNMetadata;
|
||||||
import org.jclouds.rackspace.cloudfiles.domain.ContainerMetadata;
|
import org.jclouds.rackspace.cloudfiles.domain.ContainerMetadata;
|
||||||
|
import org.jclouds.rackspace.cloudfiles.domain.MutableObjectInfoWithMetadata;
|
||||||
|
import org.jclouds.rackspace.cloudfiles.domain.ObjectInfo;
|
||||||
|
import org.jclouds.rackspace.cloudfiles.functions.ObjectName;
|
||||||
import org.jclouds.rackspace.cloudfiles.functions.ParseAccountMetadataResponseFromHeaders;
|
import org.jclouds.rackspace.cloudfiles.functions.ParseAccountMetadataResponseFromHeaders;
|
||||||
import org.jclouds.rackspace.cloudfiles.functions.ParseBlobFromHeadersAndHttpContent;
|
|
||||||
import org.jclouds.rackspace.cloudfiles.functions.ParseBlobMetadataListFromJsonResponse;
|
|
||||||
import org.jclouds.rackspace.cloudfiles.functions.ParseCdnUriFromHeaders;
|
import org.jclouds.rackspace.cloudfiles.functions.ParseCdnUriFromHeaders;
|
||||||
import org.jclouds.rackspace.cloudfiles.functions.ParseContainerCDNMetadataFromHeaders;
|
import org.jclouds.rackspace.cloudfiles.functions.ParseContainerCDNMetadataFromHeaders;
|
||||||
import org.jclouds.rackspace.cloudfiles.functions.ParseContainerCDNMetadataListFromGsonResponse;
|
import org.jclouds.rackspace.cloudfiles.functions.ParseContainerCDNMetadataListFromGsonResponse;
|
||||||
import org.jclouds.rackspace.cloudfiles.functions.ParseContainerListFromJsonResponse;
|
import org.jclouds.rackspace.cloudfiles.functions.ParseContainerListFromJsonResponse;
|
||||||
|
import org.jclouds.rackspace.cloudfiles.functions.ParseObjectFromHeadersAndHttpContent;
|
||||||
|
import org.jclouds.rackspace.cloudfiles.functions.ParseObjectInfoListFromJsonResponse;
|
||||||
import org.jclouds.rackspace.cloudfiles.functions.ParseObjectMetadataFromHeaders;
|
import org.jclouds.rackspace.cloudfiles.functions.ParseObjectMetadataFromHeaders;
|
||||||
import org.jclouds.rackspace.cloudfiles.functions.ReturnTrueOn404FalseOn409;
|
import org.jclouds.rackspace.cloudfiles.functions.ReturnTrueOn404FalseOn409;
|
||||||
import org.jclouds.rackspace.cloudfiles.options.ListCdnContainerOptions;
|
import org.jclouds.rackspace.cloudfiles.options.ListCdnContainerOptions;
|
||||||
|
@ -91,7 +92,7 @@ import org.jclouds.rest.annotations.SkipEncoding;
|
||||||
@Endpoint(CloudFiles.class)
|
@Endpoint(CloudFiles.class)
|
||||||
public interface CloudFilesClient {
|
public interface CloudFilesClient {
|
||||||
|
|
||||||
Blob newBlob();
|
CFObject newCFObject();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* HEAD operations against an account are performed to retrieve the number of Containers and the
|
* HEAD operations against an account are performed to retrieve the number of Containers and the
|
||||||
|
@ -142,9 +143,9 @@ public interface CloudFilesClient {
|
||||||
SortedSet<ContainerMetadata> listContainers(ListContainerOptions... options);
|
SortedSet<ContainerMetadata> listContainers(ListContainerOptions... options);
|
||||||
|
|
||||||
@POST
|
@POST
|
||||||
@Path("{container}/{key}")
|
@Path("{container}/{name}")
|
||||||
boolean setObjectMetadata(@PathParam("container") String container,
|
boolean setObjectMetadata(@PathParam("container") String container,
|
||||||
@PathParam("key") String key,
|
@PathParam("name") String name,
|
||||||
@BinderParam(BindMapToHeadersWithPrefix.class) Map<String, String> userMetadata);
|
@BinderParam(BindMapToHeadersWithPrefix.class) Map<String, String> userMetadata);
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
|
@ -200,9 +201,9 @@ public interface CloudFilesClient {
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@QueryParams(keys = "format", values = "json")
|
@QueryParams(keys = "format", values = "json")
|
||||||
@ResponseParser(ParseBlobMetadataListFromJsonResponse.class)
|
@ResponseParser(ParseObjectInfoListFromJsonResponse.class)
|
||||||
@Path("{container}")
|
@Path("{container}")
|
||||||
Future<BoundedSortedSet<BlobMetadata>> listObjects(@PathParam("container") String container,
|
Future<BoundedSortedSet<ObjectInfo>> listObjects(@PathParam("container") String container,
|
||||||
ListContainerOptions... options);
|
ListContainerOptions... options);
|
||||||
|
|
||||||
@HEAD
|
@HEAD
|
||||||
|
@ -211,29 +212,28 @@ public interface CloudFilesClient {
|
||||||
boolean containerExists(@PathParam("container") String container);
|
boolean containerExists(@PathParam("container") String container);
|
||||||
|
|
||||||
@PUT
|
@PUT
|
||||||
@Path("{container}/{key}")
|
@Path("{container}/{name}")
|
||||||
@ResponseParser(ParseETagHeader.class)
|
@ResponseParser(ParseETagHeader.class)
|
||||||
Future<String> putObject(
|
Future<String> putObject(
|
||||||
@PathParam("container") String container,
|
@PathParam("container") String container,
|
||||||
@PathParam("key") @ParamParser(BlobName.class) @BinderParam(BindCFObjectAsEntity.class) Blob object);
|
@PathParam("name") @ParamParser(ObjectName.class) @BinderParam(BindCFObjectToEntity.class) CFObject object);
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@ResponseParser(ParseBlobFromHeadersAndHttpContent.class)
|
@ResponseParser(ParseObjectFromHeadersAndHttpContent.class)
|
||||||
@ExceptionParser(ThrowKeyNotFoundOn404.class)
|
@ExceptionParser(ThrowKeyNotFoundOn404.class)
|
||||||
@Path("{container}/{key}")
|
@Path("{container}/{name}")
|
||||||
Future<Blob> getObject(@PathParam("container") String container, @PathParam("key") String key,
|
Future<CFObject> getObject(@PathParam("container") String container, @PathParam("name") String name,
|
||||||
GetOptions... options);
|
GetOptions... options);
|
||||||
|
|
||||||
@HEAD
|
@HEAD
|
||||||
@ResponseParser(ParseObjectMetadataFromHeaders.class)
|
@ResponseParser(ParseObjectMetadataFromHeaders.class)
|
||||||
@ExceptionParser(ThrowKeyNotFoundOn404.class)
|
@ExceptionParser(ThrowKeyNotFoundOn404.class)
|
||||||
@Path("{container}/{key}")
|
@Path("{container}/{name}")
|
||||||
BlobMetadata getObjectMetadata(@PathParam("container") String container,
|
MutableObjectInfoWithMetadata getObjectInfo(@PathParam("container") String container, @PathParam("name") String name);
|
||||||
@PathParam("key") String key);
|
|
||||||
|
|
||||||
@DELETE
|
@DELETE
|
||||||
@ExceptionParser(ReturnVoidOnNotFoundOr404.class)
|
@ExceptionParser(ReturnVoidOnNotFoundOr404.class)
|
||||||
@Path("{container}/{key}")
|
@Path("{container}/{name}")
|
||||||
Future<Void> removeObject(@PathParam("container") String container, @PathParam("key") String key);
|
Future<Void> removeObject(@PathParam("container") String container, @PathParam("name") String name);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,25 +26,29 @@ package org.jclouds.rackspace.cloudfiles.binders;
|
||||||
import static com.google.common.base.Preconditions.checkArgument;
|
import static com.google.common.base.Preconditions.checkArgument;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Named;
|
|
||||||
import javax.ws.rs.core.HttpHeaders;
|
import javax.ws.rs.core.HttpHeaders;
|
||||||
|
|
||||||
import org.jclouds.blobstore.binders.BindBlobToEntityAndUserMetadataToHeadersWithPrefix;
|
import org.jclouds.blobstore.binders.BindBlobToEntityAndUserMetadataToHeadersWithPrefix;
|
||||||
import org.jclouds.blobstore.domain.Blob;
|
|
||||||
import org.jclouds.http.HttpRequest;
|
import org.jclouds.http.HttpRequest;
|
||||||
import org.jclouds.http.HttpUtils;
|
import org.jclouds.http.HttpUtils;
|
||||||
import org.jclouds.rackspace.cloudfiles.reference.CloudFilesConstants;
|
import org.jclouds.rackspace.cloudfiles.blobstore.functions.ObjectToBlob;
|
||||||
|
import org.jclouds.rackspace.cloudfiles.domain.CFObject;
|
||||||
|
import org.jclouds.rest.Binder;
|
||||||
|
|
||||||
|
public class BindCFObjectToEntity implements Binder {
|
||||||
|
private final BindBlobToEntityAndUserMetadataToHeadersWithPrefix blobBinder;
|
||||||
|
private final ObjectToBlob object2Blob;
|
||||||
|
|
||||||
public class BindCFObjectAsEntity extends BindBlobToEntityAndUserMetadataToHeadersWithPrefix {
|
|
||||||
@Inject
|
@Inject
|
||||||
public BindCFObjectAsEntity(
|
public BindCFObjectToEntity(ObjectToBlob object2Blob,
|
||||||
@Named(CloudFilesConstants.PROPERTY_CLOUDFILES_METADATA_PREFIX) String metadataPrefix) {
|
BindBlobToEntityAndUserMetadataToHeadersWithPrefix blobBinder) {
|
||||||
super(metadataPrefix);
|
this.blobBinder = blobBinder;
|
||||||
|
this.object2Blob = object2Blob;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void bindToRequest(HttpRequest request, Object entity) {
|
public void bindToRequest(HttpRequest request, Object entity) {
|
||||||
Blob object = (Blob) entity;
|
CFObject object = (CFObject) entity;
|
||||||
if (object.getContentLength() >= 0) {
|
if (object.getContentLength() != null && object.getContentLength() >= 0) {
|
||||||
checkArgument(object.getContentLength() <= 5 * 1024 * 1024 * 1024,
|
checkArgument(object.getContentLength() <= 5 * 1024 * 1024 * 1024,
|
||||||
"maximum size for put object is 5GB");
|
"maximum size for put object is 5GB");
|
||||||
request.getHeaders().put(HttpHeaders.CONTENT_LENGTH, object.getContentLength() + "");
|
request.getHeaders().put(HttpHeaders.CONTENT_LENGTH, object.getContentLength() + "");
|
||||||
|
@ -52,14 +56,14 @@ public class BindCFObjectAsEntity extends BindBlobToEntityAndUserMetadataToHeade
|
||||||
// Enable "chunked"/"streamed" data, where the size needn't be known in advance.
|
// Enable "chunked"/"streamed" data, where the size needn't be known in advance.
|
||||||
request.getHeaders().put("Transfer-Encoding", "chunked");
|
request.getHeaders().put("Transfer-Encoding", "chunked");
|
||||||
}
|
}
|
||||||
/**
|
|
||||||
* rackspace uses ETag header instead of Content-MD5.
|
blobBinder.bindToRequest(request, object2Blob.apply(object));
|
||||||
*/
|
if (object.getInfo().getHash() != null) {
|
||||||
if (object.getMetadata().getContentMD5() != null) {
|
request.getHeaders().put(HttpHeaders.ETAG,
|
||||||
request.getHeaders().put(HttpHeaders.ETAG,// note it needs to be in hex!
|
HttpUtils.toHexString(object.getInfo().getHash()));
|
||||||
HttpUtils.toHexString(object.getMetadata().getContentMD5()));
|
request.getHeaders().removeAll("Content-MD5");
|
||||||
}
|
}
|
||||||
super.bindToRequest(request, entity);
|
|
||||||
request.getHeaders().removeAll("Content-MD5");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
package org.jclouds.rackspace.cloudfiles.blobstore.functions;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
|
import org.jclouds.blobstore.domain.Blob;
|
||||||
|
import org.jclouds.rackspace.cloudfiles.domain.CFObject;
|
||||||
|
|
||||||
|
import com.google.common.base.Function;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Adrian Cole
|
||||||
|
*/
|
||||||
|
@Singleton
|
||||||
|
public class BlobToObject implements Function<Blob, CFObject> {
|
||||||
|
private final BlobToObjectInfo blob2ObjectMd;
|
||||||
|
private final CFObject.Factory objectProvider;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
BlobToObject(BlobToObjectInfo blob2ObjectMd, CFObject.Factory objectProvider) {
|
||||||
|
this.blob2ObjectMd = blob2ObjectMd;
|
||||||
|
this.objectProvider = objectProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CFObject apply(Blob from) {
|
||||||
|
CFObject object = objectProvider.create(blob2ObjectMd.apply(from.getMetadata()));
|
||||||
|
if (from.getContentLength() != null)
|
||||||
|
object.setContentLength(from.getContentLength());
|
||||||
|
object.setData(from.getData());
|
||||||
|
object.setAllHeaders(from.getAllHeaders());
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
package org.jclouds.rackspace.cloudfiles.blobstore.functions;
|
||||||
|
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
|
import org.jclouds.blobstore.domain.BlobMetadata;
|
||||||
|
import org.jclouds.rackspace.cloudfiles.domain.MutableObjectInfoWithMetadata;
|
||||||
|
import org.jclouds.rackspace.cloudfiles.domain.internal.MutableObjectInfoWithMetadataImpl;
|
||||||
|
|
||||||
|
import com.google.common.base.Function;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Adrian Cole
|
||||||
|
*/
|
||||||
|
@Singleton
|
||||||
|
public class BlobToObjectInfo implements Function<BlobMetadata, MutableObjectInfoWithMetadata> {
|
||||||
|
public MutableObjectInfoWithMetadata apply(BlobMetadata base) {
|
||||||
|
MutableObjectInfoWithMetadata to = new MutableObjectInfoWithMetadataImpl();
|
||||||
|
to.setContentType(base.getContentType());
|
||||||
|
to.setHash(base.getContentMD5());
|
||||||
|
to.setName(base.getName());
|
||||||
|
to.setLastModified(base.getLastModified());
|
||||||
|
if (base.getSize() != null)
|
||||||
|
to.setBytes(base.getSize());
|
||||||
|
if (base.getUserMetadata() != null)
|
||||||
|
to.getMetadata().putAll(base.getUserMetadata());
|
||||||
|
return to;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
package org.jclouds.rackspace.cloudfiles.blobstore.functions;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
|
import org.jclouds.blobstore.domain.Blob;
|
||||||
|
import org.jclouds.blobstore.domain.Blob.Factory;
|
||||||
|
import org.jclouds.rackspace.cloudfiles.domain.CFObject;
|
||||||
|
|
||||||
|
import com.google.common.base.Function;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Adrian Cole
|
||||||
|
*/
|
||||||
|
@Singleton
|
||||||
|
public class ObjectToBlob implements Function<CFObject, Blob> {
|
||||||
|
private final Blob.Factory blobFactory;
|
||||||
|
private final ObjectToBlobMetadata object2BlobMd;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
ObjectToBlob(Factory blobFactory, ObjectToBlobMetadata object2BlobMd) {
|
||||||
|
this.blobFactory = blobFactory;
|
||||||
|
this.object2BlobMd = object2BlobMd;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Blob apply(CFObject from) {
|
||||||
|
Blob blob = blobFactory.create(object2BlobMd.apply(from.getInfo()));
|
||||||
|
if (from.getContentLength() != null)
|
||||||
|
blob.setContentLength(from.getContentLength());
|
||||||
|
blob.setData(from.getData());
|
||||||
|
blob.setAllHeaders(from.getAllHeaders());
|
||||||
|
return blob;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
package org.jclouds.rackspace.cloudfiles.blobstore.functions;
|
||||||
|
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
|
import org.jclouds.blobstore.domain.MutableBlobMetadata;
|
||||||
|
import org.jclouds.blobstore.domain.ResourceType;
|
||||||
|
import org.jclouds.blobstore.domain.internal.MutableBlobMetadataImpl;
|
||||||
|
import org.jclouds.http.HttpUtils;
|
||||||
|
import org.jclouds.rackspace.cloudfiles.domain.MutableObjectInfoWithMetadata;
|
||||||
|
|
||||||
|
import com.google.common.base.Function;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Adrian Cole
|
||||||
|
*/
|
||||||
|
@Singleton
|
||||||
|
public class ObjectToBlobMetadata implements
|
||||||
|
Function<MutableObjectInfoWithMetadata, MutableBlobMetadata> {
|
||||||
|
public MutableBlobMetadata apply(MutableObjectInfoWithMetadata from) {
|
||||||
|
MutableBlobMetadata to = new MutableBlobMetadataImpl();
|
||||||
|
to.setContentMD5(from.getHash());
|
||||||
|
if (from.getContentType() != null)
|
||||||
|
to.setContentType(from.getContentType());
|
||||||
|
if (from.getHash() != null)
|
||||||
|
to.setETag(HttpUtils.toHexString(from.getHash()));
|
||||||
|
to.setName(from.getName());
|
||||||
|
if (from.getBytes() != null)
|
||||||
|
to.setSize(from.getBytes());
|
||||||
|
to.setType(ResourceType.BLOB);
|
||||||
|
to.setUserMetadata(from.getMetadata());
|
||||||
|
if (from.getContentType() != null && from.getContentType().equals("application/directory")) {
|
||||||
|
to.setType(ResourceType.RELATIVE_PATH);
|
||||||
|
}
|
||||||
|
return to;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
package org.jclouds.rackspace.cloudfiles.config;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Provider;
|
||||||
|
|
||||||
|
import org.jclouds.blobstore.functions.CalculateSize;
|
||||||
|
import org.jclouds.blobstore.functions.GenerateMD5;
|
||||||
|
import org.jclouds.blobstore.functions.GenerateMD5Result;
|
||||||
|
import org.jclouds.rackspace.cloudfiles.domain.CFObject;
|
||||||
|
import org.jclouds.rackspace.cloudfiles.domain.MutableObjectInfoWithMetadata;
|
||||||
|
import org.jclouds.rackspace.cloudfiles.domain.internal.CFObjectImpl;
|
||||||
|
|
||||||
|
import com.google.inject.AbstractModule;
|
||||||
|
import com.google.inject.Provides;
|
||||||
|
import com.google.inject.Scopes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures the domain object mappings needed for all CF implementations
|
||||||
|
*
|
||||||
|
* @author Adrian Cole
|
||||||
|
*/
|
||||||
|
public class CFObjectModule extends AbstractModule {
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* explicit factories are created here as it has been shown that Assisted Inject is extremely
|
||||||
|
* inefficient. http://code.google.com/p/google-guice/issues/detail?id=435
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void configure() {
|
||||||
|
bind(CFObject.Factory.class).to(CFObjectFactory.class).in(Scopes.SINGLETON);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class CFObjectFactory implements CFObject.Factory {
|
||||||
|
@Inject
|
||||||
|
GenerateMD5Result generateMD5Result;
|
||||||
|
@Inject
|
||||||
|
GenerateMD5 generateMD5;
|
||||||
|
@Inject
|
||||||
|
CalculateSize calculateSize;
|
||||||
|
@Inject
|
||||||
|
Provider<MutableObjectInfoWithMetadata> metadataProvider;
|
||||||
|
|
||||||
|
public CFObject create(MutableObjectInfoWithMetadata metadata) {
|
||||||
|
return new CFObjectImpl(generateMD5Result, generateMD5, calculateSize,
|
||||||
|
metadata != null ? metadata : metadataProvider.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
CFObject provideCFObject(CFObject.Factory factory) {
|
||||||
|
return factory.create(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -50,6 +50,7 @@ public class CloudFilesContextModule extends AbstractModule {
|
||||||
protected void configure() {
|
protected void configure() {
|
||||||
// for converters to work.
|
// for converters to work.
|
||||||
install(new BlobStoreObjectModule());
|
install(new BlobStoreObjectModule());
|
||||||
|
install(new CFObjectModule());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
|
|
|
@ -0,0 +1,89 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.com>
|
||||||
|
*
|
||||||
|
* ====================================================================
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
* ====================================================================
|
||||||
|
*/
|
||||||
|
package org.jclouds.rackspace.cloudfiles.domain;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import com.google.common.collect.Multimap;
|
||||||
|
import com.google.inject.internal.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Adrian Cole
|
||||||
|
*/
|
||||||
|
public interface CFObject extends Comparable<CFObject> {
|
||||||
|
public interface Factory {
|
||||||
|
CFObject create(@Nullable MutableObjectInfoWithMetadata info);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets entity for the request or the content from the response. If size isn't set, this will
|
||||||
|
* attempt to discover it.
|
||||||
|
*
|
||||||
|
* @param data
|
||||||
|
* typically InputStream for downloads, or File, byte [], String, or InputStream for
|
||||||
|
* uploads.
|
||||||
|
*/
|
||||||
|
void setData(Object data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return InputStream, if downloading, or whatever was set during {@link #setData(Object)}
|
||||||
|
*/
|
||||||
|
Object getData();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* generate an MD5 Hash for the current data.
|
||||||
|
* <p/>
|
||||||
|
* <h2>Note</h2>
|
||||||
|
* <p/>
|
||||||
|
* If this is an InputStream, it will be converted to a byte array first.
|
||||||
|
*
|
||||||
|
* @throws IOException
|
||||||
|
* if there is a problem generating the hash.
|
||||||
|
*/
|
||||||
|
void generateMD5() throws IOException;
|
||||||
|
|
||||||
|
void setContentLength(long contentLength);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the total size of the downloaded object, or the chunk that's available.
|
||||||
|
* <p/>
|
||||||
|
* Chunking is only used when org.jclouds.http.GetOptions is called with options like tail,
|
||||||
|
* range, or startAt.
|
||||||
|
*
|
||||||
|
* @return the length in bytes that can be be obtained from {@link #getData()}
|
||||||
|
* @see org.jclouds.http.HttpHeaders#CONTENT_LENGTH
|
||||||
|
* @see GetObjectOptions
|
||||||
|
*/
|
||||||
|
Long getContentLength();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return System and User metadata relevant to this object.
|
||||||
|
*/
|
||||||
|
MutableObjectInfoWithMetadata getInfo();
|
||||||
|
|
||||||
|
Multimap<String, String> getAllHeaders();
|
||||||
|
|
||||||
|
void setAllHeaders(Multimap<String, String> allHeaders);
|
||||||
|
}
|
|
@ -40,7 +40,6 @@ public class ContainerMetadata implements Comparable<ContainerMetadata> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public ContainerMetadata(String name, long count, long bytes) {
|
public ContainerMetadata(String name, long count, long bytes) {
|
||||||
this();
|
|
||||||
setName(name);
|
setName(name);
|
||||||
setBytes(bytes);
|
setBytes(bytes);
|
||||||
setCount(count);
|
setCount(count);
|
||||||
|
|
|
@ -21,24 +21,33 @@
|
||||||
* under the License.
|
* under the License.
|
||||||
* ====================================================================
|
* ====================================================================
|
||||||
*/
|
*/
|
||||||
package org.jclouds.rackspace.cloudfiles.functions;
|
package org.jclouds.rackspace.cloudfiles.domain;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.jclouds.blobstore.domain.Blob;
|
import org.jclouds.rackspace.cloudfiles.domain.internal.MutableObjectInfoWithMetadataImpl;
|
||||||
import org.jclouds.blobstore.functions.ParseSystemAndUserMetadataFromHeaders;
|
import org.joda.time.DateTime;
|
||||||
|
|
||||||
|
import com.google.inject.ImplementedBy;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses response headers and creates a new Rackspace Blob from them and the HTTP content.
|
|
||||||
*
|
*
|
||||||
* @see ParseSystemAndUserMetadataFromHeaders
|
|
||||||
* @author Adrian Cole
|
* @author Adrian Cole
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
public class ParseBlobFromHeadersAndHttpContent extends
|
@ImplementedBy(MutableObjectInfoWithMetadataImpl.class)
|
||||||
org.jclouds.blobstore.functions.ParseBlobFromHeadersAndHttpContent {
|
public interface MutableObjectInfoWithMetadata extends ObjectInfo {
|
||||||
@Inject
|
|
||||||
public ParseBlobFromHeadersAndHttpContent(ParseObjectMetadataFromHeaders metadataParser,
|
void setName(String name);
|
||||||
Blob.Factory blobFactory) {
|
|
||||||
super(metadataParser, blobFactory);
|
void setHash(byte[] hash);
|
||||||
}
|
|
||||||
|
void setBytes(long bytes);
|
||||||
|
|
||||||
|
void setLastModified(DateTime lastModified);
|
||||||
|
|
||||||
|
void setContentType(String contentType);
|
||||||
|
|
||||||
|
Map<String, String> getMetadata();
|
||||||
|
|
||||||
}
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.com>
|
||||||
|
*
|
||||||
|
* ====================================================================
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
* ====================================================================
|
||||||
|
*/
|
||||||
|
package org.jclouds.rackspace.cloudfiles.domain;
|
||||||
|
|
||||||
|
import org.joda.time.DateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Adrian Cole
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public interface ObjectInfo extends Comparable<ObjectInfo> {
|
||||||
|
|
||||||
|
String getName();
|
||||||
|
byte [] getHash();
|
||||||
|
Long getBytes();
|
||||||
|
String getContentType();
|
||||||
|
DateTime getLastModified();
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,121 @@
|
||||||
|
package org.jclouds.rackspace.cloudfiles.domain.internal;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
import static com.google.common.base.Preconditions.checkState;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import org.jclouds.blobstore.domain.MD5InputStreamResult;
|
||||||
|
import org.jclouds.blobstore.functions.CalculateSize;
|
||||||
|
import org.jclouds.blobstore.functions.GenerateMD5;
|
||||||
|
import org.jclouds.blobstore.functions.GenerateMD5Result;
|
||||||
|
import org.jclouds.rackspace.cloudfiles.domain.CFObject;
|
||||||
|
import org.jclouds.rackspace.cloudfiles.domain.MutableObjectInfoWithMetadata;
|
||||||
|
|
||||||
|
import com.google.common.collect.HashMultimap;
|
||||||
|
import com.google.common.collect.Multimap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default Implementation of {@link CFObject}.
|
||||||
|
*
|
||||||
|
* @author Adrian Cole
|
||||||
|
*/
|
||||||
|
public class CFObjectImpl implements CFObject, Comparable<CFObject> {
|
||||||
|
private final GenerateMD5Result generateMD5Result;
|
||||||
|
private final GenerateMD5 generateMD5;
|
||||||
|
private final CalculateSize calculateSize;
|
||||||
|
private final MutableObjectInfoWithMetadata info;
|
||||||
|
private Object data;
|
||||||
|
private Multimap<String, String> allHeaders = HashMultimap.create();
|
||||||
|
private Long contentLength;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public CFObjectImpl(GenerateMD5Result generateMD5Result, GenerateMD5 generateMD5,
|
||||||
|
CalculateSize calculateSize, MutableObjectInfoWithMetadata info) {
|
||||||
|
this.generateMD5Result = generateMD5Result;
|
||||||
|
this.generateMD5 = generateMD5;
|
||||||
|
this.calculateSize = calculateSize;
|
||||||
|
this.info = info;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public void generateMD5() {
|
||||||
|
checkState(data != null, "data");
|
||||||
|
if (data instanceof InputStream) {
|
||||||
|
MD5InputStreamResult result = generateMD5Result.apply((InputStream) data);
|
||||||
|
getInfo().setHash(result.md5);
|
||||||
|
setContentLength(result.length);
|
||||||
|
setData(result.data);
|
||||||
|
} else {
|
||||||
|
getInfo().setHash(generateMD5.apply(data));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public Object getData() {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public void setData(Object data) {
|
||||||
|
this.data = checkNotNull(data, "data");
|
||||||
|
if (getContentLength() == null) {
|
||||||
|
Long size = calculateSize.apply(data);
|
||||||
|
if (size != null)
|
||||||
|
this.setContentLength(size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public Long getContentLength() {
|
||||||
|
return contentLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public void setContentLength(long contentLength) {
|
||||||
|
this.contentLength = contentLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public MutableObjectInfoWithMetadata getInfo() {
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public Multimap<String, String> getAllHeaders() {
|
||||||
|
return allHeaders;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public void setAllHeaders(Multimap<String, String> allHeaders) {
|
||||||
|
this.allHeaders = checkNotNull(allHeaders, "allHeaders");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public int compareTo(CFObject o) {
|
||||||
|
if (getInfo().getName() == null)
|
||||||
|
return -1;
|
||||||
|
return (this == o) ? 0 : getInfo().getName().compareTo(o.getInfo().getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,151 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.com>
|
||||||
|
*
|
||||||
|
* ====================================================================
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
* ====================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jclouds.rackspace.cloudfiles.domain.internal;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.ws.rs.core.MediaType;
|
||||||
|
|
||||||
|
import org.jclouds.rackspace.cloudfiles.domain.MutableObjectInfoWithMetadata;
|
||||||
|
import org.jclouds.rackspace.cloudfiles.domain.ObjectInfo;
|
||||||
|
import org.joda.time.DateTime;
|
||||||
|
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Adrian Cole
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class MutableObjectInfoWithMetadataImpl implements MutableObjectInfoWithMetadata {
|
||||||
|
private String name;
|
||||||
|
private Long bytes;
|
||||||
|
private byte[] hash;
|
||||||
|
private String contentType = MediaType.APPLICATION_OCTET_STREAM;
|
||||||
|
private DateTime lastModified;
|
||||||
|
private final Map<String, String> metadata = Maps.newHashMap();
|
||||||
|
|
||||||
|
public Map<String, String> getMetadata() {
|
||||||
|
return metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBytes(long bytes) {
|
||||||
|
this.bytes = bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setContentType(String contentType) {
|
||||||
|
this.contentType = contentType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHash(byte[] hash) {
|
||||||
|
this.hash = hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getBytes() {
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getContentType() {
|
||||||
|
return contentType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getHash() {
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DateTime getLastModified() {
|
||||||
|
return lastModified;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
final int prime = 31;
|
||||||
|
int result = 1;
|
||||||
|
result = prime * result + ((bytes == null) ? 0 : bytes.hashCode());
|
||||||
|
result = prime * result + ((contentType == null) ? 0 : contentType.hashCode());
|
||||||
|
result = prime * result + Arrays.hashCode(hash);
|
||||||
|
result = prime * result + ((lastModified == null) ? 0 : lastModified.hashCode());
|
||||||
|
result = prime * result + ((metadata == null) ? 0 : metadata.hashCode());
|
||||||
|
result = prime * result + ((name == null) ? 0 : name.hashCode());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj)
|
||||||
|
return true;
|
||||||
|
if (obj == null)
|
||||||
|
return false;
|
||||||
|
if (getClass() != obj.getClass())
|
||||||
|
return false;
|
||||||
|
MutableObjectInfoWithMetadataImpl other = (MutableObjectInfoWithMetadataImpl) obj;
|
||||||
|
if (bytes == null) {
|
||||||
|
if (other.bytes != null)
|
||||||
|
return false;
|
||||||
|
} else if (!bytes.equals(other.bytes))
|
||||||
|
return false;
|
||||||
|
if (contentType == null) {
|
||||||
|
if (other.contentType != null)
|
||||||
|
return false;
|
||||||
|
} else if (!contentType.equals(other.contentType))
|
||||||
|
return false;
|
||||||
|
if (!Arrays.equals(hash, other.hash))
|
||||||
|
return false;
|
||||||
|
if (lastModified == null) {
|
||||||
|
if (other.lastModified != null)
|
||||||
|
return false;
|
||||||
|
} else if (!lastModified.equals(other.lastModified))
|
||||||
|
return false;
|
||||||
|
if (metadata == null) {
|
||||||
|
if (other.metadata != null)
|
||||||
|
return false;
|
||||||
|
} else if (!metadata.equals(other.metadata))
|
||||||
|
return false;
|
||||||
|
if (name == null) {
|
||||||
|
if (other.name != null)
|
||||||
|
return false;
|
||||||
|
} else if (!name.equals(other.name))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int compareTo(ObjectInfo o) {
|
||||||
|
return (this == o) ? 0 : getName().compareTo(o.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLastModified(DateTime lastModified) {
|
||||||
|
this.lastModified = lastModified;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
package org.jclouds.rackspace.cloudfiles.functions;
|
||||||
|
|
||||||
|
import org.jclouds.rackspace.cloudfiles.domain.CFObject;
|
||||||
|
|
||||||
|
import com.google.common.base.Function;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Adrian Cole
|
||||||
|
*/
|
||||||
|
public class ObjectName implements Function<Object, String> {
|
||||||
|
|
||||||
|
public String apply(Object from) {
|
||||||
|
return ((CFObject) from).getInfo().getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,101 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.com>
|
||||||
|
*
|
||||||
|
* ====================================================================
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
* ====================================================================
|
||||||
|
*/
|
||||||
|
package org.jclouds.rackspace.cloudfiles.functions;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.ws.rs.core.HttpHeaders;
|
||||||
|
|
||||||
|
import org.jclouds.blobstore.functions.ParseSystemAndUserMetadataFromHeaders;
|
||||||
|
import org.jclouds.http.HttpException;
|
||||||
|
import org.jclouds.http.HttpResponse;
|
||||||
|
import org.jclouds.rackspace.cloudfiles.domain.CFObject;
|
||||||
|
import org.jclouds.rest.InvocationContext;
|
||||||
|
import org.jclouds.rest.internal.GeneratedHttpRequest;
|
||||||
|
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
import com.google.common.base.Function;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses response headers and creates a new CFObject from them and the HTTP content.
|
||||||
|
*
|
||||||
|
* @see ParseMetadataFromHeaders
|
||||||
|
* @author Adrian Cole
|
||||||
|
*/
|
||||||
|
public class ParseObjectFromHeadersAndHttpContent implements Function<HttpResponse, CFObject>,
|
||||||
|
InvocationContext {
|
||||||
|
|
||||||
|
private final ParseObjectMetadataFromHeaders infoParser;
|
||||||
|
private final CFObject.Factory objectProvider;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public ParseObjectFromHeadersAndHttpContent(ParseObjectMetadataFromHeaders infoParser,
|
||||||
|
CFObject.Factory objectProvider) {
|
||||||
|
this.infoParser = infoParser;
|
||||||
|
this.objectProvider = objectProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* First, calls {@link ParseSystemAndUserMetadataFromHeaders}.
|
||||||
|
*
|
||||||
|
* Then, sets the object size based on the Content-Length header and adds the content to the
|
||||||
|
* {@link CFObject} result.
|
||||||
|
*
|
||||||
|
* @throws org.jclouds.http.HttpException
|
||||||
|
*/
|
||||||
|
public CFObject apply(HttpResponse from) {
|
||||||
|
CFObject object = objectProvider.create(infoParser.apply(from));
|
||||||
|
addAllHeadersTo(from, object);
|
||||||
|
object.setData(from.getContent());
|
||||||
|
attemptToParseSizeAndRangeFromHeaders(from, object);
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
void attemptToParseSizeAndRangeFromHeaders(HttpResponse from, CFObject object)
|
||||||
|
throws HttpException {
|
||||||
|
String contentLength = from.getFirstHeaderOrNull(HttpHeaders.CONTENT_LENGTH);
|
||||||
|
String contentRange = from.getFirstHeaderOrNull("Content-Range");
|
||||||
|
|
||||||
|
if (contentLength != null) {
|
||||||
|
object.setContentLength(Long.parseLong(contentLength));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (contentRange == null && contentLength != null) {
|
||||||
|
object.getInfo().setBytes(object.getContentLength());
|
||||||
|
} else if (contentRange != null) {
|
||||||
|
object.getInfo().setBytes(
|
||||||
|
Long.parseLong(contentRange.substring(contentRange.lastIndexOf('/') + 1)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
void addAllHeadersTo(HttpResponse from, CFObject object) {
|
||||||
|
object.getAllHeaders().putAll(from.getHeaders());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setContext(GeneratedHttpRequest<?> request) {
|
||||||
|
infoParser.setContext(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -33,14 +33,12 @@ import java.lang.reflect.Type;
|
||||||
import java.util.SortedSet;
|
import java.util.SortedSet;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Provider;
|
|
||||||
|
|
||||||
import org.jclouds.blobstore.domain.BlobMetadata;
|
|
||||||
import org.jclouds.blobstore.domain.BoundedSortedSet;
|
import org.jclouds.blobstore.domain.BoundedSortedSet;
|
||||||
import org.jclouds.blobstore.domain.MutableBlobMetadata;
|
|
||||||
import org.jclouds.blobstore.domain.internal.BoundedTreeSet;
|
import org.jclouds.blobstore.domain.internal.BoundedTreeSet;
|
||||||
import org.jclouds.http.HttpUtils;
|
import org.jclouds.http.HttpUtils;
|
||||||
import org.jclouds.http.functions.ParseJson;
|
import org.jclouds.http.functions.ParseJson;
|
||||||
|
import org.jclouds.rackspace.cloudfiles.domain.ObjectInfo;
|
||||||
import org.jclouds.rackspace.cloudfiles.options.ListContainerOptions;
|
import org.jclouds.rackspace.cloudfiles.options.ListContainerOptions;
|
||||||
import org.jclouds.rest.InvocationContext;
|
import org.jclouds.rest.InvocationContext;
|
||||||
import org.jclouds.rest.internal.GeneratedHttpRequest;
|
import org.jclouds.rest.internal.GeneratedHttpRequest;
|
||||||
|
@ -53,39 +51,103 @@ import com.google.gson.Gson;
|
||||||
import com.google.gson.reflect.TypeToken;
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This parses {@link BlobMetadata} from a gson string.
|
* This parses {@link ObjectInfo} from a gson string.
|
||||||
*
|
*
|
||||||
* @author Adrian Cole
|
* @author Adrian Cole
|
||||||
*/
|
*/
|
||||||
public class ParseBlobMetadataListFromJsonResponse extends
|
public class ParseObjectInfoListFromJsonResponse extends ParseJson<BoundedSortedSet<ObjectInfo>>
|
||||||
ParseJson<BoundedSortedSet<BlobMetadata>> implements InvocationContext {
|
implements InvocationContext {
|
||||||
|
|
||||||
private final Provider<MutableBlobMetadata> metadataFactory;
|
|
||||||
private GeneratedHttpRequest<?> request;
|
private GeneratedHttpRequest<?> request;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public ParseBlobMetadataListFromJsonResponse(Provider<MutableBlobMetadata> metadataFactory,
|
public ParseObjectInfoListFromJsonResponse(Gson gson) {
|
||||||
Gson gson) {
|
|
||||||
super(gson);
|
super(gson);
|
||||||
this.metadataFactory = metadataFactory;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class CloudFilesMetadata implements Comparable<CloudFilesMetadata> {
|
public static class ObjectInfoImpl implements ObjectInfo {
|
||||||
public CloudFilesMetadata() {
|
|
||||||
}
|
|
||||||
|
|
||||||
String name;
|
String name;
|
||||||
String hash;
|
String hash;
|
||||||
long bytes;
|
long bytes;
|
||||||
String content_type;
|
String content_type;
|
||||||
DateTime last_modified;
|
DateTime last_modified;
|
||||||
|
|
||||||
public int compareTo(CloudFilesMetadata o) {
|
public int compareTo(ObjectInfoImpl o) {
|
||||||
return (this == o) ? 0 : name.compareTo(o.name);
|
return (this == o) ? 0 : name.compareTo(o.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Long getBytes() {
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getContentType() {
|
||||||
|
return content_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getHash() {
|
||||||
|
return HttpUtils.fromHexString(hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DateTime getLastModified() {
|
||||||
|
return last_modified;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
final int prime = 31;
|
||||||
|
int result = 1;
|
||||||
|
result = prime * result + (int) (bytes ^ (bytes >>> 32));
|
||||||
|
result = prime * result + ((content_type == null) ? 0 : content_type.hashCode());
|
||||||
|
result = prime * result + ((hash == null) ? 0 : hash.hashCode());
|
||||||
|
result = prime * result + ((last_modified == null) ? 0 : last_modified.hashCode());
|
||||||
|
result = prime * result + ((name == null) ? 0 : name.hashCode());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj)
|
||||||
|
return true;
|
||||||
|
if (obj == null)
|
||||||
|
return false;
|
||||||
|
if (getClass() != obj.getClass())
|
||||||
|
return false;
|
||||||
|
ObjectInfoImpl other = (ObjectInfoImpl) obj;
|
||||||
|
if (bytes != other.bytes)
|
||||||
|
return false;
|
||||||
|
if (content_type == null) {
|
||||||
|
if (other.content_type != null)
|
||||||
|
return false;
|
||||||
|
} else if (!content_type.equals(other.content_type))
|
||||||
|
return false;
|
||||||
|
if (hash == null) {
|
||||||
|
if (other.hash != null)
|
||||||
|
return false;
|
||||||
|
} else if (!hash.equals(other.hash))
|
||||||
|
return false;
|
||||||
|
if (last_modified == null) {
|
||||||
|
if (other.last_modified != null)
|
||||||
|
return false;
|
||||||
|
} else if (!last_modified.equals(other.last_modified))
|
||||||
|
return false;
|
||||||
|
if (name == null) {
|
||||||
|
if (other.name != null)
|
||||||
|
return false;
|
||||||
|
} else if (!name.equals(other.name))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int compareTo(ObjectInfo o) {
|
||||||
|
return (this == o) ? 0 : getName().compareTo(o.getName());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public BoundedSortedSet<BlobMetadata> apply(InputStream stream) {
|
public BoundedSortedSet<ObjectInfo> apply(InputStream stream) {
|
||||||
checkState(request != null, "request should be initialized at this point");
|
checkState(request != null, "request should be initialized at this point");
|
||||||
checkState(request.getArgs() != null, "request.getArgs() should be initialized at this point");
|
checkState(request.getArgs() != null, "request.getArgs() should be initialized at this point");
|
||||||
checkArgument(request.getArgs()[0] instanceof String, "arg[0] must be a container name");
|
checkArgument(request.getArgs()[0] instanceof String, "arg[0] must be a container name");
|
||||||
|
@ -94,28 +156,21 @@ public class ParseBlobMetadataListFromJsonResponse extends
|
||||||
ListContainerOptions[] optionsList = (ListContainerOptions[]) request.getArgs()[1];
|
ListContainerOptions[] optionsList = (ListContainerOptions[]) request.getArgs()[1];
|
||||||
ListContainerOptions options = optionsList.length > 0 ? optionsList[0]
|
ListContainerOptions options = optionsList.length > 0 ? optionsList[0]
|
||||||
: ListContainerOptions.NONE;
|
: ListContainerOptions.NONE;
|
||||||
Type listType = new TypeToken<SortedSet<CloudFilesMetadata>>() {
|
Type listType = new TypeToken<SortedSet<ObjectInfoImpl>>() {
|
||||||
}.getType();
|
}.getType();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
SortedSet<CloudFilesMetadata> list = gson.fromJson(new InputStreamReader(stream, "UTF-8"),
|
SortedSet<ObjectInfoImpl> list = gson.fromJson(new InputStreamReader(stream, "UTF-8"),
|
||||||
listType);
|
listType);
|
||||||
SortedSet<BlobMetadata> returnVal = Sets.newTreeSet(Iterables.transform(list,
|
SortedSet<ObjectInfo> returnVal = Sets.newTreeSet(Iterables.transform(list,
|
||||||
new Function<CloudFilesMetadata, BlobMetadata>() {
|
new Function<ObjectInfoImpl, ObjectInfo>() {
|
||||||
public BlobMetadata apply(CloudFilesMetadata from) {
|
public ObjectInfo apply(ObjectInfoImpl from) {
|
||||||
MutableBlobMetadata metadata = metadataFactory.get();
|
return from;
|
||||||
metadata.setName(from.name);
|
|
||||||
metadata.setSize(from.bytes);
|
|
||||||
metadata.setLastModified(from.last_modified);
|
|
||||||
metadata.setContentType(from.content_type);
|
|
||||||
metadata.setETag(from.hash);
|
|
||||||
metadata.setContentMD5(HttpUtils.fromHexString(from.hash));
|
|
||||||
return metadata;
|
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
boolean truncated = options.getMaxResults() == returnVal.size();
|
boolean truncated = options.getMaxResults() == returnVal.size();
|
||||||
String marker = truncated ? returnVal.last().getName() : null;
|
String marker = truncated ? returnVal.last().getName() : null;
|
||||||
return new BoundedTreeSet<BlobMetadata>(returnVal, options.getPath(), marker, options
|
return new BoundedTreeSet<ObjectInfo>(returnVal, options.getPath(), marker, options
|
||||||
.getMaxResults(), truncated);
|
.getMaxResults(), truncated);
|
||||||
|
|
||||||
} catch (UnsupportedEncodingException e) {
|
} catch (UnsupportedEncodingException e) {
|
|
@ -24,43 +24,50 @@
|
||||||
package org.jclouds.rackspace.cloudfiles.functions;
|
package org.jclouds.rackspace.cloudfiles.functions;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Named;
|
|
||||||
import javax.inject.Provider;
|
|
||||||
|
|
||||||
import org.jclouds.blobstore.domain.MutableBlobMetadata;
|
import org.jclouds.blobstore.domain.BlobMetadata;
|
||||||
import org.jclouds.blobstore.functions.ParseSystemAndUserMetadataFromHeaders;
|
import org.jclouds.blobstore.functions.ParseSystemAndUserMetadataFromHeaders;
|
||||||
import org.jclouds.http.HttpResponse;
|
import org.jclouds.http.HttpResponse;
|
||||||
import org.jclouds.http.HttpUtils;
|
import org.jclouds.http.HttpUtils;
|
||||||
import org.jclouds.rackspace.cloudfiles.reference.CloudFilesConstants;
|
import org.jclouds.rackspace.cloudfiles.blobstore.functions.BlobToObjectInfo;
|
||||||
import org.jclouds.util.DateService;
|
import org.jclouds.rackspace.cloudfiles.domain.MutableObjectInfoWithMetadata;
|
||||||
|
import org.jclouds.rest.InvocationContext;
|
||||||
|
import org.jclouds.rest.internal.GeneratedHttpRequest;
|
||||||
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.base.Function;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* This parses @{link {@link MutableObjectInfoWithMetadata} from HTTP headers.
|
||||||
|
*
|
||||||
* @author Adrian Cole
|
* @author Adrian Cole
|
||||||
*/
|
*/
|
||||||
public class ParseObjectMetadataFromHeaders extends ParseSystemAndUserMetadataFromHeaders {
|
public class ParseObjectMetadataFromHeaders implements
|
||||||
|
Function<HttpResponse, MutableObjectInfoWithMetadata>, InvocationContext {
|
||||||
|
private final ParseSystemAndUserMetadataFromHeaders blobMetadataParser;
|
||||||
|
private final BlobToObjectInfo blobToObjectInfo;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public ParseObjectMetadataFromHeaders(Provider<MutableBlobMetadata> metadataFactory,
|
public ParseObjectMetadataFromHeaders(ParseSystemAndUserMetadataFromHeaders blobMetadataParser,
|
||||||
DateService dateParser,
|
BlobToObjectInfo blobToObjectMetadata) {
|
||||||
@Named(CloudFilesConstants.PROPERTY_CLOUDFILES_METADATA_PREFIX) String metadataPrefix) {
|
this.blobMetadataParser = blobMetadataParser;
|
||||||
super(metadataFactory, dateParser, metadataPrefix);
|
this.blobToObjectInfo = blobToObjectMetadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
/**
|
/**
|
||||||
* ETag == Content-MD5
|
* parses the http response headers to create a new {@link MutableObjectInfoWithMetadata} object.
|
||||||
*/
|
*/
|
||||||
protected void addETagTo(HttpResponse from, MutableBlobMetadata metadata) {
|
public MutableObjectInfoWithMetadata apply(HttpResponse from) {
|
||||||
super.addETagTo(from, metadata);
|
BlobMetadata base = blobMetadataParser.apply(from);
|
||||||
if (metadata.getETag() == null) {
|
MutableObjectInfoWithMetadata to = blobToObjectInfo.apply(base);
|
||||||
// etag comes back incorrect case
|
String eTagHeader = from.getFirstHeaderOrNull("Etag");
|
||||||
String eTagHeader = from.getFirstHeaderOrNull("Etag");
|
if (eTagHeader != null) {
|
||||||
if (eTagHeader != null) {
|
to.setHash(HttpUtils.fromHexString(eTagHeader.replaceAll("\"", "")));
|
||||||
metadata.setETag(eTagHeader);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
metadata.setContentMD5(HttpUtils.fromHexString(metadata.getETag()));
|
return to;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setContext(GeneratedHttpRequest<?> request) {
|
||||||
|
blobMetadataParser.setContext(request);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -41,8 +41,6 @@ import java.util.concurrent.TimeoutException;
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.jclouds.blobstore.ContainerNotFoundException;
|
import org.jclouds.blobstore.ContainerNotFoundException;
|
||||||
import org.jclouds.blobstore.KeyNotFoundException;
|
import org.jclouds.blobstore.KeyNotFoundException;
|
||||||
import org.jclouds.blobstore.domain.Blob;
|
|
||||||
import org.jclouds.blobstore.domain.BlobMetadata;
|
|
||||||
import org.jclouds.blobstore.domain.BoundedSortedSet;
|
import org.jclouds.blobstore.domain.BoundedSortedSet;
|
||||||
import org.jclouds.blobstore.integration.internal.BaseBlobStoreIntegrationTest;
|
import org.jclouds.blobstore.integration.internal.BaseBlobStoreIntegrationTest;
|
||||||
import org.jclouds.http.HttpResponseException;
|
import org.jclouds.http.HttpResponseException;
|
||||||
|
@ -50,8 +48,11 @@ import org.jclouds.http.HttpUtils;
|
||||||
import org.jclouds.http.options.GetOptions;
|
import org.jclouds.http.options.GetOptions;
|
||||||
import org.jclouds.logging.log4j.config.Log4JLoggingModule;
|
import org.jclouds.logging.log4j.config.Log4JLoggingModule;
|
||||||
import org.jclouds.rackspace.cloudfiles.domain.AccountMetadata;
|
import org.jclouds.rackspace.cloudfiles.domain.AccountMetadata;
|
||||||
|
import org.jclouds.rackspace.cloudfiles.domain.CFObject;
|
||||||
import org.jclouds.rackspace.cloudfiles.domain.ContainerCDNMetadata;
|
import org.jclouds.rackspace.cloudfiles.domain.ContainerCDNMetadata;
|
||||||
import org.jclouds.rackspace.cloudfiles.domain.ContainerMetadata;
|
import org.jclouds.rackspace.cloudfiles.domain.ContainerMetadata;
|
||||||
|
import org.jclouds.rackspace.cloudfiles.domain.MutableObjectInfoWithMetadata;
|
||||||
|
import org.jclouds.rackspace.cloudfiles.domain.ObjectInfo;
|
||||||
import org.jclouds.rackspace.cloudfiles.options.ListCdnContainerOptions;
|
import org.jclouds.rackspace.cloudfiles.options.ListCdnContainerOptions;
|
||||||
import org.jclouds.rackspace.cloudfiles.options.ListContainerOptions;
|
import org.jclouds.rackspace.cloudfiles.options.ListContainerOptions;
|
||||||
import org.testng.annotations.BeforeGroups;
|
import org.testng.annotations.BeforeGroups;
|
||||||
|
@ -283,10 +284,10 @@ public class CloudFilesClientLiveTest {
|
||||||
|
|
||||||
String data = "foo";
|
String data = "foo";
|
||||||
|
|
||||||
connection.putObject(containerName, newBlob(data, "foo")).get(10, TimeUnit.SECONDS);
|
connection.putObject(containerName, newCFObject(data, "foo")).get(10, TimeUnit.SECONDS);
|
||||||
connection.putObject(containerName, newBlob(data, "path/bar")).get(10, TimeUnit.SECONDS);
|
connection.putObject(containerName, newCFObject(data, "path/bar")).get(10, TimeUnit.SECONDS);
|
||||||
|
|
||||||
BoundedSortedSet<BlobMetadata> container = connection.listObjects(containerName,
|
BoundedSortedSet<ObjectInfo> container = connection.listObjects(containerName,
|
||||||
underPath("")).get(10, TimeUnit.SECONDS);
|
underPath("")).get(10, TimeUnit.SECONDS);
|
||||||
assert !container.isTruncated();
|
assert !container.isTruncated();
|
||||||
assertEquals(container.size(), 1);
|
assertEquals(container.size(), 1);
|
||||||
|
@ -314,36 +315,36 @@ public class CloudFilesClientLiveTest {
|
||||||
// Test PUT with string data, ETag hash, and a piece of metadata
|
// Test PUT with string data, ETag hash, and a piece of metadata
|
||||||
String data = "Here is my data";
|
String data = "Here is my data";
|
||||||
String key = "object";
|
String key = "object";
|
||||||
Blob object = newBlob(data, key);
|
CFObject object = newCFObject(data, key);
|
||||||
byte[] md5 = object.getMetadata().getContentMD5();
|
byte[] md5 = object.getInfo().getHash();
|
||||||
String newEtag = connection.putObject(containerName, object).get(10, TimeUnit.SECONDS);
|
String newEtag = connection.putObject(containerName, object).get(10, TimeUnit.SECONDS);
|
||||||
assertEquals(HttpUtils.toHexString(md5), HttpUtils.toHexString(object.getMetadata()
|
assertEquals(HttpUtils.toHexString(md5), HttpUtils.toHexString(object.getInfo()
|
||||||
.getContentMD5()));
|
.getHash()));
|
||||||
|
|
||||||
// Test HEAD of missing object
|
// Test HEAD of missing object
|
||||||
try {
|
try {
|
||||||
connection.getObjectMetadata(containerName, "non-existent-object");
|
connection.getObjectInfo(containerName, "non-existent-object");
|
||||||
assert false;
|
assert false;
|
||||||
} catch (KeyNotFoundException e) {
|
} catch (KeyNotFoundException e) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test HEAD of object
|
// Test HEAD of object
|
||||||
BlobMetadata metadata = connection.getObjectMetadata(containerName, object.getMetadata()
|
MutableObjectInfoWithMetadata metadata = connection.getObjectInfo(containerName, object.getInfo()
|
||||||
.getName());
|
.getName());
|
||||||
// TODO assertEquals(metadata.getName(), object.getMetadata().getName());
|
// TODO assertEquals(metadata.getName(), object.getMetadata().getName());
|
||||||
assertEquals(metadata.getSize(), new Long(data.length()));
|
assertEquals(metadata.getBytes(), new Long(data.length()));
|
||||||
assertEquals(metadata.getContentType(), "text/plain");
|
assertEquals(metadata.getContentType(), "text/plain");
|
||||||
assertEquals(HttpUtils.toHexString(md5), HttpUtils.toHexString(object.getMetadata()
|
assertEquals(HttpUtils.toHexString(md5), HttpUtils.toHexString(object.getInfo()
|
||||||
.getContentMD5()));
|
.getHash()));
|
||||||
assertEquals(metadata.getETag(), newEtag);
|
assertEquals(metadata.getHash(), HttpUtils.fromHexString(newEtag));
|
||||||
assertEquals(metadata.getUserMetadata().entrySet().size(), 1);
|
assertEquals(metadata.getMetadata().entrySet().size(), 1);
|
||||||
assertEquals(metadata.getUserMetadata().get("metadata"), "metadata-value");
|
assertEquals(metadata.getMetadata().get("metadata"), "metadata-value");
|
||||||
|
|
||||||
// // Test POST to update object's metadata
|
// // Test POST to update object's metadata
|
||||||
Map<String, String> userMetadata = Maps.newHashMap();
|
Map<String, String> userMetadata = Maps.newHashMap();
|
||||||
userMetadata.put("New-Metadata-1", "value-1");
|
userMetadata.put("New-Metadata-1", "value-1");
|
||||||
userMetadata.put("New-Metadata-2", "value-2");
|
userMetadata.put("New-Metadata-2", "value-2");
|
||||||
assertTrue(connection.setObjectMetadata(containerName, object.getMetadata().getName(),
|
assertTrue(connection.setObjectMetadata(containerName, object.getInfo().getName(),
|
||||||
userMetadata));
|
userMetadata));
|
||||||
|
|
||||||
// Test GET of missing object
|
// Test GET of missing object
|
||||||
|
@ -353,23 +354,23 @@ public class CloudFilesClientLiveTest {
|
||||||
} catch (KeyNotFoundException e) {
|
} catch (KeyNotFoundException e) {
|
||||||
}
|
}
|
||||||
// Test GET of object (including updated metadata)
|
// Test GET of object (including updated metadata)
|
||||||
Blob getBlob = connection.getObject(containerName, object.getMetadata().getName()).get(120,
|
CFObject getBlob = connection.getObject(containerName, object.getInfo().getName()).get(120,
|
||||||
TimeUnit.SECONDS);
|
TimeUnit.SECONDS);
|
||||||
assertEquals(IOUtils.toString((InputStream) getBlob.getData()), data);
|
assertEquals(IOUtils.toString((InputStream) getBlob.getData()), data);
|
||||||
// TODO assertEquals(getBlob.getName(), object.getMetadata().getName());
|
// TODO assertEquals(getBlob.getName(), object.getMetadata().getName());
|
||||||
assertEquals(getBlob.getContentLength(), new Long(data.length()));
|
assertEquals(getBlob.getContentLength(), new Long(data.length()));
|
||||||
assertEquals(getBlob.getMetadata().getContentType(), "text/plain");
|
assertEquals(getBlob.getInfo().getContentType(), "text/plain");
|
||||||
assertEquals(HttpUtils.toHexString(md5), HttpUtils.toHexString(getBlob.getMetadata()
|
assertEquals(HttpUtils.toHexString(md5), HttpUtils.toHexString(getBlob.getInfo()
|
||||||
.getContentMD5()));
|
.getHash()));
|
||||||
assertEquals(newEtag, getBlob.getMetadata().getETag());
|
assertEquals(HttpUtils.fromHexString(newEtag), getBlob.getInfo().getHash());
|
||||||
assertEquals(getBlob.getMetadata().getUserMetadata().entrySet().size(), 2);
|
assertEquals(getBlob.getInfo().getMetadata().entrySet().size(), 2);
|
||||||
assertEquals(getBlob.getMetadata().getUserMetadata().get("new-metadata-1"), "value-1");
|
assertEquals(getBlob.getInfo().getMetadata().get("new-metadata-1"), "value-1");
|
||||||
assertEquals(getBlob.getMetadata().getUserMetadata().get("new-metadata-2"), "value-2");
|
assertEquals(getBlob.getInfo().getMetadata().get("new-metadata-2"), "value-2");
|
||||||
|
|
||||||
// Test PUT with invalid ETag (as if object's data was corrupted in transit)
|
// Test PUT with invalid ETag (as if object's data was corrupted in transit)
|
||||||
String correctEtag = newEtag;
|
String correctEtag = newEtag;
|
||||||
String incorrectEtag = "0" + correctEtag.substring(1);
|
String incorrectEtag = "0" + correctEtag.substring(1);
|
||||||
object.getMetadata().setETag(incorrectEtag);
|
object.getInfo().setHash(HttpUtils.fromHexString(incorrectEtag));
|
||||||
try {
|
try {
|
||||||
connection.putObject(containerName, object).get(10, TimeUnit.SECONDS);
|
connection.putObject(containerName, object).get(10, TimeUnit.SECONDS);
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
|
@ -379,17 +380,17 @@ public class CloudFilesClientLiveTest {
|
||||||
|
|
||||||
// Test PUT chunked/streamed upload with data of "unknown" length
|
// Test PUT chunked/streamed upload with data of "unknown" length
|
||||||
ByteArrayInputStream bais = new ByteArrayInputStream(data.getBytes("UTF-8"));
|
ByteArrayInputStream bais = new ByteArrayInputStream(data.getBytes("UTF-8"));
|
||||||
Blob blob = connection.newBlob();
|
CFObject blob = connection.newCFObject();
|
||||||
blob.getMetadata().setName("chunked-object");
|
blob.getInfo().setName("chunked-object");
|
||||||
object.setData(bais);
|
blob.setData(bais);
|
||||||
newEtag = connection.putObject(containerName, object).get(10, TimeUnit.SECONDS);
|
newEtag = connection.putObject(containerName, blob).get(10, TimeUnit.SECONDS);
|
||||||
assertEquals(HttpUtils.toHexString(md5), HttpUtils.toHexString(getBlob.getMetadata()
|
assertEquals(HttpUtils.toHexString(md5), HttpUtils.toHexString(getBlob.getInfo()
|
||||||
.getContentMD5()));
|
.getHash()));
|
||||||
|
|
||||||
// Test GET with options
|
// Test GET with options
|
||||||
// Non-matching ETag
|
// Non-matching ETag
|
||||||
try {
|
try {
|
||||||
connection.getObject(containerName, object.getMetadata().getName(),
|
connection.getObject(containerName, object.getInfo().getName(),
|
||||||
GetOptions.Builder.ifETagDoesntMatch(newEtag)).get(120, TimeUnit.SECONDS);
|
GetOptions.Builder.ifETagDoesntMatch(newEtag)).get(120, TimeUnit.SECONDS);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertEquals(e.getCause().getClass(), HttpResponseException.class);
|
assertEquals(e.getCause().getClass(), HttpResponseException.class);
|
||||||
|
@ -397,10 +398,10 @@ public class CloudFilesClientLiveTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Matching ETag
|
// Matching ETag
|
||||||
getBlob = connection.getObject(containerName, object.getMetadata().getName(),
|
getBlob = connection.getObject(containerName, object.getInfo().getName(),
|
||||||
GetOptions.Builder.ifETagMatches(newEtag)).get(120, TimeUnit.SECONDS);
|
GetOptions.Builder.ifETagMatches(newEtag)).get(120, TimeUnit.SECONDS);
|
||||||
assertEquals(getBlob.getMetadata().getETag(), newEtag);
|
assertEquals(getBlob.getInfo().getHash(), HttpUtils.fromHexString(newEtag));
|
||||||
getBlob = connection.getObject(containerName, object.getMetadata().getName(),
|
getBlob = connection.getObject(containerName, object.getInfo().getName(),
|
||||||
GetOptions.Builder.startAt(8)).get(120, TimeUnit.SECONDS);
|
GetOptions.Builder.startAt(8)).get(120, TimeUnit.SECONDS);
|
||||||
assertEquals(IOUtils.toString((InputStream) getBlob.getData()), data.substring(8));
|
assertEquals(IOUtils.toString((InputStream) getBlob.getData()), data.substring(8));
|
||||||
|
|
||||||
|
@ -410,14 +411,13 @@ public class CloudFilesClientLiveTest {
|
||||||
assertTrue(connection.deleteContainerIfEmpty(containerName).get(10, TimeUnit.SECONDS));
|
assertTrue(connection.deleteContainerIfEmpty(containerName).get(10, TimeUnit.SECONDS));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Blob newBlob(String data, String key) throws IOException {
|
private CFObject newCFObject(String data, String key) throws IOException {
|
||||||
Blob object = connection.newBlob();
|
CFObject object = connection.newCFObject();
|
||||||
object.getMetadata().setName(key);
|
object.getInfo().setName(key);
|
||||||
object.setData(data);
|
object.setData(data);
|
||||||
object.setContentLength(data.length());
|
|
||||||
object.generateMD5();
|
object.generateMD5();
|
||||||
object.getMetadata().setContentType("text/plain");
|
object.getInfo().setContentType("text/plain");
|
||||||
object.getMetadata().getUserMetadata().put("Metadata", "metadata-value");
|
object.getInfo().getMetadata().put("Metadata", "metadata-value");
|
||||||
return object;
|
return object;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,40 +31,54 @@ import java.net.URI;
|
||||||
import javax.ws.rs.core.HttpHeaders;
|
import javax.ws.rs.core.HttpHeaders;
|
||||||
import javax.ws.rs.core.MediaType;
|
import javax.ws.rs.core.MediaType;
|
||||||
|
|
||||||
import org.jclouds.blobstore.config.BlobStoreObjectModule;
|
|
||||||
import org.jclouds.blobstore.domain.Blob;
|
import org.jclouds.blobstore.domain.Blob;
|
||||||
import org.jclouds.blobstore.domain.Blob.Factory;
|
import org.jclouds.blobstore.domain.Blob.Factory;
|
||||||
import org.jclouds.http.HttpRequest;
|
import org.jclouds.http.HttpRequest;
|
||||||
|
import org.jclouds.rackspace.cloudfiles.CloudFilesContextBuilder;
|
||||||
|
import org.jclouds.rackspace.cloudfiles.CloudFilesPropertiesBuilder;
|
||||||
|
import org.jclouds.rackspace.cloudfiles.blobstore.functions.BlobToObject;
|
||||||
|
import org.jclouds.rackspace.cloudfiles.domain.CFObject;
|
||||||
import org.testng.annotations.Test;
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
import com.google.inject.Guice;
|
import com.google.inject.Injector;
|
||||||
|
import com.google.inject.Key;
|
||||||
|
import com.google.inject.Provider;
|
||||||
|
import com.google.inject.TypeLiteral;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests parsing of a request
|
* Tests parsing of a request
|
||||||
*
|
*
|
||||||
* @author Adrian Cole
|
* @author Adrian Cole
|
||||||
*/
|
*/
|
||||||
@Test(testName = "cloudfiles.BindCFObjectAsEntityTest")
|
@Test(testName = "cloudfiles.BindCFObjectToEntityTest")
|
||||||
public class BindCFObjectAsEntityTest {
|
public class BindCFObjectToEntityTest {
|
||||||
private Factory blobProvider;
|
private Factory blobProvider;
|
||||||
|
private Provider<BindCFObjectToEntity> binderProvider;
|
||||||
|
private BlobToObject blob2Object;
|
||||||
|
|
||||||
public BindCFObjectAsEntityTest() {
|
public BindCFObjectToEntityTest() {
|
||||||
blobProvider = Guice.createInjector(new BlobStoreObjectModule()).getInstance(
|
Injector injector = new CloudFilesContextBuilder(new CloudFilesPropertiesBuilder("id",
|
||||||
Blob.Factory.class);
|
"secret").build()).buildInjector();
|
||||||
|
|
||||||
|
blobProvider = injector.getInstance(Blob.Factory.class);
|
||||||
|
binderProvider = injector.getInstance(Key
|
||||||
|
.get(new TypeLiteral<Provider<BindCFObjectToEntity>>() {
|
||||||
|
}));
|
||||||
|
blob2Object = injector.getInstance(BlobToObject.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Blob testBlob() {
|
public CFObject testBlob() {
|
||||||
|
|
||||||
Blob TEST_BLOB = blobProvider.create(null);
|
Blob TEST_BLOB = blobProvider.create(null);
|
||||||
TEST_BLOB.getMetadata().setName("hello");
|
TEST_BLOB.getMetadata().setName("hello");
|
||||||
TEST_BLOB.setData("hello");
|
TEST_BLOB.setData("hello");
|
||||||
TEST_BLOB.getMetadata().setContentType(MediaType.TEXT_PLAIN);
|
TEST_BLOB.getMetadata().setContentType(MediaType.TEXT_PLAIN);
|
||||||
return TEST_BLOB;
|
return blob2Object.apply(TEST_BLOB);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testNormal() throws IOException {
|
public void testNormal() throws IOException {
|
||||||
|
|
||||||
BindCFObjectAsEntity binder = new BindCFObjectAsEntity("test");
|
BindCFObjectToEntity binder = binderProvider.get();
|
||||||
|
|
||||||
HttpRequest request = new HttpRequest("GET", URI.create("http://localhost:8001"));
|
HttpRequest request = new HttpRequest("GET", URI.create("http://localhost:8001"));
|
||||||
binder.bindToRequest(request, testBlob());
|
binder.bindToRequest(request, testBlob());
|
||||||
|
@ -76,9 +90,9 @@ public class BindCFObjectAsEntityTest {
|
||||||
|
|
||||||
public void testMD5InHex() throws IOException {
|
public void testMD5InHex() throws IOException {
|
||||||
|
|
||||||
BindCFObjectAsEntity binder = new BindCFObjectAsEntity("test");
|
BindCFObjectToEntity binder = binderProvider.get();
|
||||||
|
|
||||||
Blob blob = testBlob();
|
CFObject blob = testBlob();
|
||||||
blob.generateMD5();
|
blob.generateMD5();
|
||||||
HttpRequest request = new HttpRequest("GET", URI.create("http://localhost:8001"));
|
HttpRequest request = new HttpRequest("GET", URI.create("http://localhost:8001"));
|
||||||
binder.bindToRequest(request, blob);
|
binder.bindToRequest(request, blob);
|
|
@ -31,10 +31,9 @@ import static org.testng.Assert.assertEquals;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.jclouds.blobstore.domain.BlobMetadata;
|
|
||||||
import org.jclouds.blobstore.domain.MutableBlobMetadata;
|
|
||||||
import org.jclouds.http.HttpUtils;
|
|
||||||
import org.jclouds.http.functions.config.ParserModule;
|
import org.jclouds.http.functions.config.ParserModule;
|
||||||
|
import org.jclouds.rackspace.cloudfiles.domain.ObjectInfo;
|
||||||
|
import org.jclouds.rackspace.cloudfiles.functions.ParseObjectInfoListFromJsonResponse.ObjectInfoImpl;
|
||||||
import org.jclouds.rackspace.cloudfiles.options.ListContainerOptions;
|
import org.jclouds.rackspace.cloudfiles.options.ListContainerOptions;
|
||||||
import org.jclouds.rest.internal.GeneratedHttpRequest;
|
import org.jclouds.rest.internal.GeneratedHttpRequest;
|
||||||
import org.joda.time.DateTime;
|
import org.joda.time.DateTime;
|
||||||
|
@ -56,30 +55,28 @@ public class ParseBlobMetadataListFromJsonResponseTest {
|
||||||
|
|
||||||
public void testApplyInputStream() {
|
public void testApplyInputStream() {
|
||||||
InputStream is = getClass().getResourceAsStream("/test_list_container.json");
|
InputStream is = getClass().getResourceAsStream("/test_list_container.json");
|
||||||
List<BlobMetadata> expects = Lists.newArrayList();
|
List<ObjectInfo> expects = Lists.newArrayList();
|
||||||
MutableBlobMetadata one = i.getInstance(MutableBlobMetadata.class);
|
ObjectInfoImpl one = i.getInstance(ObjectInfoImpl.class);
|
||||||
one.setName("test_obj_1");
|
one.name = "test_obj_1";
|
||||||
one.setETag("4281c348eaf83e70ddce0e07221c3d28");
|
one.hash = "4281c348eaf83e70ddce0e07221c3d28";
|
||||||
one.setContentMD5(HttpUtils.fromHexString(one.getETag()));
|
one.bytes = 14l;
|
||||||
one.setSize(14l);
|
one.content_type = "application/octet-stream";
|
||||||
one.setContentType("application/octet-stream");
|
one.last_modified = new DateTime("2009-02-03T05:26:32.612278");
|
||||||
one.setLastModified(new DateTime("2009-02-03T05:26:32.612278"));
|
|
||||||
expects.add(one);
|
expects.add(one);
|
||||||
MutableBlobMetadata two = i.getInstance(MutableBlobMetadata.class);
|
ObjectInfoImpl two = i.getInstance(ObjectInfoImpl.class);
|
||||||
two.setName("test_obj_2");
|
two.name = ("test_obj_2");
|
||||||
two.setETag("b039efe731ad111bc1b0ef221c3849d0");
|
two.hash = ("b039efe731ad111bc1b0ef221c3849d0");
|
||||||
two.setContentMD5(HttpUtils.fromHexString(two.getETag()));
|
two.bytes = (64l);
|
||||||
two.setSize(64l);
|
two.content_type = ("application/octet-stream");
|
||||||
two.setContentType("application/octet-stream");
|
two.last_modified =(new DateTime("2009-02-03T05:26:32.612278"));
|
||||||
two.setLastModified(new DateTime("2009-02-03T05:26:32.612278"));
|
|
||||||
expects.add(two);
|
expects.add(two);
|
||||||
GeneratedHttpRequest<?> request = createMock(GeneratedHttpRequest.class);
|
GeneratedHttpRequest<?> request = createMock(GeneratedHttpRequest.class);
|
||||||
ListContainerOptions options = new ListContainerOptions();
|
ListContainerOptions options = new ListContainerOptions();
|
||||||
expect(request.getArgs()).andReturn(
|
expect(request.getArgs()).andReturn(
|
||||||
new Object[] { "containter", new ListContainerOptions[] { options } }).atLeastOnce();
|
new Object[] { "containter", new ListContainerOptions[] { options } }).atLeastOnce();
|
||||||
replay(request);
|
replay(request);
|
||||||
ParseBlobMetadataListFromJsonResponse parser = i
|
ParseObjectInfoListFromJsonResponse parser = i
|
||||||
.getInstance(ParseBlobMetadataListFromJsonResponse.class);
|
.getInstance(ParseObjectInfoListFromJsonResponse.class);
|
||||||
parser.setContext(request);
|
parser.setContext(request);
|
||||||
assertEquals(parser.apply(is), expects);
|
assertEquals(parser.apply(is), expects);
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,12 +23,18 @@
|
||||||
*/
|
*/
|
||||||
package org.jclouds.rackspace.cloudfiles.functions;
|
package org.jclouds.rackspace.cloudfiles.functions;
|
||||||
|
|
||||||
|
import static org.easymock.EasyMock.expect;
|
||||||
|
import static org.easymock.classextension.EasyMock.createMock;
|
||||||
|
import static org.easymock.classextension.EasyMock.replay;
|
||||||
import static org.testng.Assert.assertNotNull;
|
import static org.testng.Assert.assertNotNull;
|
||||||
|
|
||||||
import org.jclouds.blobstore.domain.MutableBlobMetadata;
|
import java.net.URI;
|
||||||
|
|
||||||
|
import org.jclouds.blobstore.reference.BlobStoreConstants;
|
||||||
import org.jclouds.http.HttpResponse;
|
import org.jclouds.http.HttpResponse;
|
||||||
import org.jclouds.http.functions.config.ParserModule;
|
import org.jclouds.http.functions.config.ParserModule;
|
||||||
import org.jclouds.rackspace.cloudfiles.reference.CloudFilesConstants;
|
import org.jclouds.rackspace.cloudfiles.domain.MutableObjectInfoWithMetadata;
|
||||||
|
import org.jclouds.rest.internal.GeneratedHttpRequest;
|
||||||
import org.testng.annotations.Test;
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
import com.google.inject.AbstractModule;
|
import com.google.inject.AbstractModule;
|
||||||
|
@ -48,17 +54,24 @@ public class ParseObjectMetadataFromHeadersTest {
|
||||||
@Override
|
@Override
|
||||||
protected void configure() {
|
protected void configure() {
|
||||||
bindConstant().annotatedWith(
|
bindConstant().annotatedWith(
|
||||||
Jsr330.named(CloudFilesConstants.PROPERTY_CLOUDFILES_METADATA_PREFIX)).to("sdf");
|
Jsr330.named(BlobStoreConstants.PROPERTY_USER_METADATA_PREFIX)).to("sdf");
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
public void testEtagCaseIssue() {
|
public void testEtagCaseIssue() {
|
||||||
ParseObjectMetadataFromHeaders parser = i.getInstance(ParseObjectMetadataFromHeaders.class);
|
ParseObjectMetadataFromHeaders parser = i.getInstance(ParseObjectMetadataFromHeaders.class);
|
||||||
MutableBlobMetadata md = i.getInstance(MutableBlobMetadata.class);
|
GeneratedHttpRequest<?> request = createMock(GeneratedHttpRequest.class);
|
||||||
|
expect(request.getEndpoint()).andReturn(URI.create("http://localhost/test")).atLeastOnce();
|
||||||
|
replay(request);
|
||||||
|
parser.setContext(request);
|
||||||
HttpResponse response = new HttpResponse();
|
HttpResponse response = new HttpResponse();
|
||||||
|
response.getHeaders().put("Content-Type", "text/plain");
|
||||||
|
response.getHeaders().put("Last-Modified", "Fri, 12 Jun 2007 13:40:18 GMT");
|
||||||
|
response.getHeaders().put("Content-Length", "0");
|
||||||
|
|
||||||
response.getHeaders().put("Etag", "feb1");
|
response.getHeaders().put("Etag", "feb1");
|
||||||
parser.addETagTo(response, md);
|
MutableObjectInfoWithMetadata md = parser.apply(response);
|
||||||
assertNotNull(md.getETag());
|
assertNotNull(md.getHash());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,14 +27,15 @@ import java.util.Map;
|
||||||
import java.util.SortedSet;
|
import java.util.SortedSet;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
|
|
||||||
import org.jclouds.blobstore.domain.Blob;
|
|
||||||
import org.jclouds.blobstore.domain.BlobMetadata;
|
|
||||||
import org.jclouds.blobstore.domain.BoundedSortedSet;
|
import org.jclouds.blobstore.domain.BoundedSortedSet;
|
||||||
import org.jclouds.http.options.GetOptions;
|
import org.jclouds.http.options.GetOptions;
|
||||||
import org.jclouds.rackspace.cloudfiles.CloudFilesClient;
|
import org.jclouds.rackspace.cloudfiles.CloudFilesClient;
|
||||||
import org.jclouds.rackspace.cloudfiles.domain.AccountMetadata;
|
import org.jclouds.rackspace.cloudfiles.domain.AccountMetadata;
|
||||||
|
import org.jclouds.rackspace.cloudfiles.domain.CFObject;
|
||||||
import org.jclouds.rackspace.cloudfiles.domain.ContainerCDNMetadata;
|
import org.jclouds.rackspace.cloudfiles.domain.ContainerCDNMetadata;
|
||||||
import org.jclouds.rackspace.cloudfiles.domain.ContainerMetadata;
|
import org.jclouds.rackspace.cloudfiles.domain.ContainerMetadata;
|
||||||
|
import org.jclouds.rackspace.cloudfiles.domain.MutableObjectInfoWithMetadata;
|
||||||
|
import org.jclouds.rackspace.cloudfiles.domain.ObjectInfo;
|
||||||
import org.jclouds.rackspace.cloudfiles.options.ListCdnContainerOptions;
|
import org.jclouds.rackspace.cloudfiles.options.ListCdnContainerOptions;
|
||||||
import org.jclouds.rackspace.cloudfiles.options.ListContainerOptions;
|
import org.jclouds.rackspace.cloudfiles.options.ListContainerOptions;
|
||||||
|
|
||||||
|
@ -77,11 +78,11 @@ public class StubCloudFilesClient implements CloudFilesClient {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Future<Blob> getObject(String container, String key, GetOptions... options) {
|
public Future<CFObject> getObject(String container, String key, GetOptions... options) {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public BlobMetadata getObjectMetadata(String container, String key) {
|
public MutableObjectInfoWithMetadata getObjectInfo(String container, String key) {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,16 +94,12 @@ public class StubCloudFilesClient implements CloudFilesClient {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Future<BoundedSortedSet<BlobMetadata>> listObjects(String container,
|
public Future<BoundedSortedSet<ObjectInfo>> listObjects(String container,
|
||||||
ListContainerOptions... options) {
|
ListContainerOptions... options) {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Blob newBlob() {
|
public Future<String> putObject(String container, CFObject object) {
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Future<String> putObject(String container, Blob object) {
|
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,4 +115,8 @@ public class StubCloudFilesClient implements CloudFilesClient {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CFObject newCFObject() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue