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 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.functions.BlobName;
|
||||
import org.jclouds.blobstore.functions.ReturnVoidOnNotFoundOr404;
|
||||
import org.jclouds.blobstore.functions.ThrowContainerNotFoundOn404;
|
||||
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.rackspace.CloudFiles;
|
||||
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.CFObject;
|
||||
import org.jclouds.rackspace.cloudfiles.domain.ContainerCDNMetadata;
|
||||
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.ParseBlobFromHeadersAndHttpContent;
|
||||
import org.jclouds.rackspace.cloudfiles.functions.ParseBlobMetadataListFromJsonResponse;
|
||||
import org.jclouds.rackspace.cloudfiles.functions.ParseCdnUriFromHeaders;
|
||||
import org.jclouds.rackspace.cloudfiles.functions.ParseContainerCDNMetadataFromHeaders;
|
||||
import org.jclouds.rackspace.cloudfiles.functions.ParseContainerCDNMetadataListFromGsonResponse;
|
||||
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.ReturnTrueOn404FalseOn409;
|
||||
import org.jclouds.rackspace.cloudfiles.options.ListCdnContainerOptions;
|
||||
|
@ -91,7 +92,7 @@ import org.jclouds.rest.annotations.SkipEncoding;
|
|||
@Endpoint(CloudFiles.class)
|
||||
public interface CloudFilesClient {
|
||||
|
||||
Blob newBlob();
|
||||
CFObject newCFObject();
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
@POST
|
||||
@Path("{container}/{key}")
|
||||
@Path("{container}/{name}")
|
||||
boolean setObjectMetadata(@PathParam("container") String container,
|
||||
@PathParam("key") String key,
|
||||
@PathParam("name") String name,
|
||||
@BinderParam(BindMapToHeadersWithPrefix.class) Map<String, String> userMetadata);
|
||||
|
||||
@GET
|
||||
|
@ -200,9 +201,9 @@ public interface CloudFilesClient {
|
|||
|
||||
@GET
|
||||
@QueryParams(keys = "format", values = "json")
|
||||
@ResponseParser(ParseBlobMetadataListFromJsonResponse.class)
|
||||
@ResponseParser(ParseObjectInfoListFromJsonResponse.class)
|
||||
@Path("{container}")
|
||||
Future<BoundedSortedSet<BlobMetadata>> listObjects(@PathParam("container") String container,
|
||||
Future<BoundedSortedSet<ObjectInfo>> listObjects(@PathParam("container") String container,
|
||||
ListContainerOptions... options);
|
||||
|
||||
@HEAD
|
||||
|
@ -211,29 +212,28 @@ public interface CloudFilesClient {
|
|||
boolean containerExists(@PathParam("container") String container);
|
||||
|
||||
@PUT
|
||||
@Path("{container}/{key}")
|
||||
@Path("{container}/{name}")
|
||||
@ResponseParser(ParseETagHeader.class)
|
||||
Future<String> putObject(
|
||||
@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
|
||||
@ResponseParser(ParseBlobFromHeadersAndHttpContent.class)
|
||||
@ResponseParser(ParseObjectFromHeadersAndHttpContent.class)
|
||||
@ExceptionParser(ThrowKeyNotFoundOn404.class)
|
||||
@Path("{container}/{key}")
|
||||
Future<Blob> getObject(@PathParam("container") String container, @PathParam("key") String key,
|
||||
@Path("{container}/{name}")
|
||||
Future<CFObject> getObject(@PathParam("container") String container, @PathParam("name") String name,
|
||||
GetOptions... options);
|
||||
|
||||
@HEAD
|
||||
@ResponseParser(ParseObjectMetadataFromHeaders.class)
|
||||
@ExceptionParser(ThrowKeyNotFoundOn404.class)
|
||||
@Path("{container}/{key}")
|
||||
BlobMetadata getObjectMetadata(@PathParam("container") String container,
|
||||
@PathParam("key") String key);
|
||||
@Path("{container}/{name}")
|
||||
MutableObjectInfoWithMetadata getObjectInfo(@PathParam("container") String container, @PathParam("name") String name);
|
||||
|
||||
@DELETE
|
||||
@ExceptionParser(ReturnVoidOnNotFoundOr404.class)
|
||||
@Path("{container}/{key}")
|
||||
Future<Void> removeObject(@PathParam("container") String container, @PathParam("key") String key);
|
||||
@Path("{container}/{name}")
|
||||
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 javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javax.ws.rs.core.HttpHeaders;
|
||||
|
||||
import org.jclouds.blobstore.binders.BindBlobToEntityAndUserMetadataToHeadersWithPrefix;
|
||||
import org.jclouds.blobstore.domain.Blob;
|
||||
import org.jclouds.http.HttpRequest;
|
||||
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
|
||||
public BindCFObjectAsEntity(
|
||||
@Named(CloudFilesConstants.PROPERTY_CLOUDFILES_METADATA_PREFIX) String metadataPrefix) {
|
||||
super(metadataPrefix);
|
||||
public BindCFObjectToEntity(ObjectToBlob object2Blob,
|
||||
BindBlobToEntityAndUserMetadataToHeadersWithPrefix blobBinder) {
|
||||
this.blobBinder = blobBinder;
|
||||
this.object2Blob = object2Blob;
|
||||
}
|
||||
|
||||
public void bindToRequest(HttpRequest request, Object entity) {
|
||||
Blob object = (Blob) entity;
|
||||
if (object.getContentLength() >= 0) {
|
||||
CFObject object = (CFObject) entity;
|
||||
if (object.getContentLength() != null && object.getContentLength() >= 0) {
|
||||
checkArgument(object.getContentLength() <= 5 * 1024 * 1024 * 1024,
|
||||
"maximum size for put object is 5GB");
|
||||
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.
|
||||
request.getHeaders().put("Transfer-Encoding", "chunked");
|
||||
}
|
||||
/**
|
||||
* rackspace uses ETag header instead of Content-MD5.
|
||||
*/
|
||||
if (object.getMetadata().getContentMD5() != null) {
|
||||
request.getHeaders().put(HttpHeaders.ETAG,// note it needs to be in hex!
|
||||
HttpUtils.toHexString(object.getMetadata().getContentMD5()));
|
||||
}
|
||||
super.bindToRequest(request, entity);
|
||||
|
||||
blobBinder.bindToRequest(request, object2Blob.apply(object));
|
||||
if (object.getInfo().getHash() != null) {
|
||||
request.getHeaders().put(HttpHeaders.ETAG,
|
||||
HttpUtils.toHexString(object.getInfo().getHash()));
|
||||
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() {
|
||||
// for converters to work.
|
||||
install(new BlobStoreObjectModule());
|
||||
install(new CFObjectModule());
|
||||
}
|
||||
|
||||
@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) {
|
||||
this();
|
||||
setName(name);
|
||||
setBytes(bytes);
|
||||
setCount(count);
|
||||
|
|
|
@ -21,24 +21,33 @@
|
|||
* 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.blobstore.functions.ParseSystemAndUserMetadataFromHeaders;
|
||||
import org.jclouds.rackspace.cloudfiles.domain.internal.MutableObjectInfoWithMetadataImpl;
|
||||
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
|
||||
*
|
||||
*/
|
||||
public class ParseBlobFromHeadersAndHttpContent extends
|
||||
org.jclouds.blobstore.functions.ParseBlobFromHeadersAndHttpContent {
|
||||
@Inject
|
||||
public ParseBlobFromHeadersAndHttpContent(ParseObjectMetadataFromHeaders metadataParser,
|
||||
Blob.Factory blobFactory) {
|
||||
super(metadataParser, blobFactory);
|
||||
}
|
||||
@ImplementedBy(MutableObjectInfoWithMetadataImpl.class)
|
||||
public interface MutableObjectInfoWithMetadata extends ObjectInfo {
|
||||
|
||||
void setName(String name);
|
||||
|
||||
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 javax.inject.Inject;
|
||||
import javax.inject.Provider;
|
||||
|
||||
import org.jclouds.blobstore.domain.BlobMetadata;
|
||||
import org.jclouds.blobstore.domain.BoundedSortedSet;
|
||||
import org.jclouds.blobstore.domain.MutableBlobMetadata;
|
||||
import org.jclouds.blobstore.domain.internal.BoundedTreeSet;
|
||||
import org.jclouds.http.HttpUtils;
|
||||
import org.jclouds.http.functions.ParseJson;
|
||||
import org.jclouds.rackspace.cloudfiles.domain.ObjectInfo;
|
||||
import org.jclouds.rackspace.cloudfiles.options.ListContainerOptions;
|
||||
import org.jclouds.rest.InvocationContext;
|
||||
import org.jclouds.rest.internal.GeneratedHttpRequest;
|
||||
|
@ -53,39 +51,103 @@ import com.google.gson.Gson;
|
|||
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
|
||||
*/
|
||||
public class ParseBlobMetadataListFromJsonResponse extends
|
||||
ParseJson<BoundedSortedSet<BlobMetadata>> implements InvocationContext {
|
||||
public class ParseObjectInfoListFromJsonResponse extends ParseJson<BoundedSortedSet<ObjectInfo>>
|
||||
implements InvocationContext {
|
||||
|
||||
private final Provider<MutableBlobMetadata> metadataFactory;
|
||||
private GeneratedHttpRequest<?> request;
|
||||
|
||||
@Inject
|
||||
public ParseBlobMetadataListFromJsonResponse(Provider<MutableBlobMetadata> metadataFactory,
|
||||
Gson gson) {
|
||||
public ParseObjectInfoListFromJsonResponse(Gson gson) {
|
||||
super(gson);
|
||||
this.metadataFactory = metadataFactory;
|
||||
}
|
||||
|
||||
public static class CloudFilesMetadata implements Comparable<CloudFilesMetadata> {
|
||||
public CloudFilesMetadata() {
|
||||
}
|
||||
|
||||
public static class ObjectInfoImpl implements ObjectInfo {
|
||||
String name;
|
||||
String hash;
|
||||
long bytes;
|
||||
String content_type;
|
||||
DateTime last_modified;
|
||||
|
||||
public int compareTo(CloudFilesMetadata o) {
|
||||
public int compareTo(ObjectInfoImpl o) {
|
||||
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.getArgs() != null, "request.getArgs() should be initialized at this point");
|
||||
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 options = optionsList.length > 0 ? optionsList[0]
|
||||
: ListContainerOptions.NONE;
|
||||
Type listType = new TypeToken<SortedSet<CloudFilesMetadata>>() {
|
||||
Type listType = new TypeToken<SortedSet<ObjectInfoImpl>>() {
|
||||
}.getType();
|
||||
|
||||
try {
|
||||
SortedSet<CloudFilesMetadata> list = gson.fromJson(new InputStreamReader(stream, "UTF-8"),
|
||||
SortedSet<ObjectInfoImpl> list = gson.fromJson(new InputStreamReader(stream, "UTF-8"),
|
||||
listType);
|
||||
SortedSet<BlobMetadata> returnVal = Sets.newTreeSet(Iterables.transform(list,
|
||||
new Function<CloudFilesMetadata, BlobMetadata>() {
|
||||
public BlobMetadata apply(CloudFilesMetadata from) {
|
||||
MutableBlobMetadata metadata = metadataFactory.get();
|
||||
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;
|
||||
SortedSet<ObjectInfo> returnVal = Sets.newTreeSet(Iterables.transform(list,
|
||||
new Function<ObjectInfoImpl, ObjectInfo>() {
|
||||
public ObjectInfo apply(ObjectInfoImpl from) {
|
||||
return from;
|
||||
}
|
||||
}));
|
||||
boolean truncated = options.getMaxResults() == returnVal.size();
|
||||
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);
|
||||
|
||||
} catch (UnsupportedEncodingException e) {
|
|
@ -24,43 +24,50 @@
|
|||
package org.jclouds.rackspace.cloudfiles.functions;
|
||||
|
||||
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.http.HttpResponse;
|
||||
import org.jclouds.http.HttpUtils;
|
||||
import org.jclouds.rackspace.cloudfiles.reference.CloudFilesConstants;
|
||||
import org.jclouds.util.DateService;
|
||||
import org.jclouds.rackspace.cloudfiles.blobstore.functions.BlobToObjectInfo;
|
||||
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
|
||||
*/
|
||||
public class ParseObjectMetadataFromHeaders extends ParseSystemAndUserMetadataFromHeaders {
|
||||
public class ParseObjectMetadataFromHeaders implements
|
||||
Function<HttpResponse, MutableObjectInfoWithMetadata>, InvocationContext {
|
||||
private final ParseSystemAndUserMetadataFromHeaders blobMetadataParser;
|
||||
private final BlobToObjectInfo blobToObjectInfo;
|
||||
|
||||
@Inject
|
||||
public ParseObjectMetadataFromHeaders(Provider<MutableBlobMetadata> metadataFactory,
|
||||
DateService dateParser,
|
||||
@Named(CloudFilesConstants.PROPERTY_CLOUDFILES_METADATA_PREFIX) String metadataPrefix) {
|
||||
super(metadataFactory, dateParser, metadataPrefix);
|
||||
public ParseObjectMetadataFromHeaders(ParseSystemAndUserMetadataFromHeaders blobMetadataParser,
|
||||
BlobToObjectInfo blobToObjectMetadata) {
|
||||
this.blobMetadataParser = blobMetadataParser;
|
||||
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) {
|
||||
super.addETagTo(from, metadata);
|
||||
if (metadata.getETag() == null) {
|
||||
// etag comes back incorrect case
|
||||
public MutableObjectInfoWithMetadata apply(HttpResponse from) {
|
||||
BlobMetadata base = blobMetadataParser.apply(from);
|
||||
MutableObjectInfoWithMetadata to = blobToObjectInfo.apply(base);
|
||||
String eTagHeader = from.getFirstHeaderOrNull("Etag");
|
||||
if (eTagHeader != null) {
|
||||
metadata.setETag(eTagHeader);
|
||||
to.setHash(HttpUtils.fromHexString(eTagHeader.replaceAll("\"", "")));
|
||||
}
|
||||
return to;
|
||||
}
|
||||
metadata.setContentMD5(HttpUtils.fromHexString(metadata.getETag()));
|
||||
|
||||
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.jclouds.blobstore.ContainerNotFoundException;
|
||||
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.integration.internal.BaseBlobStoreIntegrationTest;
|
||||
import org.jclouds.http.HttpResponseException;
|
||||
|
@ -50,8 +48,11 @@ import org.jclouds.http.HttpUtils;
|
|||
import org.jclouds.http.options.GetOptions;
|
||||
import org.jclouds.logging.log4j.config.Log4JLoggingModule;
|
||||
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.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.ListContainerOptions;
|
||||
import org.testng.annotations.BeforeGroups;
|
||||
|
@ -283,10 +284,10 @@ public class CloudFilesClientLiveTest {
|
|||
|
||||
String data = "foo";
|
||||
|
||||
connection.putObject(containerName, newBlob(data, "foo")).get(10, TimeUnit.SECONDS);
|
||||
connection.putObject(containerName, newBlob(data, "path/bar")).get(10, TimeUnit.SECONDS);
|
||||
connection.putObject(containerName, newCFObject(data, "foo")).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);
|
||||
assert !container.isTruncated();
|
||||
assertEquals(container.size(), 1);
|
||||
|
@ -314,36 +315,36 @@ public class CloudFilesClientLiveTest {
|
|||
// Test PUT with string data, ETag hash, and a piece of metadata
|
||||
String data = "Here is my data";
|
||||
String key = "object";
|
||||
Blob object = newBlob(data, key);
|
||||
byte[] md5 = object.getMetadata().getContentMD5();
|
||||
CFObject object = newCFObject(data, key);
|
||||
byte[] md5 = object.getInfo().getHash();
|
||||
String newEtag = connection.putObject(containerName, object).get(10, TimeUnit.SECONDS);
|
||||
assertEquals(HttpUtils.toHexString(md5), HttpUtils.toHexString(object.getMetadata()
|
||||
.getContentMD5()));
|
||||
assertEquals(HttpUtils.toHexString(md5), HttpUtils.toHexString(object.getInfo()
|
||||
.getHash()));
|
||||
|
||||
// Test HEAD of missing object
|
||||
try {
|
||||
connection.getObjectMetadata(containerName, "non-existent-object");
|
||||
connection.getObjectInfo(containerName, "non-existent-object");
|
||||
assert false;
|
||||
} catch (KeyNotFoundException e) {
|
||||
}
|
||||
|
||||
// Test HEAD of object
|
||||
BlobMetadata metadata = connection.getObjectMetadata(containerName, object.getMetadata()
|
||||
MutableObjectInfoWithMetadata metadata = connection.getObjectInfo(containerName, object.getInfo()
|
||||
.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(HttpUtils.toHexString(md5), HttpUtils.toHexString(object.getMetadata()
|
||||
.getContentMD5()));
|
||||
assertEquals(metadata.getETag(), newEtag);
|
||||
assertEquals(metadata.getUserMetadata().entrySet().size(), 1);
|
||||
assertEquals(metadata.getUserMetadata().get("metadata"), "metadata-value");
|
||||
assertEquals(HttpUtils.toHexString(md5), HttpUtils.toHexString(object.getInfo()
|
||||
.getHash()));
|
||||
assertEquals(metadata.getHash(), HttpUtils.fromHexString(newEtag));
|
||||
assertEquals(metadata.getMetadata().entrySet().size(), 1);
|
||||
assertEquals(metadata.getMetadata().get("metadata"), "metadata-value");
|
||||
|
||||
// // Test POST to update object's metadata
|
||||
Map<String, String> userMetadata = Maps.newHashMap();
|
||||
userMetadata.put("New-Metadata-1", "value-1");
|
||||
userMetadata.put("New-Metadata-2", "value-2");
|
||||
assertTrue(connection.setObjectMetadata(containerName, object.getMetadata().getName(),
|
||||
assertTrue(connection.setObjectMetadata(containerName, object.getInfo().getName(),
|
||||
userMetadata));
|
||||
|
||||
// Test GET of missing object
|
||||
|
@ -353,23 +354,23 @@ public class CloudFilesClientLiveTest {
|
|||
} catch (KeyNotFoundException e) {
|
||||
}
|
||||
// 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);
|
||||
assertEquals(IOUtils.toString((InputStream) getBlob.getData()), data);
|
||||
// TODO assertEquals(getBlob.getName(), object.getMetadata().getName());
|
||||
assertEquals(getBlob.getContentLength(), new Long(data.length()));
|
||||
assertEquals(getBlob.getMetadata().getContentType(), "text/plain");
|
||||
assertEquals(HttpUtils.toHexString(md5), HttpUtils.toHexString(getBlob.getMetadata()
|
||||
.getContentMD5()));
|
||||
assertEquals(newEtag, getBlob.getMetadata().getETag());
|
||||
assertEquals(getBlob.getMetadata().getUserMetadata().entrySet().size(), 2);
|
||||
assertEquals(getBlob.getMetadata().getUserMetadata().get("new-metadata-1"), "value-1");
|
||||
assertEquals(getBlob.getMetadata().getUserMetadata().get("new-metadata-2"), "value-2");
|
||||
assertEquals(getBlob.getInfo().getContentType(), "text/plain");
|
||||
assertEquals(HttpUtils.toHexString(md5), HttpUtils.toHexString(getBlob.getInfo()
|
||||
.getHash()));
|
||||
assertEquals(HttpUtils.fromHexString(newEtag), getBlob.getInfo().getHash());
|
||||
assertEquals(getBlob.getInfo().getMetadata().entrySet().size(), 2);
|
||||
assertEquals(getBlob.getInfo().getMetadata().get("new-metadata-1"), "value-1");
|
||||
assertEquals(getBlob.getInfo().getMetadata().get("new-metadata-2"), "value-2");
|
||||
|
||||
// Test PUT with invalid ETag (as if object's data was corrupted in transit)
|
||||
String correctEtag = newEtag;
|
||||
String incorrectEtag = "0" + correctEtag.substring(1);
|
||||
object.getMetadata().setETag(incorrectEtag);
|
||||
object.getInfo().setHash(HttpUtils.fromHexString(incorrectEtag));
|
||||
try {
|
||||
connection.putObject(containerName, object).get(10, TimeUnit.SECONDS);
|
||||
} catch (Throwable e) {
|
||||
|
@ -379,17 +380,17 @@ public class CloudFilesClientLiveTest {
|
|||
|
||||
// Test PUT chunked/streamed upload with data of "unknown" length
|
||||
ByteArrayInputStream bais = new ByteArrayInputStream(data.getBytes("UTF-8"));
|
||||
Blob blob = connection.newBlob();
|
||||
blob.getMetadata().setName("chunked-object");
|
||||
object.setData(bais);
|
||||
newEtag = connection.putObject(containerName, object).get(10, TimeUnit.SECONDS);
|
||||
assertEquals(HttpUtils.toHexString(md5), HttpUtils.toHexString(getBlob.getMetadata()
|
||||
.getContentMD5()));
|
||||
CFObject blob = connection.newCFObject();
|
||||
blob.getInfo().setName("chunked-object");
|
||||
blob.setData(bais);
|
||||
newEtag = connection.putObject(containerName, blob).get(10, TimeUnit.SECONDS);
|
||||
assertEquals(HttpUtils.toHexString(md5), HttpUtils.toHexString(getBlob.getInfo()
|
||||
.getHash()));
|
||||
|
||||
// Test GET with options
|
||||
// Non-matching ETag
|
||||
try {
|
||||
connection.getObject(containerName, object.getMetadata().getName(),
|
||||
connection.getObject(containerName, object.getInfo().getName(),
|
||||
GetOptions.Builder.ifETagDoesntMatch(newEtag)).get(120, TimeUnit.SECONDS);
|
||||
} catch (Exception e) {
|
||||
assertEquals(e.getCause().getClass(), HttpResponseException.class);
|
||||
|
@ -397,10 +398,10 @@ public class CloudFilesClientLiveTest {
|
|||
}
|
||||
|
||||
// Matching ETag
|
||||
getBlob = connection.getObject(containerName, object.getMetadata().getName(),
|
||||
getBlob = connection.getObject(containerName, object.getInfo().getName(),
|
||||
GetOptions.Builder.ifETagMatches(newEtag)).get(120, TimeUnit.SECONDS);
|
||||
assertEquals(getBlob.getMetadata().getETag(), newEtag);
|
||||
getBlob = connection.getObject(containerName, object.getMetadata().getName(),
|
||||
assertEquals(getBlob.getInfo().getHash(), HttpUtils.fromHexString(newEtag));
|
||||
getBlob = connection.getObject(containerName, object.getInfo().getName(),
|
||||
GetOptions.Builder.startAt(8)).get(120, TimeUnit.SECONDS);
|
||||
assertEquals(IOUtils.toString((InputStream) getBlob.getData()), data.substring(8));
|
||||
|
||||
|
@ -410,14 +411,13 @@ public class CloudFilesClientLiveTest {
|
|||
assertTrue(connection.deleteContainerIfEmpty(containerName).get(10, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
private Blob newBlob(String data, String key) throws IOException {
|
||||
Blob object = connection.newBlob();
|
||||
object.getMetadata().setName(key);
|
||||
private CFObject newCFObject(String data, String key) throws IOException {
|
||||
CFObject object = connection.newCFObject();
|
||||
object.getInfo().setName(key);
|
||||
object.setData(data);
|
||||
object.setContentLength(data.length());
|
||||
object.generateMD5();
|
||||
object.getMetadata().setContentType("text/plain");
|
||||
object.getMetadata().getUserMetadata().put("Metadata", "metadata-value");
|
||||
object.getInfo().setContentType("text/plain");
|
||||
object.getInfo().getMetadata().put("Metadata", "metadata-value");
|
||||
return object;
|
||||
}
|
||||
|
||||
|
|
|
@ -31,40 +31,54 @@ import java.net.URI;
|
|||
import javax.ws.rs.core.HttpHeaders;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
|
||||
import org.jclouds.blobstore.config.BlobStoreObjectModule;
|
||||
import org.jclouds.blobstore.domain.Blob;
|
||||
import org.jclouds.blobstore.domain.Blob.Factory;
|
||||
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 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
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Test(testName = "cloudfiles.BindCFObjectAsEntityTest")
|
||||
public class BindCFObjectAsEntityTest {
|
||||
@Test(testName = "cloudfiles.BindCFObjectToEntityTest")
|
||||
public class BindCFObjectToEntityTest {
|
||||
private Factory blobProvider;
|
||||
private Provider<BindCFObjectToEntity> binderProvider;
|
||||
private BlobToObject blob2Object;
|
||||
|
||||
public BindCFObjectAsEntityTest() {
|
||||
blobProvider = Guice.createInjector(new BlobStoreObjectModule()).getInstance(
|
||||
Blob.Factory.class);
|
||||
public BindCFObjectToEntityTest() {
|
||||
Injector injector = new CloudFilesContextBuilder(new CloudFilesPropertiesBuilder("id",
|
||||
"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);
|
||||
TEST_BLOB.getMetadata().setName("hello");
|
||||
TEST_BLOB.setData("hello");
|
||||
TEST_BLOB.getMetadata().setContentType(MediaType.TEXT_PLAIN);
|
||||
return TEST_BLOB;
|
||||
return blob2Object.apply(TEST_BLOB);
|
||||
}
|
||||
|
||||
public void testNormal() throws IOException {
|
||||
|
||||
BindCFObjectAsEntity binder = new BindCFObjectAsEntity("test");
|
||||
BindCFObjectToEntity binder = binderProvider.get();
|
||||
|
||||
HttpRequest request = new HttpRequest("GET", URI.create("http://localhost:8001"));
|
||||
binder.bindToRequest(request, testBlob());
|
||||
|
@ -76,9 +90,9 @@ public class BindCFObjectAsEntityTest {
|
|||
|
||||
public void testMD5InHex() throws IOException {
|
||||
|
||||
BindCFObjectAsEntity binder = new BindCFObjectAsEntity("test");
|
||||
BindCFObjectToEntity binder = binderProvider.get();
|
||||
|
||||
Blob blob = testBlob();
|
||||
CFObject blob = testBlob();
|
||||
blob.generateMD5();
|
||||
HttpRequest request = new HttpRequest("GET", URI.create("http://localhost:8001"));
|
||||
binder.bindToRequest(request, blob);
|
|
@ -31,10 +31,9 @@ import static org.testng.Assert.assertEquals;
|
|||
import java.io.InputStream;
|
||||
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.rackspace.cloudfiles.domain.ObjectInfo;
|
||||
import org.jclouds.rackspace.cloudfiles.functions.ParseObjectInfoListFromJsonResponse.ObjectInfoImpl;
|
||||
import org.jclouds.rackspace.cloudfiles.options.ListContainerOptions;
|
||||
import org.jclouds.rest.internal.GeneratedHttpRequest;
|
||||
import org.joda.time.DateTime;
|
||||
|
@ -56,30 +55,28 @@ public class ParseBlobMetadataListFromJsonResponseTest {
|
|||
|
||||
public void testApplyInputStream() {
|
||||
InputStream is = getClass().getResourceAsStream("/test_list_container.json");
|
||||
List<BlobMetadata> expects = Lists.newArrayList();
|
||||
MutableBlobMetadata one = i.getInstance(MutableBlobMetadata.class);
|
||||
one.setName("test_obj_1");
|
||||
one.setETag("4281c348eaf83e70ddce0e07221c3d28");
|
||||
one.setContentMD5(HttpUtils.fromHexString(one.getETag()));
|
||||
one.setSize(14l);
|
||||
one.setContentType("application/octet-stream");
|
||||
one.setLastModified(new DateTime("2009-02-03T05:26:32.612278"));
|
||||
List<ObjectInfo> expects = Lists.newArrayList();
|
||||
ObjectInfoImpl one = i.getInstance(ObjectInfoImpl.class);
|
||||
one.name = "test_obj_1";
|
||||
one.hash = "4281c348eaf83e70ddce0e07221c3d28";
|
||||
one.bytes = 14l;
|
||||
one.content_type = "application/octet-stream";
|
||||
one.last_modified = new DateTime("2009-02-03T05:26:32.612278");
|
||||
expects.add(one);
|
||||
MutableBlobMetadata two = i.getInstance(MutableBlobMetadata.class);
|
||||
two.setName("test_obj_2");
|
||||
two.setETag("b039efe731ad111bc1b0ef221c3849d0");
|
||||
two.setContentMD5(HttpUtils.fromHexString(two.getETag()));
|
||||
two.setSize(64l);
|
||||
two.setContentType("application/octet-stream");
|
||||
two.setLastModified(new DateTime("2009-02-03T05:26:32.612278"));
|
||||
ObjectInfoImpl two = i.getInstance(ObjectInfoImpl.class);
|
||||
two.name = ("test_obj_2");
|
||||
two.hash = ("b039efe731ad111bc1b0ef221c3849d0");
|
||||
two.bytes = (64l);
|
||||
two.content_type = ("application/octet-stream");
|
||||
two.last_modified =(new DateTime("2009-02-03T05:26:32.612278"));
|
||||
expects.add(two);
|
||||
GeneratedHttpRequest<?> request = createMock(GeneratedHttpRequest.class);
|
||||
ListContainerOptions options = new ListContainerOptions();
|
||||
expect(request.getArgs()).andReturn(
|
||||
new Object[] { "containter", new ListContainerOptions[] { options } }).atLeastOnce();
|
||||
replay(request);
|
||||
ParseBlobMetadataListFromJsonResponse parser = i
|
||||
.getInstance(ParseBlobMetadataListFromJsonResponse.class);
|
||||
ParseObjectInfoListFromJsonResponse parser = i
|
||||
.getInstance(ParseObjectInfoListFromJsonResponse.class);
|
||||
parser.setContext(request);
|
||||
assertEquals(parser.apply(is), expects);
|
||||
}
|
||||
|
|
|
@ -23,12 +23,18 @@
|
|||
*/
|
||||
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 org.jclouds.blobstore.domain.MutableBlobMetadata;
|
||||
import java.net.URI;
|
||||
|
||||
import org.jclouds.blobstore.reference.BlobStoreConstants;
|
||||
import org.jclouds.http.HttpResponse;
|
||||
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 com.google.inject.AbstractModule;
|
||||
|
@ -48,17 +54,24 @@ public class ParseObjectMetadataFromHeadersTest {
|
|||
@Override
|
||||
protected void configure() {
|
||||
bindConstant().annotatedWith(
|
||||
Jsr330.named(CloudFilesConstants.PROPERTY_CLOUDFILES_METADATA_PREFIX)).to("sdf");
|
||||
Jsr330.named(BlobStoreConstants.PROPERTY_USER_METADATA_PREFIX)).to("sdf");
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
public void testEtagCaseIssue() {
|
||||
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();
|
||||
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");
|
||||
parser.addETagTo(response, md);
|
||||
assertNotNull(md.getETag());
|
||||
MutableObjectInfoWithMetadata md = parser.apply(response);
|
||||
assertNotNull(md.getHash());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,14 +27,15 @@ import java.util.Map;
|
|||
import java.util.SortedSet;
|
||||
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.http.options.GetOptions;
|
||||
import org.jclouds.rackspace.cloudfiles.CloudFilesClient;
|
||||
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.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.ListContainerOptions;
|
||||
|
||||
|
@ -77,11 +78,11 @@ public class StubCloudFilesClient implements CloudFilesClient {
|
|||
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();
|
||||
}
|
||||
|
||||
public BlobMetadata getObjectMetadata(String container, String key) {
|
||||
public MutableObjectInfoWithMetadata getObjectInfo(String container, String key) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
|
@ -93,16 +94,12 @@ public class StubCloudFilesClient implements CloudFilesClient {
|
|||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public Future<BoundedSortedSet<BlobMetadata>> listObjects(String container,
|
||||
public Future<BoundedSortedSet<ObjectInfo>> listObjects(String container,
|
||||
ListContainerOptions... options) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public Blob newBlob() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public Future<String> putObject(String container, Blob object) {
|
||||
public Future<String> putObject(String container, CFObject object) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
|
@ -118,4 +115,8 @@ public class StubCloudFilesClient implements CloudFilesClient {
|
|||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public CFObject newCFObject() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue