mirror of https://github.com/apache/jclouds.git
Issue 353: added disposition, encoding, language to blob and transient blobstore
This commit is contained in:
parent
50a44b91c4
commit
ac9a642cd3
|
@ -63,7 +63,7 @@ public class DirectoryEntryListToResourceMetadataList implements
|
|||
null, null, null, null, Maps.<String, String> newHashMap());
|
||||
else
|
||||
return new BlobMetadataImpl(from.getObjectID(), from.getObjectName(), defaultLocation.get(), null,
|
||||
null, null, null, Maps.<String, String> newHashMap(), null, null);
|
||||
null, null, null, Maps.<String, String> newHashMap(), null, null, null, null, null);
|
||||
}
|
||||
|
||||
}), from.getToken());
|
||||
|
|
|
@ -94,7 +94,7 @@ public class AtmosBlobRequestSignerTest extends RestClientTest<AtmosStorageAsync
|
|||
request,
|
||||
"Accept: */*\nDate: Thu, 05 Jun 2008 16:38:19 GMT\nx-emc-signature: aLpB1oQaCA27AXT6Nzam7s0f0pI=\nx-emc-uid: identity\n");
|
||||
|
||||
assertContentHeadersEqual(request, "text/plain", (long) 2l, new byte[] { 0, 2, 4, 8 });
|
||||
assertContentHeadersEqual(request, "text/plain", null, null, null, (long) 2l, new byte[] { 0, 2, 4, 8 });
|
||||
|
||||
assertEquals(request.getFilters().size(), 0);
|
||||
}
|
||||
|
|
|
@ -91,7 +91,7 @@ public class S3BlobRequestSignerTest extends RestClientTest<S3AsyncClient> {
|
|||
request,
|
||||
"Authorization: AWS identity:j9Dy/lmmvlCKjA4lkqZenLxMkR4=\nDate: Thu, 05 Jun 2008 16:38:19 GMT\nHost: container.s3.amazonaws.com\n");
|
||||
|
||||
assertContentHeadersEqual(request, "text/plain", (long) 2l, new byte[] { 0, 2, 4, 8 });
|
||||
assertContentHeadersEqual(request, "text/plain", null, null, null, (long) 2l, new byte[] { 0, 2, 4, 8 });
|
||||
|
||||
assertEquals(request.getFilters().size(), 0);
|
||||
}
|
||||
|
|
|
@ -90,7 +90,7 @@ public class AzureBlobBlobRequestSignerTest extends RestClientTest<AzureBlobAsyn
|
|||
assertNonPayloadHeadersEqual(
|
||||
request,
|
||||
"Authorization: SharedKeyLite identity:LT+HBNzhbRsZY07kC+/JxeuAURbxTmwJaIe464LO36c=\nDate: Thu, 05 Jun 2008 16:38:19 GMT\nx-ms-blob-type: BlockBlob\nx-ms-version: 2009-09-19\n");
|
||||
assertContentHeadersEqual(request, "text/plain", (long) 2l, new byte[] { 0, 2, 4, 8 });
|
||||
assertContentHeadersEqual(request, "text/plain", null, null, null, (long) 2l, new byte[] { 0, 2, 4, 8 });
|
||||
|
||||
assertEquals(request.getFilters().size(), 0);
|
||||
}
|
||||
|
|
|
@ -87,6 +87,7 @@ import org.jclouds.http.HttpRequest;
|
|||
import org.jclouds.http.HttpResponse;
|
||||
import org.jclouds.http.HttpResponseException;
|
||||
import org.jclouds.http.options.HttpRequestOptions;
|
||||
import org.jclouds.io.Payload;
|
||||
import org.jclouds.io.Payloads;
|
||||
import org.jclouds.io.payloads.ByteArrayPayload;
|
||||
import org.jclouds.io.payloads.DelegatingPayload;
|
||||
|
@ -102,7 +103,8 @@ import com.google.common.util.concurrent.ListenableFuture;
|
|||
import com.google.inject.internal.Nullable;
|
||||
|
||||
/**
|
||||
* Implementation of {@link BaseAsyncBlobStore} which keeps all data in a local Map object.
|
||||
* Implementation of {@link BaseAsyncBlobStore} which keeps all data in a local
|
||||
* Map object.
|
||||
*
|
||||
* @author Adrian Cole
|
||||
* @author James Murty
|
||||
|
@ -119,12 +121,11 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore {
|
|||
|
||||
@Inject
|
||||
protected TransientAsyncBlobStore(BlobStoreContext context, DateService dateService, Crypto crypto,
|
||||
ConcurrentMap<String, ConcurrentMap<String, Blob>> containerToBlobs,
|
||||
ConcurrentMap<String, Location> containerToLocation,
|
||||
HttpGetOptionsListToGetOptions httpGetOptionsConverter,
|
||||
IfDirectoryReturnNameStrategy ifDirectoryReturnName, Blob.Factory blobFactory, BlobUtils blobUtils,
|
||||
@Named(Constants.PROPERTY_USER_THREADS) ExecutorService service, Supplier<Location> defaultLocation,
|
||||
Supplier<Set<? extends Location>> locations) {
|
||||
ConcurrentMap<String, ConcurrentMap<String, Blob>> containerToBlobs,
|
||||
ConcurrentMap<String, Location> containerToLocation, HttpGetOptionsListToGetOptions httpGetOptionsConverter,
|
||||
IfDirectoryReturnNameStrategy ifDirectoryReturnName, Blob.Factory blobFactory, BlobUtils blobUtils,
|
||||
@Named(Constants.PROPERTY_USER_THREADS) ExecutorService service, Supplier<Location> defaultLocation,
|
||||
Supplier<Set<? extends Location>> locations) {
|
||||
super(context, blobUtils, service, defaultLocation, locations);
|
||||
this.blobFactory = blobFactory;
|
||||
this.dateService = dateService;
|
||||
|
@ -148,21 +149,21 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore {
|
|||
return immediateFailedFuture(cnfe(container));
|
||||
|
||||
SortedSet<StorageMetadata> contents = newTreeSet(transform(realContents.keySet(),
|
||||
new Function<String, StorageMetadata>() {
|
||||
public StorageMetadata apply(String key) {
|
||||
Blob oldBlob = realContents.get(key);
|
||||
checkState(oldBlob != null, "blob " + key + " is not present although it was in the list of "
|
||||
+ container);
|
||||
checkState(oldBlob.getMetadata() != null, "blob " + container + "/" + key + " has no metadata");
|
||||
MutableBlobMetadata md = copy(oldBlob.getMetadata());
|
||||
String directoryName = ifDirectoryReturnName.execute(md);
|
||||
if (directoryName != null) {
|
||||
md.setName(directoryName);
|
||||
md.setType(StorageType.RELATIVE_PATH);
|
||||
}
|
||||
return md;
|
||||
new Function<String, StorageMetadata>() {
|
||||
public StorageMetadata apply(String key) {
|
||||
Blob oldBlob = realContents.get(key);
|
||||
checkState(oldBlob != null, "blob " + key + " is not present although it was in the list of "
|
||||
+ container);
|
||||
checkState(oldBlob.getMetadata() != null, "blob " + container + "/" + key + " has no metadata");
|
||||
MutableBlobMetadata md = copy(oldBlob.getMetadata());
|
||||
String directoryName = ifDirectoryReturnName.execute(md);
|
||||
if (directoryName != null) {
|
||||
md.setName(directoryName);
|
||||
md.setType(StorageType.RELATIVE_PATH);
|
||||
}
|
||||
}));
|
||||
return md;
|
||||
}
|
||||
}));
|
||||
|
||||
if (options.getMarker() != null) {
|
||||
final String finalMarker = options.getMarker();
|
||||
|
@ -207,14 +208,14 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore {
|
|||
contents = newTreeSet(filter(contents, new DelimiterFilter(prefix != null ? prefix : null, delimiter)));
|
||||
|
||||
Iterables.<StorageMetadata> addAll(contents, transform(commonPrefixes,
|
||||
new Function<String, StorageMetadata>() {
|
||||
public StorageMetadata apply(String o) {
|
||||
MutableStorageMetadata md = new MutableStorageMetadataImpl();
|
||||
md.setType(StorageType.RELATIVE_PATH);
|
||||
md.setName(o);
|
||||
return md;
|
||||
}
|
||||
}));
|
||||
new Function<String, StorageMetadata>() {
|
||||
public StorageMetadata apply(String o) {
|
||||
MutableStorageMetadata md = new MutableStorageMetadataImpl();
|
||||
md.setType(StorageType.RELATIVE_PATH);
|
||||
md.setName(o);
|
||||
return md;
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
// trim metadata, if the response isn't supposed to be detailed.
|
||||
|
@ -225,13 +226,13 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore {
|
|||
}
|
||||
|
||||
return Futures.<PageSet<? extends StorageMetadata>> immediateFuture(new PageSetImpl<StorageMetadata>(contents,
|
||||
marker));
|
||||
marker));
|
||||
|
||||
}
|
||||
|
||||
private ContainerNotFoundException cnfe(final String name) {
|
||||
return new ContainerNotFoundException(name, String.format("container %s not in %s", name, getContainerToBlobs()
|
||||
.keySet()));
|
||||
.keySet()));
|
||||
}
|
||||
|
||||
public static MutableBlobMetadata copy(MutableBlobMetadata in) {
|
||||
|
@ -328,15 +329,15 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore {
|
|||
@Override
|
||||
public ListenableFuture<PageSet<? extends StorageMetadata>> list() {
|
||||
return Futures.<PageSet<? extends StorageMetadata>> immediateFuture(new PageSetImpl<StorageMetadata>(transform(
|
||||
getContainerToBlobs().keySet(), new Function<String, StorageMetadata>() {
|
||||
public StorageMetadata apply(String name) {
|
||||
MutableStorageMetadata cmd = create();
|
||||
cmd.setName(name);
|
||||
cmd.setType(StorageType.CONTAINER);
|
||||
cmd.setLocation(getContainerToLocation().get(name));
|
||||
return cmd;
|
||||
}
|
||||
}), null));
|
||||
getContainerToBlobs().keySet(), new Function<String, StorageMetadata>() {
|
||||
public StorageMetadata apply(String name) {
|
||||
MutableStorageMetadata cmd = create();
|
||||
cmd.setName(name);
|
||||
cmd.setType(StorageType.CONTAINER);
|
||||
cmd.setLocation(getContainerToLocation().get(name));
|
||||
return cmd;
|
||||
}
|
||||
}), null));
|
||||
}
|
||||
|
||||
protected MutableStorageMetadata create() {
|
||||
|
@ -360,7 +361,7 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore {
|
|||
*/
|
||||
public ListenableFuture<Void> createContainerInLocationIfAbsent(final Location location, final String name) {
|
||||
ConcurrentMap<String, Blob> container = getContainerToBlobs().putIfAbsent(name,
|
||||
new ConcurrentHashMap<String, Blob>());
|
||||
new ConcurrentHashMap<String, Blob>());
|
||||
if (container == null) {
|
||||
getContainerToLocation().put(name, location != null ? location : defaultLocation.get());
|
||||
return immediateFuture((Void) null);
|
||||
|
@ -505,11 +506,11 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore {
|
|||
|
||||
protected Blob createUpdatedCopyOfBlob(Blob in) {
|
||||
ByteArrayPayload payload = (in.getPayload() instanceof ByteArrayPayload) ? ByteArrayPayload.class.cast(in
|
||||
.getPayload()) : null;
|
||||
.getPayload()) : null;
|
||||
if (payload == null)
|
||||
payload = (in.getPayload() instanceof DelegatingPayload) ? (DelegatingPayload.class.cast(in.getPayload())
|
||||
.getDelegate() instanceof ByteArrayPayload) ? ByteArrayPayload.class.cast(DelegatingPayload.class
|
||||
.cast(in.getPayload()).getDelegate()) : null : null;
|
||||
.getDelegate() instanceof ByteArrayPayload) ? ByteArrayPayload.class.cast(DelegatingPayload.class.cast(
|
||||
in.getPayload()).getDelegate()) : null : null;
|
||||
try {
|
||||
if (payload == null || !(payload instanceof ByteArrayPayload)) {
|
||||
String oldContentType = in.getPayload().getContentType();
|
||||
|
@ -531,23 +532,40 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore {
|
|||
blob.getMetadata().setContentMD5(payload.getContentMD5());
|
||||
blob.getMetadata().setContentType(payload.getContentType());
|
||||
blob.getMetadata().setContentDisposition(payload.getContentDisposition());
|
||||
blob.getMetadata().setContentEncoding(payload.getContentEncoding());
|
||||
blob.getMetadata().setContentLanguage(payload.getContentLanguage());
|
||||
|
||||
String eTag = CryptoStreams.hex(payload.getContentMD5());
|
||||
blob.getMetadata().setETag(eTag);
|
||||
// Set HTTP headers to match metadata
|
||||
blob.getAllHeaders().replaceValues(HttpHeaders.LAST_MODIFIED,
|
||||
Collections.singleton(dateService.rfc822DateFormat(blob.getMetadata().getLastModified())));
|
||||
Collections.singleton(dateService.rfc822DateFormat(blob.getMetadata().getLastModified())));
|
||||
blob.getAllHeaders().replaceValues(HttpHeaders.ETAG, Collections.singleton(eTag));
|
||||
blob.getAllHeaders().replaceValues(HttpHeaders.CONTENT_TYPE, Collections.singleton(payload.getContentType()));
|
||||
blob.getAllHeaders().replaceValues(HttpHeaders.CONTENT_LENGTH,
|
||||
Collections.singleton(payload.getContentLength() + ""));
|
||||
blob.getAllHeaders().replaceValues("Content-MD5",
|
||||
Collections.singleton(CryptoStreams.base64(payload.getContentMD5())));
|
||||
//TODO maybe i should change here content-disposition
|
||||
copyPayloadHeadersToBlob(payload, blob);
|
||||
blob.getAllHeaders().putAll(Multimaps.forMap(blob.getMetadata().getUserMetadata()));
|
||||
return blob;
|
||||
}
|
||||
|
||||
private void copyPayloadHeadersToBlob(Payload payload, Blob blob) {
|
||||
if (payload.getContentType() != null)
|
||||
blob.getAllHeaders().replaceValues(HttpHeaders.CONTENT_TYPE, Collections.singleton(payload.getContentType()));
|
||||
if (payload.getContentDisposition() != null)
|
||||
blob.getAllHeaders().replaceValues("Content-Disposition",
|
||||
Collections.singleton(payload.getContentDisposition()));
|
||||
if (payload.getContentEncoding() != null)
|
||||
blob.getAllHeaders().replaceValues(HttpHeaders.CONTENT_ENCODING,
|
||||
Collections.singleton(payload.getContentEncoding()));
|
||||
if (payload.getContentEncoding() != null)
|
||||
blob.getAllHeaders().replaceValues(HttpHeaders.CONTENT_LANGUAGE,
|
||||
Collections.singleton(payload.getContentLanguage()));
|
||||
if (payload.getContentLength() != null)
|
||||
blob.getAllHeaders().replaceValues(HttpHeaders.CONTENT_LENGTH,
|
||||
Collections.singleton(payload.getContentLength() + ""));
|
||||
if (payload.getContentMD5() != null)
|
||||
blob.getAllHeaders().replaceValues("Content-MD5",
|
||||
Collections.singleton(CryptoStreams.base64(payload.getContentMD5())));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
|
@ -585,7 +603,7 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore {
|
|||
if (object.getMetadata().getLastModified().before(modifiedSince)) {
|
||||
HttpResponse response = new HttpResponse(304, null, null);
|
||||
return immediateFailedFuture(new HttpResponseException(String.format("%1$s is before %2$s", object
|
||||
.getMetadata().getLastModified(), modifiedSince), null, response));
|
||||
.getMetadata().getLastModified(), modifiedSince), null, response));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -594,7 +612,7 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore {
|
|||
if (object.getMetadata().getLastModified().after(unmodifiedSince)) {
|
||||
HttpResponse response = new HttpResponse(412, null, null);
|
||||
return immediateFailedFuture(new HttpResponseException(String.format("%1$s is after %2$s", object
|
||||
.getMetadata().getLastModified(), unmodifiedSince), null, response));
|
||||
.getMetadata().getLastModified(), unmodifiedSince), null, response));
|
||||
}
|
||||
}
|
||||
Blob returnVal = copyBlob(object);
|
||||
|
@ -647,9 +665,10 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore {
|
|||
}
|
||||
}
|
||||
|
||||
private Blob copyBlob(Blob object) {
|
||||
Blob returnVal = blobFactory.create(copy(object.getMetadata()));
|
||||
returnVal.setPayload(object.getPayload());
|
||||
private Blob copyBlob(Blob blob) {
|
||||
Blob returnVal = blobFactory.create(copy(blob.getMetadata()));
|
||||
returnVal.setPayload(blob.getPayload());
|
||||
copyPayloadHeadersToBlob(blob.getPayload(), returnVal);
|
||||
return returnVal;
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
|
||||
package org.jclouds.blobstore.domain;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.jclouds.blobstore.domain.internal.BlobMetadataImpl;
|
||||
|
||||
import com.google.inject.ImplementedBy;
|
||||
|
@ -32,10 +34,11 @@ import com.google.inject.ImplementedBy;
|
|||
public interface BlobMetadata extends StorageMetadata {
|
||||
|
||||
/**
|
||||
* A standard MIME type describing the format of the contents. If none is provided, the default
|
||||
* is binary/octet-stream.
|
||||
* A standard MIME type describing the format of the contents. If none is
|
||||
* provided, the default is binary/octet-stream.
|
||||
*
|
||||
* @see <a href= "http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html?sec14.17." />
|
||||
* @see <a href=
|
||||
* "http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html?sec14.17." />
|
||||
*/
|
||||
String getContentType();
|
||||
|
||||
|
@ -45,8 +48,24 @@ public interface BlobMetadata extends StorageMetadata {
|
|||
* Content-Disposition to set for the blob.
|
||||
* <p/>
|
||||
* <b>Attention</b>: Not all provider support it!
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String getContentDisposition();
|
||||
|
||||
/**
|
||||
* Get Content Encoding of the blob
|
||||
* <p/>
|
||||
* Not all providers may support it
|
||||
*/
|
||||
@Nullable
|
||||
String getContentEncoding();
|
||||
|
||||
/**
|
||||
* Get Content Language of the blob
|
||||
* <p/>
|
||||
* Not all providers may support it
|
||||
*/
|
||||
@Nullable
|
||||
String getContentLanguage();
|
||||
}
|
|
@ -34,15 +34,20 @@ import com.google.inject.ImplementedBy;
|
|||
public interface MutableBlobMetadata extends BlobMetadata, MutableStorageMetadata {
|
||||
|
||||
/**
|
||||
* A standard MIME type describing the format of the contents. If none is provided, the default
|
||||
* is binary/octet-stream.
|
||||
* A standard MIME type describing the format of the contents. If none is
|
||||
* provided, the default is binary/octet-stream.
|
||||
*
|
||||
* @see <a href= "http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html?sec14.17." />
|
||||
* @see <a href=
|
||||
* "http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html?sec14.17." />
|
||||
*/
|
||||
void setContentType(@Nullable String type);
|
||||
|
||||
void setContentMD5(@Nullable byte[] md5);
|
||||
|
||||
String setContentDisposition(@Nullable String contentDisposition);
|
||||
void setContentDisposition(@Nullable String contentDisposition);
|
||||
|
||||
void setContentEncoding(@Nullable String contentEncoding);
|
||||
|
||||
void setContentLanguage(@Nullable String contentLanguage);
|
||||
|
||||
}
|
|
@ -125,13 +125,13 @@ public class BlobImpl extends PayloadEnclosingImpl implements Blob, Comparable<B
|
|||
}
|
||||
|
||||
/**
|
||||
* link the new payload to the metadata object so that when content-related metadata is updated
|
||||
* on the payload, it is also copied the metadata object.
|
||||
* link the new payload to the metadata object so that when content-related
|
||||
* metadata is updated on the payload, it is also copied the metadata object.
|
||||
*/
|
||||
void linkPayloadToMetadata(Payload data) {
|
||||
if (data instanceof DelegatingPayload)
|
||||
super.setPayload(new SetMetadataPropertiesPayload(DelegatingPayload.class.cast(data)
|
||||
.getDelegate(), _metadata));
|
||||
super
|
||||
.setPayload(new SetMetadataPropertiesPayload(DelegatingPayload.class.cast(data).getDelegate(), _metadata));
|
||||
else
|
||||
super.setPayload(new SetMetadataPropertiesPayload(data, _metadata));
|
||||
}
|
||||
|
@ -145,6 +145,8 @@ public class BlobImpl extends PayloadEnclosingImpl implements Blob, Comparable<B
|
|||
this.metadata = metadata;
|
||||
setContentType(metadata.getContentType());
|
||||
setContentDisposition(metadata.getContentDisposition());
|
||||
setContentEncoding(metadata.getContentEncoding());
|
||||
setContentLanguage(metadata.getContentLanguage());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -158,16 +160,28 @@ public class BlobImpl extends PayloadEnclosingImpl implements Blob, Comparable<B
|
|||
super.setContentDisposition(contentDisposition);
|
||||
metadata.setContentDisposition(contentDisposition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentEncoding(String contentEncoding) {
|
||||
super.setContentEncoding(contentEncoding);
|
||||
metadata.setContentEncoding(contentEncoding);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentLanguage(String contentLanguage) {
|
||||
super.setContentLanguage(contentLanguage);
|
||||
metadata.setContentLanguage(contentLanguage);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* link the metadata object to this so that when content-related metadata is updated, it is also
|
||||
* copied the currentpayload object.
|
||||
* link the metadata object to this so that when content-related metadata is
|
||||
* updated, it is also copied the currentpayload object.
|
||||
*/
|
||||
SetPayloadPropertiesMutableBlobMetadata linkMetadataToThis(MutableBlobMetadata metadata) {
|
||||
return metadata instanceof DelegatingMutableBlobMetadata ? new SetPayloadPropertiesMutableBlobMetadata(
|
||||
DelegatingMutableBlobMetadata.class.cast(metadata).getDelegate(), this)
|
||||
: new SetPayloadPropertiesMutableBlobMetadata(metadata, this);
|
||||
DelegatingMutableBlobMetadata.class.cast(metadata).getDelegate(), this)
|
||||
: new SetPayloadPropertiesMutableBlobMetadata(metadata, this);
|
||||
}
|
||||
|
||||
static class SetPayloadPropertiesMutableBlobMetadata extends DelegatingMutableBlobMetadata {
|
||||
|
@ -175,8 +189,7 @@ public class BlobImpl extends PayloadEnclosingImpl implements Blob, Comparable<B
|
|||
private static final long serialVersionUID = -5072270546219814521L;
|
||||
private transient final PayloadEnclosing blob;
|
||||
|
||||
public SetPayloadPropertiesMutableBlobMetadata(MutableBlobMetadata delegate,
|
||||
PayloadEnclosing blob) {
|
||||
public SetPayloadPropertiesMutableBlobMetadata(MutableBlobMetadata delegate, PayloadEnclosing blob) {
|
||||
super(delegate);
|
||||
this.blob = blob;
|
||||
}
|
||||
|
@ -188,6 +201,27 @@ public class BlobImpl extends PayloadEnclosingImpl implements Blob, Comparable<B
|
|||
blob.getPayload().setContentType(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentDisposition(String Disposition) {
|
||||
super.setContentDisposition(Disposition);
|
||||
if (canSetPayload())
|
||||
blob.getPayload().setContentDisposition(Disposition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentEncoding(String Encoding) {
|
||||
super.setContentEncoding(Encoding);
|
||||
if (canSetPayload())
|
||||
blob.getPayload().setContentEncoding(Encoding);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentLanguage(String Language) {
|
||||
super.setContentLanguage(Language);
|
||||
if (canSetPayload())
|
||||
blob.getPayload().setContentLanguage(Language);
|
||||
}
|
||||
|
||||
private boolean canSetPayload() {
|
||||
return blob != null && blob.getPayload() != null;
|
||||
}
|
||||
|
|
|
@ -41,13 +41,19 @@ public class BlobMetadataImpl extends StorageMetadataImpl implements Serializabl
|
|||
private static final long serialVersionUID = -5932618957134612231L;
|
||||
|
||||
private final String contentType;
|
||||
private final String contentDisposition;
|
||||
private final String contentEncoding;
|
||||
private final String contentLanguage;
|
||||
private final byte[] contentMD5;
|
||||
|
||||
public BlobMetadataImpl(String id, String name, @Nullable Location location, URI uri,
|
||||
String eTag, Long size, Date lastModified, Map<String, String> userMetadata,
|
||||
String contentType, byte[] contentMD5) {
|
||||
public BlobMetadataImpl(String id, String name, @Nullable Location location, URI uri, String eTag, Long size,
|
||||
Date lastModified, Map<String, String> userMetadata, String contentType, String contentDisposition,
|
||||
String contentEncoding, String contentLanguage, byte[] contentMD5) {
|
||||
super(StorageType.BLOB, id, name, location, uri, eTag, size, lastModified, userMetadata);
|
||||
this.contentType = contentType;
|
||||
this.contentDisposition = contentDisposition;
|
||||
this.contentEncoding = contentEncoding;
|
||||
this.contentLanguage = contentLanguage;
|
||||
this.contentMD5 = contentMD5;
|
||||
}
|
||||
|
||||
|
@ -59,6 +65,30 @@ public class BlobMetadataImpl extends StorageMetadataImpl implements Serializabl
|
|||
return contentType;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public String getContentDisposition() {
|
||||
return contentDisposition;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public String getContentEncoding() {
|
||||
return contentEncoding;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public String getContentLanguage() {
|
||||
return contentLanguage;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
|
|
|
@ -176,4 +176,34 @@ public class DelegatingMutableBlobMetadata implements MutableBlobMetadata, Seria
|
|||
return delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentDisposition(String contentDisposition) {
|
||||
delegate.setContentDisposition(contentDisposition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContentDisposition() {
|
||||
return delegate.getContentDisposition();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContentEncoding() {
|
||||
return delegate.getContentEncoding();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContentLanguage() {
|
||||
return delegate.getContentLanguage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentEncoding(String contentEncoding) {
|
||||
delegate.setContentEncoding(contentEncoding);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentLanguage(String contentLanguage) {
|
||||
delegate.setContentLanguage(contentLanguage);
|
||||
}
|
||||
|
||||
}
|
|
@ -29,12 +29,14 @@ import org.jclouds.blobstore.domain.StorageType;
|
|||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
public class MutableBlobMetadataImpl extends MutableStorageMetadataImpl implements
|
||||
MutableBlobMetadata {
|
||||
public class MutableBlobMetadataImpl extends MutableStorageMetadataImpl implements MutableBlobMetadata {
|
||||
/** The serialVersionUID */
|
||||
private static final long serialVersionUID = -5932618957134612231L;
|
||||
|
||||
private String contentType;
|
||||
private String contentDisposition;
|
||||
private String contentEncoding;
|
||||
private String contentLanguage;
|
||||
private byte[] contentMD5;
|
||||
|
||||
public MutableBlobMetadataImpl() {
|
||||
|
@ -45,6 +47,9 @@ public class MutableBlobMetadataImpl extends MutableStorageMetadataImpl implemen
|
|||
public MutableBlobMetadataImpl(BlobMetadata from) {
|
||||
super(from);
|
||||
this.contentType = from.getContentType();
|
||||
this.contentDisposition = from.getContentDisposition();
|
||||
this.contentEncoding = from.getContentEncoding();
|
||||
this.contentLanguage = from.getContentLanguage();
|
||||
this.contentMD5 = from.getContentMD5();
|
||||
}
|
||||
|
||||
|
@ -56,6 +61,62 @@ public class MutableBlobMetadataImpl extends MutableStorageMetadataImpl implemen
|
|||
return contentType;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void setContentType(String contentType) {
|
||||
this.contentType = contentType;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public String getContentDisposition() {
|
||||
return contentDisposition;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void setContentDisposition(String contentDisposition) {
|
||||
this.contentDisposition = contentDisposition;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public String getContentEncoding() {
|
||||
return contentEncoding;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void setContentEncoding(String contentEncoding) {
|
||||
this.contentEncoding = contentEncoding;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public String getContentLanguage() {
|
||||
return contentLanguage;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void setContentLanguage(String contentLanguage) {
|
||||
this.contentLanguage = contentLanguage;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
|
@ -82,12 +143,4 @@ public class MutableBlobMetadataImpl extends MutableStorageMetadataImpl implemen
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void setContentType(String type) {
|
||||
this.contentType = type;
|
||||
}
|
||||
|
||||
}
|
|
@ -44,7 +44,7 @@ public class TransientBlobRequestSignerTest extends RestClientTest<TransientAsyn
|
|||
private Factory blobFactory;
|
||||
|
||||
public void testSignGetBlob() throws ArrayIndexOutOfBoundsException, SecurityException, IllegalArgumentException,
|
||||
NoSuchMethodException, IOException {
|
||||
NoSuchMethodException, IOException {
|
||||
HttpRequest request = signer.signGetBlob("container", "name");
|
||||
|
||||
assertRequestLineEquals(request, "GET http://localhost/container/name HTTP/1.1");
|
||||
|
@ -55,7 +55,7 @@ public class TransientBlobRequestSignerTest extends RestClientTest<TransientAsyn
|
|||
}
|
||||
|
||||
public void testSignRemoveBlob() throws ArrayIndexOutOfBoundsException, SecurityException, IllegalArgumentException,
|
||||
NoSuchMethodException, IOException {
|
||||
NoSuchMethodException, IOException {
|
||||
HttpRequest request = signer.signRemoveBlob("container", "name");
|
||||
|
||||
assertRequestLineEquals(request, "DELETE http://localhost/container/name HTTP/1.1");
|
||||
|
@ -66,7 +66,7 @@ public class TransientBlobRequestSignerTest extends RestClientTest<TransientAsyn
|
|||
}
|
||||
|
||||
public void testSignPutBlob() throws ArrayIndexOutOfBoundsException, SecurityException, IllegalArgumentException,
|
||||
NoSuchMethodException, IOException {
|
||||
NoSuchMethodException, IOException {
|
||||
Blob blob = blobFactory.create(null);
|
||||
blob.getMetadata().setName("name");
|
||||
blob.setPayload(new PhantomPayload(2l, new byte[] { 0, 2, 4, 8 }));
|
||||
|
@ -75,8 +75,10 @@ public class TransientBlobRequestSignerTest extends RestClientTest<TransientAsyn
|
|||
HttpRequest request = signer.signPutBlob("container", blob);
|
||||
|
||||
assertRequestLineEquals(request, "PUT http://localhost/container/name HTTP/1.1");
|
||||
assertNonPayloadHeadersEqual(request, "Authorization: Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==\nContent-Length: 2\nContent-MD5: AAIECA==\nContent-Type: text/plain\n");
|
||||
assertContentHeadersEqual(request, "text/plain", (long) 2l, new byte[] { 0, 2, 4, 8 });
|
||||
assertNonPayloadHeadersEqual(
|
||||
request,
|
||||
"Authorization: Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==\nContent-Length: 2\nContent-MD5: AAIECA==\nContent-Type: text/plain\n");
|
||||
assertContentHeadersEqual(request, "text/plain", null, null, null, (long) 2l, new byte[] { 0, 2, 4, 8 });
|
||||
|
||||
assertEquals(request.getFilters().size(), 0);
|
||||
}
|
||||
|
|
|
@ -45,6 +45,7 @@ import java.util.concurrent.ExecutionException;
|
|||
import java.util.concurrent.Future;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
import javax.ws.rs.core.HttpHeaders;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
|
||||
import org.jclouds.blobstore.ContainerNotFoundException;
|
||||
|
@ -155,13 +156,14 @@ public class BaseBlobIntegrationTest extends BaseBlobStoreIntegrationTest {
|
|||
|
||||
/**
|
||||
* Methods for checking Content-Disposition. In this way, in implementations
|
||||
* that do not support the new field, it is easy to override with empty,
|
||||
* and allow the rest of the test to work.
|
||||
* that do not support the new field, it is easy to override with empty, and
|
||||
* allow the rest of the test to work.
|
||||
*
|
||||
* @param blob
|
||||
* @param expected
|
||||
*/
|
||||
protected void checkContentDispostion(Blob blob, String expected){
|
||||
assertEquals(blob.getPayload().getContentDisposition(), expected);
|
||||
protected void checkContentDispostion(Blob blob, String expected) {
|
||||
assertEquals(blob.getPayload().getContentDisposition(), expected);
|
||||
}
|
||||
|
||||
@Test(groups = { "integration", "live" })
|
||||
|
@ -456,6 +458,9 @@ public class BaseBlobIntegrationTest extends BaseBlobStoreIntegrationTest {
|
|||
}
|
||||
}));
|
||||
blob.getMetadata().setContentType("text/csv");
|
||||
blob.getMetadata().setContentDisposition("attachment; filename=photo.jpg");
|
||||
blob.getMetadata().setContentEncoding("gzip");
|
||||
blob.getMetadata().setContentLanguage("en");
|
||||
|
||||
String containerName = getContainerName();
|
||||
try {
|
||||
|
@ -465,7 +470,10 @@ public class BaseBlobIntegrationTest extends BaseBlobStoreIntegrationTest {
|
|||
blob = context.getBlobStore().getBlob(containerName, blob.getMetadata().getName());
|
||||
String returnedString = getContentAsStringOrNullAndClose(blob);
|
||||
assertEquals(returnedString, "foo");
|
||||
assert blob.getPayload().getContentType().startsWith("text/csv") : blob.getPayload().getContentType();
|
||||
checkContentType(blob, "text/csv");
|
||||
checkContentDisposition(blob, "attachment; filename=photo.jpg");
|
||||
checkContentEncoding(blob, "gzip");
|
||||
checkContentLanguage(blob, "en");
|
||||
PageSet<? extends StorageMetadata> set = context.getBlobStore().list(containerName);
|
||||
assert set.size() == 1 : set;
|
||||
} finally {
|
||||
|
@ -473,6 +481,40 @@ public class BaseBlobIntegrationTest extends BaseBlobStoreIntegrationTest {
|
|||
}
|
||||
}
|
||||
|
||||
protected void checkContentType(Blob blob, String contentType) {
|
||||
assert blob.getPayload().getContentType().startsWith(contentType) : blob.getPayload().getContentType();
|
||||
assert blob.getMetadata().getContentType().startsWith(contentType) : blob.getMetadata().getContentType();
|
||||
assert Iterables.getOnlyElement(blob.getAllHeaders().get(HttpHeaders.CONTENT_TYPE)).startsWith(contentType) : blob
|
||||
.getAllHeaders().get(HttpHeaders.CONTENT_TYPE);
|
||||
}
|
||||
|
||||
protected void checkContentDisposition(Blob blob, String contentDisposition) {
|
||||
assert blob.getPayload().getContentDisposition().startsWith(contentDisposition) : blob.getPayload()
|
||||
.getContentDisposition();
|
||||
assert blob.getMetadata().getContentDisposition().startsWith(contentDisposition) : blob.getMetadata()
|
||||
.getContentDisposition();
|
||||
assert Iterables.getOnlyElement(blob.getAllHeaders().get("Content-Disposition")).startsWith(contentDisposition) : blob
|
||||
.getAllHeaders().get("Content-Disposition");
|
||||
}
|
||||
|
||||
protected void checkContentEncoding(Blob blob, String contentEncoding) {
|
||||
assert blob.getPayload().getContentEncoding().startsWith(contentEncoding) : blob.getPayload()
|
||||
.getContentEncoding();
|
||||
assert blob.getMetadata().getContentEncoding().startsWith(contentEncoding) : blob.getMetadata()
|
||||
.getContentEncoding();
|
||||
assert Iterables.getOnlyElement(blob.getAllHeaders().get(HttpHeaders.CONTENT_ENCODING)).startsWith(
|
||||
contentEncoding) : blob.getAllHeaders().get(HttpHeaders.CONTENT_ENCODING);
|
||||
}
|
||||
|
||||
protected void checkContentLanguage(Blob blob, String contentLanguage) {
|
||||
assert blob.getPayload().getContentLanguage().startsWith(contentLanguage) : blob.getPayload()
|
||||
.getContentLanguage();
|
||||
assert blob.getMetadata().getContentLanguage().startsWith(contentLanguage) : blob.getMetadata()
|
||||
.getContentLanguage();
|
||||
assert Iterables.getOnlyElement(blob.getAllHeaders().get(HttpHeaders.CONTENT_LANGUAGE)).startsWith(
|
||||
contentLanguage) : blob.getAllHeaders().get(HttpHeaders.CONTENT_LANGUAGE);
|
||||
}
|
||||
|
||||
protected volatile static Crypto crypto;
|
||||
static {
|
||||
try {
|
||||
|
|
|
@ -72,12 +72,11 @@ public interface Payload extends InputSupplier<InputStream>, WriteTo, Closeable
|
|||
@Nullable
|
||||
String getContentType();
|
||||
|
||||
|
||||
/**
|
||||
* Set Content Disposition of the payload
|
||||
* <p/>
|
||||
* Not all providers may support it
|
||||
*
|
||||
*
|
||||
* @param contentDisposition
|
||||
*/
|
||||
void setContentDisposition(@Nullable String contentDisposition);
|
||||
|
@ -87,38 +86,42 @@ public interface Payload extends InputSupplier<InputStream>, WriteTo, Closeable
|
|||
* <p/>
|
||||
* Not all providers may support it
|
||||
*/
|
||||
@Nullable String getContentDisposition();
|
||||
@Nullable
|
||||
String getContentDisposition();
|
||||
|
||||
/**
|
||||
* Set Content Language of the payload
|
||||
* <p/>
|
||||
* Not all providers may support it
|
||||
*
|
||||
*
|
||||
* @param contentLanguage
|
||||
*/
|
||||
void setContentLanguage(@Nullable String contentLanguage);
|
||||
|
||||
/**
|
||||
* Get Content Language of the payload
|
||||
* <p/>
|
||||
* Not all providers may support it
|
||||
*/
|
||||
@Nullable String getContentLanguage();
|
||||
@Nullable
|
||||
String getContentLanguage();
|
||||
|
||||
/**
|
||||
* Set Content Encoding of the payload
|
||||
* <p/>
|
||||
* Not all providers may support it
|
||||
*
|
||||
*
|
||||
* @param contentEncoding
|
||||
*/
|
||||
void setContentEncoding(@Nullable String contentEncoding);
|
||||
|
||||
/**
|
||||
* Get Content Encoding of the payload
|
||||
* <p/>
|
||||
* Not all providers may support it
|
||||
*/
|
||||
@Nullable String getContentEncoding();
|
||||
|
||||
@Nullable
|
||||
String getContentEncoding();
|
||||
|
||||
/**
|
||||
* release resources used by this entity. This should be called when data is
|
||||
|
|
|
@ -38,6 +38,9 @@ import org.jclouds.io.WriteTo;
|
|||
*/
|
||||
public class StreamingPayload implements Payload {
|
||||
protected String contentType;
|
||||
protected String contentDisposition;
|
||||
protected String contentEncoding;
|
||||
protected String contentLanguage;
|
||||
protected transient volatile boolean written;
|
||||
protected final WriteTo writeTo;
|
||||
|
||||
|
@ -112,44 +115,20 @@ public class StreamingPayload implements Payload {
|
|||
this.contentType = contentType;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void setContentDisposition(String contentDisposition) {
|
||||
throw new UnsupportedOperationException("this payload is for streaming writes only");
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public String getContentDisposition() {
|
||||
throw new UnsupportedOperationException("this payload is for streaming writes only");
|
||||
return contentDisposition;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void setContentLanguage(String contentLanguage) {
|
||||
throw new UnsupportedOperationException("this payload is for streaming writes only");
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public String getContentLanguage() {
|
||||
throw new UnsupportedOperationException("this payload is for streaming writes only");
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void setContentEncoding(String contentEncoding) {
|
||||
throw new UnsupportedOperationException("this payload is for streaming writes only");
|
||||
public void setContentDisposition(@Nullable String contentDisposition) {
|
||||
this.contentDisposition = contentDisposition;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -157,7 +136,31 @@ public class StreamingPayload implements Payload {
|
|||
*/
|
||||
@Override
|
||||
public String getContentEncoding() {
|
||||
throw new UnsupportedOperationException("this payload is for streaming writes only");
|
||||
return contentEncoding;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void setContentEncoding(@Nullable String contentEncoding) {
|
||||
this.contentEncoding = contentEncoding;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public String getContentLanguage() {
|
||||
return contentLanguage;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void setContentLanguage(@Nullable String contentLanguage) {
|
||||
this.contentLanguage = contentLanguage;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -81,7 +81,7 @@ public class CloudFilesBlobRequestSignerTest extends RestClientTest<CloudFilesAs
|
|||
|
||||
assertRequestLineEquals(request, "PUT http://storageUrl/container/name HTTP/1.1");
|
||||
assertNonPayloadHeadersEqual(request, "X-Auth-Token: testtoken\n");
|
||||
assertContentHeadersEqual(request, "text/plain", (long) 2l, new byte[] { 0, 2, 4, 8 });
|
||||
assertContentHeadersEqual(request, "text/plain", null, null, null, (long) 2l, new byte[] { 0, 2, 4, 8 });
|
||||
|
||||
assertEquals(request.getFilters().size(), 0);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue