mirror of https://github.com/apache/jclouds.git
Merge pull request #628 from aledsage/Issue-647-AddExpiresHeader
Issue-647: adding "Expires" header for ContentMetadata
This commit is contained in:
commit
2a9f48cfba
|
@ -19,6 +19,7 @@
|
|||
package org.jclouds.atmos.domain.internal;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Date;
|
||||
|
||||
import org.jclouds.atmos.domain.MutableContentMetadata;
|
||||
import org.jclouds.io.ContentMetadataBuilder;
|
||||
|
@ -154,8 +155,13 @@ public class DelegatingMutableContentMetadata implements MutableContentMetadata
|
|||
}
|
||||
|
||||
@Override
|
||||
public void setPropertiesFromHttpHeaders(Multimap<String, String> headers) {
|
||||
delegate.setPropertiesFromHttpHeaders(headers);
|
||||
public void setExpires(Date expires) {
|
||||
delegate.setExpires(expires);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date getExpires() {
|
||||
return delegate.getExpires();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -21,6 +21,9 @@ package org.jclouds.atmos.blobstore;
|
|||
import static org.testng.Assert.assertEquals;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
import org.jclouds.apis.ApiMetadata;
|
||||
import org.jclouds.atmos.AtmosApiMetadata;
|
||||
|
@ -32,6 +35,7 @@ import org.jclouds.blobstore.domain.Blob;
|
|||
import org.jclouds.blobstore.domain.Blob.Factory;
|
||||
import org.jclouds.date.TimeStamp;
|
||||
import org.jclouds.http.HttpRequest;
|
||||
import org.jclouds.io.ContentMetadata;
|
||||
import org.jclouds.rest.ConfiguresRestClient;
|
||||
import org.jclouds.rest.internal.BaseAsyncClientTest;
|
||||
import org.jclouds.rest.internal.RestAnnotationProcessor;
|
||||
|
@ -89,6 +93,7 @@ public class AtmosBlobRequestSignerTest extends BaseAsyncClientTest<AtmosAsyncCl
|
|||
blob.getPayload().getContentMetadata().setContentLength(2l);
|
||||
blob.getPayload().getContentMetadata().setContentMD5(new byte[] { 0, 2, 4, 8 });
|
||||
blob.getPayload().getContentMetadata().setContentType("text/plain");
|
||||
blob.getPayload().getContentMetadata().setExpires(new Date(1000));
|
||||
|
||||
HttpRequest request = signer.signPutBlob("container", blob);
|
||||
|
||||
|
@ -98,7 +103,7 @@ public class AtmosBlobRequestSignerTest extends BaseAsyncClientTest<AtmosAsyncCl
|
|||
request,
|
||||
"Accept: */*\nDate: Thu, 05 Jun 2008 16:38:19 GMT\nx-emc-signature: aLpB1oQaCA27AXT6Nzam7s0f0pI=\nx-emc-uid: identity\n");
|
||||
|
||||
assertContentHeadersEqual(request, "text/plain", null, null, null, (long) 2l, new byte[] { 0, 2, 4, 8 });
|
||||
assertContentHeadersEqual(request, "text/plain", null, null, null, (long) 2l, new byte[] { 0, 2, 4, 8 }, new Date(1000));
|
||||
|
||||
assertEquals(request.getFilters().size(), 0);
|
||||
}
|
||||
|
|
|
@ -95,6 +95,7 @@ import org.jclouds.http.HttpResponseException;
|
|||
import org.jclouds.http.HttpUtils;
|
||||
import org.jclouds.http.options.HttpRequestOptions;
|
||||
import org.jclouds.io.ContentMetadata;
|
||||
import org.jclouds.io.ContentMetadataCodec;
|
||||
import org.jclouds.io.Payload;
|
||||
import org.jclouds.io.Payloads;
|
||||
import org.jclouds.io.payloads.BaseMutableContentMetadata;
|
||||
|
@ -124,6 +125,7 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore {
|
|||
protected final DateService dateService;
|
||||
protected final Crypto crypto;
|
||||
protected final HttpGetOptionsListToGetOptions httpGetOptionsConverter;
|
||||
protected final ContentMetadataCodec contentMetadataCodec;
|
||||
protected final IfDirectoryReturnNameStrategy ifDirectoryReturnName;
|
||||
protected final Factory blobFactory;
|
||||
protected final FilesystemStorageStrategy storageStrategy;
|
||||
|
@ -132,6 +134,7 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore {
|
|||
protected FilesystemAsyncBlobStore(BlobStoreContext context,
|
||||
DateService dateService, Crypto crypto,
|
||||
HttpGetOptionsListToGetOptions httpGetOptionsConverter,
|
||||
ContentMetadataCodec contentMetadataCodec,
|
||||
IfDirectoryReturnNameStrategy ifDirectoryReturnName,
|
||||
BlobUtils blobUtils,
|
||||
@Named(Constants.PROPERTY_USER_THREADS) ExecutorService service,
|
||||
|
@ -143,6 +146,7 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore {
|
|||
this.dateService = dateService;
|
||||
this.crypto = crypto;
|
||||
this.httpGetOptionsConverter = httpGetOptionsConverter;
|
||||
this.contentMetadataCodec = contentMetadataCodec;
|
||||
this.ifDirectoryReturnName = ifDirectoryReturnName;
|
||||
this.storageStrategy = checkNotNull(storageStrategy, "Storage strategy");
|
||||
}
|
||||
|
@ -475,7 +479,7 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore {
|
|||
}
|
||||
|
||||
private void copyPayloadHeadersToBlob(Payload payload, Blob blob) {
|
||||
blob.getAllHeaders().putAll(HttpUtils.getContentHeadersFromMetadata(payload.getContentMetadata()));
|
||||
blob.getAllHeaders().putAll(contentMetadataCodec.toHeaders(payload.getContentMetadata()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -114,6 +114,21 @@ public class NovaEC2ParserModule extends AbstractModule {
|
|||
if (Objects.equal("-", toParse)) return null;
|
||||
return delegate.iso8601SecondsDateParse(toParse);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String rfc1123DateFormat(Date date) {
|
||||
return delegate.rfc1123DateFormat(date);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String rfc1123DateFormat() {
|
||||
return delegate.rfc1123DateFormat();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date rfc1123DateParse(String toParse) {
|
||||
return delegate.rfc1123DateParse(toParse);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -59,7 +59,7 @@ public class BucketListObjectMetadata implements Serializable, ObjectMetadata {
|
|||
this.lastModified = lastModified;
|
||||
this.eTag = eTag;
|
||||
this.owner = owner;
|
||||
this.contentMetadata = new BaseImmutableContentMetadata(null, contentLength, md5, null, null, null);
|
||||
this.contentMetadata = new BaseImmutableContentMetadata(null, contentLength, md5, null, null, null, null);
|
||||
this.storageClass = storageClass;
|
||||
}
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@ public class CopyObjectResult implements Serializable, ObjectMetadata {
|
|||
public CopyObjectResult(Date lastModified, String eTag) {
|
||||
this.lastModified = lastModified;
|
||||
this.eTag = eTag;
|
||||
this.contentMetadata = new BaseImmutableContentMetadata(null, null, null, null, null, null);
|
||||
this.contentMetadata = new BaseImmutableContentMetadata(null, null, null, null, null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -21,6 +21,7 @@ package org.jclouds.s3.blobstore;
|
|||
import static org.testng.Assert.assertEquals;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Date;
|
||||
|
||||
import javax.inject.Provider;
|
||||
|
||||
|
@ -89,7 +90,7 @@ public class S3BlobRequestSignerTest extends BaseS3AsyncClientTest<S3AsyncClient
|
|||
public void testSignPutBlob() throws ArrayIndexOutOfBoundsException, SecurityException, IllegalArgumentException,
|
||||
NoSuchMethodException, IOException {
|
||||
Blob blob = blobFactory.get().name("name").forSigning().contentLength(2l).contentMD5(new byte[] { 0, 2, 4, 8 }).contentType(
|
||||
"text/plain").build();
|
||||
"text/plain").expires(new Date(1000)).build();
|
||||
|
||||
HttpRequest request = signer.signPutBlob("container", blob);
|
||||
|
||||
|
@ -98,7 +99,7 @@ public class S3BlobRequestSignerTest extends BaseS3AsyncClientTest<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", null, null, null, (long) 2l, new byte[] { 0, 2, 4, 8 });
|
||||
assertContentHeadersEqual(request, "text/plain", null, null, null, (long) 2l, new byte[] { 0, 2, 4, 8 }, new Date(1000));
|
||||
|
||||
assertEquals(request.getFilters().size(), 0);
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ package org.jclouds.openstack.swift.blobstore;
|
|||
import static org.testng.Assert.assertEquals;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Date;
|
||||
|
||||
import org.jclouds.blobstore.BlobRequestSigner;
|
||||
import org.jclouds.blobstore.domain.Blob;
|
||||
|
@ -72,12 +73,13 @@ public class SwiftBlobRequestSignerTest extends CommonSwiftClientTest {
|
|||
blob.getPayload().getContentMetadata().setContentLength(2l);
|
||||
blob.getPayload().getContentMetadata().setContentMD5(new byte[] { 0, 2, 4, 8 });
|
||||
blob.getPayload().getContentMetadata().setContentType("text/plain");
|
||||
blob.getPayload().getContentMetadata().setExpires(new Date(1000));
|
||||
|
||||
HttpRequest request = signer.signPutBlob("container", blob);
|
||||
|
||||
assertRequestLineEquals(request, "PUT http://storage/container/name HTTP/1.1");
|
||||
assertNonPayloadHeadersEqual(request, "X-Auth-Token: testtoken\n");
|
||||
assertContentHeadersEqual(request, "text/plain", null, null, null, (long) 2l, new byte[] { 0, 2, 4, 8 });
|
||||
assertContentHeadersEqual(request, "text/plain", null, null, null, (long) 2l, new byte[] { 0, 2, 4, 8 }, new Date(1000));
|
||||
|
||||
assertEquals(request.getFilters().size(), 0);
|
||||
}
|
||||
|
|
|
@ -92,6 +92,7 @@ import org.jclouds.http.HttpResponseException;
|
|||
import org.jclouds.http.HttpUtils;
|
||||
import org.jclouds.http.options.HttpRequestOptions;
|
||||
import org.jclouds.io.ContentMetadata;
|
||||
import org.jclouds.io.ContentMetadataCodec;
|
||||
import org.jclouds.io.MutableContentMetadata;
|
||||
import org.jclouds.io.Payload;
|
||||
import org.jclouds.io.Payloads;
|
||||
|
@ -127,6 +128,7 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore {
|
|||
protected final IfDirectoryReturnNameStrategy ifDirectoryReturnName;
|
||||
protected final Factory blobFactory;
|
||||
protected final TransientStorageStrategy storageStrategy;
|
||||
protected final ContentMetadataCodec contentMetadataCodec;
|
||||
|
||||
@Inject
|
||||
protected TransientAsyncBlobStore(BlobStoreContext context,
|
||||
|
@ -137,7 +139,8 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore {
|
|||
@Named(Constants.PROPERTY_USER_THREADS) ExecutorService service,
|
||||
Supplier<Location> defaultLocation,
|
||||
@Memoized Supplier<Set<? extends Location>> locations,
|
||||
Factory blobFactory, Provider<UriBuilder> uriBuilders) {
|
||||
Factory blobFactory, Provider<UriBuilder> uriBuilders,
|
||||
ContentMetadataCodec contentMetadataCodec) {
|
||||
super(context, blobUtils, service, defaultLocation, locations);
|
||||
this.blobFactory = blobFactory;
|
||||
this.dateService = dateService;
|
||||
|
@ -146,6 +149,7 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore {
|
|||
this.httpGetOptionsConverter = httpGetOptionsConverter;
|
||||
this.ifDirectoryReturnName = ifDirectoryReturnName;
|
||||
this.storageStrategy = new TransientStorageStrategy(defaultLocation);
|
||||
this.contentMetadataCodec = contentMetadataCodec;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -521,7 +525,7 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore {
|
|||
}
|
||||
|
||||
private void copyPayloadHeadersToBlob(Payload payload, Blob blob) {
|
||||
blob.getAllHeaders().putAll(HttpUtils.getContentHeadersFromMetadata(payload.getContentMetadata()));
|
||||
blob.getAllHeaders().putAll(contentMetadataCodec.toHeaders(payload.getContentMetadata()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -31,6 +31,7 @@ import org.jclouds.blobstore.options.GetOptions;
|
|||
import org.jclouds.http.HttpRequest;
|
||||
import org.jclouds.http.HttpUtils;
|
||||
import org.jclouds.http.filters.BasicAuthentication;
|
||||
import org.jclouds.io.ContentMetadataCodec;
|
||||
import org.jclouds.location.Provider;
|
||||
|
||||
import com.google.common.base.Supplier;
|
||||
|
@ -45,12 +46,15 @@ public class TransientBlobRequestSigner implements BlobRequestSigner {
|
|||
private final BasicAuthentication basicAuth;
|
||||
private final BlobToHttpGetOptions blob2HttpGetOptions;
|
||||
private final Supplier<URI> endpoint;
|
||||
|
||||
private final ContentMetadataCodec contentMetadataCodec;
|
||||
|
||||
@Inject
|
||||
public TransientBlobRequestSigner(BasicAuthentication basicAuth, BlobToHttpGetOptions blob2HttpGetOptions, @Provider Supplier<URI> endpoint) {
|
||||
public TransientBlobRequestSigner(BasicAuthentication basicAuth, BlobToHttpGetOptions blob2HttpGetOptions, @Provider Supplier<URI> endpoint,
|
||||
ContentMetadataCodec contentMetadataCodec) {
|
||||
this.basicAuth = checkNotNull(basicAuth, "basicAuth");
|
||||
this.blob2HttpGetOptions = checkNotNull(blob2HttpGetOptions, "blob2HttpGetOptions");
|
||||
this.endpoint = endpoint;
|
||||
this.contentMetadataCodec = contentMetadataCodec;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -64,7 +68,7 @@ public class TransientBlobRequestSigner implements BlobRequestSigner {
|
|||
HttpRequest request = HttpRequest.builder().method("PUT").endpoint(
|
||||
URI.create(String.format("%s/%s/%s", endpoint.get(), container, blob.getMetadata().getName()))).payload(
|
||||
blob.getPayload()).headers(
|
||||
HttpUtils.getContentHeadersFromMetadata(blob.getMetadata().getContentMetadata())).build();
|
||||
contentMetadataCodec.toHeaders(blob.getMetadata().getContentMetadata())).build();
|
||||
return basicAuth.filter(request);
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ package org.jclouds.blobstore.domain;
|
|||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
|
||||
import org.jclouds.blobstore.domain.internal.BlobBuilderImpl;
|
||||
|
@ -117,6 +118,8 @@ public interface BlobBuilder {
|
|||
|
||||
PayloadBlobBuilder contentEncoding(String contentEncoding);
|
||||
|
||||
PayloadBlobBuilder expires(Date expires);
|
||||
|
||||
/**
|
||||
*
|
||||
* @see Payloads#calculateMD5
|
||||
|
|
|
@ -24,6 +24,7 @@ import static org.jclouds.io.Payloads.newPayload;
|
|||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
@ -221,6 +222,12 @@ public class BlobBuilderImpl implements BlobBuilder {
|
|||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PayloadBlobBuilder expires(Date expires) {
|
||||
payload.getContentMetadata().setExpires(expires);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PayloadBlobBuilder forSigning() {
|
||||
return builder.forSigning();
|
||||
|
|
|
@ -86,7 +86,7 @@ public class TransientBlobRequestSignerTest extends BaseAsyncClientTest<Transien
|
|||
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 });
|
||||
assertContentHeadersEqual(request, "text/plain", null, null, null, (long) 2l, new byte[] { 0, 2, 4, 8 }, null);
|
||||
|
||||
assertEquals(request.getFilters().size(), 0);
|
||||
}
|
||||
|
@ -94,9 +94,9 @@ public class TransientBlobRequestSignerTest extends BaseAsyncClientTest<Transien
|
|||
public void testSignPutBlobWithGenerate() throws ArrayIndexOutOfBoundsException, SecurityException,
|
||||
IllegalArgumentException, NoSuchMethodException, IOException {
|
||||
Blob blob = blobFactory.get().name(blobName).payload("foo").calculateMD5().contentType("text/plain").build();
|
||||
|
||||
assertEquals(blob.getPayload().getContentMetadata().getContentMD5(), new byte[] { -84, -67, 24, -37, 76, -62, -8,
|
||||
92, -19, -17, 101, 79, -52, -60, -92, -40 });
|
||||
byte[] md5 = new byte[] { -84, -67, 24, -37, 76, -62, -8, 92, -19, -17, 101, 79, -52, -60, -92, -40 };
|
||||
|
||||
assertEquals(blob.getPayload().getContentMetadata().getContentMD5(), md5);
|
||||
|
||||
HttpRequest request = signer.signPutBlob(containerName, blob);
|
||||
|
||||
|
@ -104,8 +104,7 @@ public class TransientBlobRequestSignerTest extends BaseAsyncClientTest<Transien
|
|||
assertNonPayloadHeadersEqual(
|
||||
request,
|
||||
"Authorization: Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==\nContent-Length: 3\nContent-MD5: rL0Y20zC+Fzt72VPzMSk2A==\nContent-Type: text/plain\n");
|
||||
assertContentHeadersEqual(request, "text/plain", null, null, null, (long) 3l, new byte[] { -84, -67, 24, -37, 76,
|
||||
-62, -8, 92, -19, -17, 101, 79, -52, -60, -92, -40 });
|
||||
assertContentHeadersEqual(request, "text/plain", null, null, null, (long) 3l, md5, null);
|
||||
|
||||
assertEquals(request.getFilters().size(), 0);
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ import static org.jclouds.blobstore.util.BlobStoreUtils.getContentAsStringOrNull
|
|||
import static org.testng.Assert.assertEquals;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
@ -338,6 +339,21 @@ public class BaseBlobStoreIntegrationTest extends BaseViewLiveTest<BlobStoreCont
|
|||
});
|
||||
}
|
||||
|
||||
protected void assertConsistencyAwareBlobExpiryMetadata(final String containerName, final String blobName, final Date expectedExpires)
|
||||
throws InterruptedException {
|
||||
assertConsistencyAware(new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
Blob blob = view.getBlobStore().getBlob(containerName, blobName);
|
||||
Date actualExpires = blob.getPayload().getContentMetadata().getExpires();
|
||||
assert expectedExpires.equals(actualExpires) : "expires="+actualExpires+"; expected="+expectedExpires;
|
||||
} catch (Exception e) {
|
||||
Throwables.propagateIfPossible(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected void assertConsistencyAwareBlobInLocation(final String containerName, final String blobName, final Location loc)
|
||||
throws InterruptedException {
|
||||
assertConsistencyAware(new Runnable() {
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
package org.jclouds.date;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.util.Date;
|
||||
|
||||
public interface DateCodec {
|
||||
|
||||
public Date toDate(String date) throws ParseException;
|
||||
|
||||
public String toString(Date date);
|
||||
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package org.jclouds.date;
|
||||
|
||||
import org.jclouds.date.internal.SimpleDateCodecFactory;
|
||||
|
||||
import com.google.inject.ImplementedBy;
|
||||
|
||||
|
||||
/**
|
||||
* Codecs for converting from Date->String and vice versa.
|
||||
*
|
||||
* @author aled
|
||||
*/
|
||||
@ImplementedBy(SimpleDateCodecFactory.class)
|
||||
public interface DateCodecFactory {
|
||||
|
||||
public DateCodec rfc1123();
|
||||
}
|
|
@ -60,4 +60,10 @@ public interface DateService {
|
|||
|
||||
Date iso8601SecondsDateParse(String toParse);
|
||||
|
||||
String rfc1123DateFormat(Date date);
|
||||
|
||||
String rfc1123DateFormat();
|
||||
|
||||
Date rfc1123DateParse(String toParse);
|
||||
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
package org.jclouds.date.internal;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.util.Date;
|
||||
|
||||
import org.jclouds.date.DateCodec;
|
||||
import org.jclouds.date.DateCodecFactory;
|
||||
import org.jclouds.date.DateService;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
public class SimpleDateCodecFactory implements DateCodecFactory {
|
||||
|
||||
private final DateService dateService;
|
||||
|
||||
private volatile DateCodec rfc1123Codec;
|
||||
|
||||
@Inject
|
||||
public SimpleDateCodecFactory(final DateService dateService) {
|
||||
this.dateService = checkNotNull(dateService, "dateService");
|
||||
}
|
||||
|
||||
public DateCodec rfc1123() {
|
||||
if (rfc1123Codec == null) {
|
||||
rfc1123Codec = new DateCodec() {
|
||||
@Override
|
||||
public Date toDate(String date) throws ParseException {
|
||||
return dateService.rfc1123DateParse(date);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(Date date) {
|
||||
return dateService.rfc1123DateFormat(date);
|
||||
}
|
||||
};
|
||||
}
|
||||
return rfc1123Codec;
|
||||
}
|
||||
}
|
|
@ -51,6 +51,11 @@ public class SimpleDateFormatDateService implements DateService {
|
|||
// @GuardedBy("this")
|
||||
private static final SimpleDateFormat rfc822SimpleDateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss 'GMT'", Locale.US);
|
||||
|
||||
// See http://stackoverflow.com/questions/10584647/simpledateformat-parse-is-one-hour-out-using-rfc-1123-gmt-in-summer
|
||||
// for why not using "zzz"
|
||||
// @GuardedBy("this")
|
||||
private static final SimpleDateFormat rfc1123SimpleDateFormat = new SimpleDateFormat("EEE, dd MMM yyyyy HH:mm:ss Z", Locale.US);
|
||||
|
||||
// @GuardedBy("this")
|
||||
private static final SimpleDateFormat cSimpleDateFormat = new SimpleDateFormat("EEE MMM dd HH:mm:ss Z yyyy", Locale.US);
|
||||
|
||||
|
@ -180,4 +185,26 @@ public class SimpleDateFormatDateService implements DateService {
|
|||
}
|
||||
}
|
||||
|
||||
}
|
||||
@Override
|
||||
public final String rfc1123DateFormat(Date date) {
|
||||
synchronized (rfc1123SimpleDateFormat) {
|
||||
return rfc1123SimpleDateFormat.format(date);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String rfc1123DateFormat() {
|
||||
return rfc1123DateFormat(new Date());
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Date rfc1123DateParse(String toParse) {
|
||||
synchronized (rfc1123SimpleDateFormat) {
|
||||
try {
|
||||
return rfc1123SimpleDateFormat.parse(toParse);
|
||||
} catch (ParseException pe) {
|
||||
throw new RuntimeException("Error parsing data at " + pe.getErrorOffset(), pe);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ import static javax.ws.rs.core.HttpHeaders.CONTENT_ENCODING;
|
|||
import static javax.ws.rs.core.HttpHeaders.CONTENT_LANGUAGE;
|
||||
import static javax.ws.rs.core.HttpHeaders.CONTENT_LENGTH;
|
||||
import static javax.ws.rs.core.HttpHeaders.CONTENT_TYPE;
|
||||
import static javax.ws.rs.core.HttpHeaders.EXPIRES;
|
||||
import static org.jclouds.util.Patterns.PATTERN_THAT_BREAKS_URI;
|
||||
import static org.jclouds.util.Patterns.URI_PATTERN;
|
||||
|
||||
|
@ -45,7 +46,6 @@ import java.util.regex.Matcher;
|
|||
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
import javax.ws.rs.core.HttpHeaders;
|
||||
|
||||
import org.jclouds.Constants;
|
||||
import org.jclouds.crypto.CryptoStreams;
|
||||
|
@ -62,8 +62,6 @@ import org.jclouds.util.Strings2;
|
|||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.ImmutableMultimap;
|
||||
import com.google.common.collect.ImmutableMultimap.Builder;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.common.collect.SortedSetMultimap;
|
||||
import com.google.common.collect.TreeMultimap;
|
||||
|
@ -183,23 +181,6 @@ public class HttpUtils {
|
|||
}
|
||||
}
|
||||
|
||||
public static Multimap<String, String> getContentHeadersFromMetadata(ContentMetadata md) {
|
||||
Builder<String, String> builder = ImmutableMultimap.builder();
|
||||
if (md.getContentType() != null)
|
||||
builder.put(HttpHeaders.CONTENT_TYPE, md.getContentType());
|
||||
if (md.getContentDisposition() != null)
|
||||
builder.put("Content-Disposition", md.getContentDisposition());
|
||||
if (md.getContentEncoding() != null)
|
||||
builder.put(HttpHeaders.CONTENT_ENCODING, md.getContentEncoding());
|
||||
if (md.getContentLanguage() != null)
|
||||
builder.put(HttpHeaders.CONTENT_LANGUAGE, md.getContentLanguage());
|
||||
if (md.getContentLength() != null)
|
||||
builder.put(HttpHeaders.CONTENT_LENGTH, md.getContentLength() + "");
|
||||
if (md.getContentMD5() != null)
|
||||
builder.put("Content-MD5", CryptoStreams.base64(md.getContentMD5()));
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
public static byte[] toByteArrayOrNull(PayloadEnclosing response) {
|
||||
if (response.getPayload() != null) {
|
||||
InputStream input = response.getPayload().getInput();
|
||||
|
@ -238,6 +219,7 @@ public class HttpUtils {
|
|||
toMd.setContentDisposition(fromMd.getContentDisposition());
|
||||
toMd.setContentEncoding(fromMd.getContentEncoding());
|
||||
toMd.setContentLanguage(fromMd.getContentLanguage());
|
||||
toMd.setExpires(fromMd.getExpires());
|
||||
}
|
||||
|
||||
public static URI parseEndPoint(String hostHeader) {
|
||||
|
@ -342,6 +324,9 @@ public class HttpUtils {
|
|||
if (message.getPayload().getContentMetadata().getContentLanguage() != null)
|
||||
logger.debug("%s %s: %s", prefix, CONTENT_LANGUAGE, message.getPayload().getContentMetadata()
|
||||
.getContentLanguage());
|
||||
if (message.getPayload().getContentMetadata().getExpires() != null)
|
||||
logger.debug("%s %s: %s", prefix, EXPIRES, message.getPayload().getContentMetadata()
|
||||
.getExpires());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -392,6 +377,10 @@ public class HttpUtils {
|
|||
message.getPayload() == null || message.getFirstHeaderOrNull(CONTENT_LANGUAGE) == null,
|
||||
"configuration error please use request.getPayload().getContentMetadata().setContentLanguage(value) as opposed to adding a content language header: "
|
||||
+ message);
|
||||
checkArgument(
|
||||
message.getPayload() == null || message.getFirstHeaderOrNull(EXPIRES) == null,
|
||||
"configuration error please use request.getPayload().getContentMetadata().setExpires(value) as opposed to adding an expires header: "
|
||||
+ message);
|
||||
}
|
||||
|
||||
public static void releasePayload(HttpMessage from) {
|
||||
|
|
|
@ -45,6 +45,7 @@ import org.jclouds.http.HttpUtils;
|
|||
import org.jclouds.http.IOExceptionRetryHandler;
|
||||
import org.jclouds.http.handlers.DelegatingErrorHandler;
|
||||
import org.jclouds.http.handlers.DelegatingRetryHandler;
|
||||
import org.jclouds.io.ContentMetadataCodec;
|
||||
import org.jclouds.logging.Logger;
|
||||
import org.jclouds.util.Throwables2;
|
||||
|
||||
|
@ -56,7 +57,8 @@ import com.google.common.io.NullOutputStream;
|
|||
*/
|
||||
public abstract class BaseHttpCommandExecutorService<Q> implements HttpCommandExecutorService {
|
||||
protected final HttpUtils utils;
|
||||
|
||||
protected final ContentMetadataCodec contentMetadataCodec;
|
||||
|
||||
private final DelegatingRetryHandler retryHandler;
|
||||
private final IOExceptionRetryHandler ioRetryHandler;
|
||||
private final DelegatingErrorHandler errorHandler;
|
||||
|
@ -71,11 +73,12 @@ public abstract class BaseHttpCommandExecutorService<Q> implements HttpCommandEx
|
|||
protected final HttpWire wire;
|
||||
|
||||
@Inject
|
||||
protected BaseHttpCommandExecutorService(HttpUtils utils,
|
||||
protected BaseHttpCommandExecutorService(HttpUtils utils, ContentMetadataCodec contentMetadataCodec,
|
||||
@Named(Constants.PROPERTY_IO_WORKER_THREADS) ExecutorService ioWorkerExecutor,
|
||||
DelegatingRetryHandler retryHandler, IOExceptionRetryHandler ioRetryHandler,
|
||||
DelegatingErrorHandler errorHandler, HttpWire wire) {
|
||||
this.utils = checkNotNull(utils, "utils");
|
||||
this.contentMetadataCodec = checkNotNull(contentMetadataCodec, "contentMetadataCodec");
|
||||
this.retryHandler = checkNotNull(retryHandler, "retryHandler");
|
||||
this.ioRetryHandler = checkNotNull(ioRetryHandler, "ioRetryHandler");
|
||||
this.errorHandler = checkNotNull(errorHandler, "errorHandler");
|
||||
|
|
|
@ -54,7 +54,6 @@ import javax.ws.rs.core.HttpHeaders;
|
|||
|
||||
import org.jclouds.Constants;
|
||||
import org.jclouds.JcloudsVersion;
|
||||
import org.jclouds.crypto.CryptoStreams;
|
||||
import org.jclouds.http.HttpCommandExecutorService;
|
||||
import org.jclouds.http.HttpRequest;
|
||||
import org.jclouds.http.HttpResponse;
|
||||
|
@ -62,6 +61,7 @@ import org.jclouds.http.HttpUtils;
|
|||
import org.jclouds.http.IOExceptionRetryHandler;
|
||||
import org.jclouds.http.handlers.DelegatingErrorHandler;
|
||||
import org.jclouds.http.handlers.DelegatingRetryHandler;
|
||||
import org.jclouds.io.ContentMetadataCodec;
|
||||
import org.jclouds.io.MutableContentMetadata;
|
||||
import org.jclouds.io.Payload;
|
||||
import org.jclouds.logging.Logger;
|
||||
|
@ -90,13 +90,13 @@ public class JavaUrlHttpCommandExecutorService extends BaseHttpCommandExecutorSe
|
|||
private final Field methodField;
|
||||
|
||||
@Inject
|
||||
public JavaUrlHttpCommandExecutorService(HttpUtils utils,
|
||||
public JavaUrlHttpCommandExecutorService(HttpUtils utils, ContentMetadataCodec contentMetadataCodec,
|
||||
@Named(Constants.PROPERTY_IO_WORKER_THREADS) ExecutorService ioWorkerExecutor,
|
||||
DelegatingRetryHandler retryHandler, IOExceptionRetryHandler ioRetryHandler,
|
||||
DelegatingErrorHandler errorHandler, HttpWire wire, @Named("untrusted") HostnameVerifier verifier,
|
||||
@Named("untrusted") Supplier<SSLContext> untrustedSSLContextProvider) throws SecurityException,
|
||||
NoSuchFieldException {
|
||||
super(utils, ioWorkerExecutor, retryHandler, ioRetryHandler, errorHandler, wire);
|
||||
super(utils, contentMetadataCodec, ioWorkerExecutor, retryHandler, ioRetryHandler, errorHandler, wire);
|
||||
if (utils.getMaxConnections() > 0)
|
||||
System.setProperty("http.maxConnections", String.valueOf(checkNotNull(utils, "utils").getMaxConnections()));
|
||||
this.untrustedSSLContextProvider = checkNotNull(untrustedSSLContextProvider, "untrustedSSLContextProvider");
|
||||
|
@ -136,7 +136,7 @@ public class JavaUrlHttpCommandExecutorService extends BaseHttpCommandExecutorSe
|
|||
ImmutableMultimap<String, String> headers = headerBuilder.build();
|
||||
if (in != null) {
|
||||
Payload payload = newInputStreamPayload(in);
|
||||
payload.getContentMetadata().setPropertiesFromHttpHeaders(headers);
|
||||
contentMetadataCodec.fromHeaders(payload.getContentMetadata(), headers);
|
||||
builder.payload(payload);
|
||||
}
|
||||
builder.headers(RestAnnotationProcessor.filterOutContentHeaders(headers));
|
||||
|
@ -214,16 +214,9 @@ public class JavaUrlHttpCommandExecutorService extends BaseHttpCommandExecutorSe
|
|||
|
||||
if (request.getPayload() != null) {
|
||||
MutableContentMetadata md = request.getPayload().getContentMetadata();
|
||||
if (md.getContentMD5() != null)
|
||||
connection.setRequestProperty("Content-MD5", CryptoStreams.base64(md.getContentMD5()));
|
||||
if (md.getContentType() != null)
|
||||
connection.setRequestProperty(HttpHeaders.CONTENT_TYPE, md.getContentType());
|
||||
if (md.getContentDisposition() != null)
|
||||
connection.setRequestProperty("Content-Disposition", md.getContentDisposition());
|
||||
if (md.getContentEncoding() != null)
|
||||
connection.setRequestProperty("Content-Encoding", md.getContentEncoding());
|
||||
if (md.getContentLanguage() != null)
|
||||
connection.setRequestProperty("Content-Language", md.getContentLanguage());
|
||||
for (Map.Entry<String,String> entry : contentMetadataCodec.toHeaders(md).entries()) {
|
||||
connection.setRequestProperty(entry.getKey(), entry.getValue());
|
||||
}
|
||||
if (chunked) {
|
||||
connection.setChunkedStreamingMode(8196);
|
||||
} else {
|
||||
|
|
|
@ -20,7 +20,9 @@ package org.jclouds.io;
|
|||
|
||||
import static javax.ws.rs.core.HttpHeaders.CONTENT_LENGTH;
|
||||
import static javax.ws.rs.core.HttpHeaders.CONTENT_TYPE;
|
||||
import static javax.ws.rs.core.HttpHeaders.EXPIRES;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Set;
|
||||
|
||||
import org.jclouds.javax.annotation.Nullable;
|
||||
|
@ -32,8 +34,12 @@ import com.google.common.collect.ImmutableSet;
|
|||
*/
|
||||
public interface ContentMetadata {
|
||||
public static final Set<String> HTTP_HEADERS = ImmutableSet.of(CONTENT_LENGTH, "Content-MD5", CONTENT_TYPE,
|
||||
"Content-Disposition", "Content-Encoding", "Content-Language");
|
||||
"Content-Disposition", "Content-Encoding", "Content-Language", EXPIRES);
|
||||
|
||||
// See http://stackoverflow.com/questions/10584647/simpledateformat-parse-is-one-hour-out-using-rfc-1123-gmt-in-summer
|
||||
// for why not using "zzz"
|
||||
public final static String RFC1123_DATE_PATTERN = "EEE, dd MMM yyyyy HH:mm:ss Z";
|
||||
|
||||
/**
|
||||
* Returns the total size of the payload, or the chunk that's available.
|
||||
* <p/>
|
||||
|
@ -86,6 +92,16 @@ public interface ContentMetadata {
|
|||
@Nullable
|
||||
String getContentLanguage();
|
||||
|
||||
ContentMetadataBuilder toBuilder();
|
||||
/**
|
||||
* Gives the date/time after which the response is considered stale.
|
||||
*
|
||||
* @throws IllegalStateException If the Expires header is non-null, and not a valid RFC 1123 date
|
||||
*
|
||||
* @see <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.21"/>
|
||||
*/
|
||||
@Nullable
|
||||
Date getExpires();
|
||||
|
||||
ContentMetadataBuilder toBuilder();
|
||||
|
||||
}
|
|
@ -18,21 +18,14 @@
|
|||
*/
|
||||
package org.jclouds.io;
|
||||
|
||||
import static com.google.common.collect.Iterables.any;
|
||||
import static javax.ws.rs.core.HttpHeaders.CONTENT_LENGTH;
|
||||
import static javax.ws.rs.core.HttpHeaders.CONTENT_TYPE;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Date;
|
||||
|
||||
import org.jclouds.crypto.CryptoStreams;
|
||||
import org.jclouds.io.payloads.BaseImmutableContentMetadata;
|
||||
import org.jclouds.javax.annotation.Nullable;
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.Multimap;
|
||||
|
||||
/**
|
||||
* @author Adrian Cole
|
||||
|
@ -51,31 +44,7 @@ public class ContentMetadataBuilder implements Serializable {
|
|||
protected String contentDisposition;
|
||||
protected String contentLanguage;
|
||||
protected String contentEncoding;
|
||||
|
||||
public ContentMetadataBuilder fromHttpHeaders(Multimap<String, String> headers) {
|
||||
boolean chunked = any(headers.entries(), new Predicate<Entry<String, String>>() {
|
||||
@Override
|
||||
public boolean apply(Entry<String, String> input) {
|
||||
return "Transfer-Encoding".equalsIgnoreCase(input.getKey()) && "chunked".equalsIgnoreCase(input.getValue());
|
||||
}
|
||||
});
|
||||
for (Entry<String, String> header : headers.entries()) {
|
||||
if (!chunked && CONTENT_LENGTH.equalsIgnoreCase(header.getKey())) {
|
||||
contentLength(new Long(header.getValue()));
|
||||
} else if ("Content-MD5".equalsIgnoreCase(header.getKey())) {
|
||||
contentMD5(CryptoStreams.base64(header.getValue()));
|
||||
} else if (CONTENT_TYPE.equalsIgnoreCase(header.getKey())) {
|
||||
contentType(header.getValue());
|
||||
} else if ("Content-Disposition".equalsIgnoreCase(header.getKey())) {
|
||||
contentDisposition(header.getValue());
|
||||
} else if ("Content-Encoding".equalsIgnoreCase(header.getKey())) {
|
||||
contentEncoding(header.getValue());
|
||||
} else if ("Content-Language".equalsIgnoreCase(header.getKey())) {
|
||||
contentLanguage(header.getValue());
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
protected Date expires;
|
||||
|
||||
public ContentMetadataBuilder contentLength(@Nullable Long contentLength) {
|
||||
this.contentLength = contentLength;
|
||||
|
@ -113,28 +82,26 @@ public class ContentMetadataBuilder implements Serializable {
|
|||
return this;
|
||||
}
|
||||
|
||||
public ContentMetadataBuilder expires(@Nullable Date expires) {
|
||||
this.expires = expires;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ContentMetadata build() {
|
||||
return new BaseImmutableContentMetadata(contentType, contentLength, contentMD5, contentDisposition,
|
||||
contentLanguage, contentEncoding);
|
||||
contentLanguage, contentEncoding, expires);
|
||||
}
|
||||
|
||||
public static ContentMetadataBuilder fromContentMetadata(ContentMetadata in) {
|
||||
return new ContentMetadataBuilder().contentType(in.getContentType()).contentLength(in.getContentLength())
|
||||
.contentMD5(in.getContentMD5()).contentDisposition(in.getContentDisposition()).contentLanguage(
|
||||
in.getContentLanguage()).contentEncoding(in.getContentEncoding());
|
||||
in.getContentLanguage()).contentEncoding(in.getContentEncoding()).expires(in.getExpires());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((contentDisposition == null) ? 0 : contentDisposition.hashCode());
|
||||
result = prime * result + ((contentEncoding == null) ? 0 : contentEncoding.hashCode());
|
||||
result = prime * result + ((contentLanguage == null) ? 0 : contentLanguage.hashCode());
|
||||
result = prime * result + ((contentLength == null) ? 0 : contentLength.hashCode());
|
||||
result = prime * result + Arrays.hashCode(contentMD5);
|
||||
result = prime * result + ((contentType == null) ? 0 : contentType.hashCode());
|
||||
return result;
|
||||
return Objects.hashCode(contentDisposition, contentEncoding, contentLanguage, contentLength,
|
||||
contentMD5, contentType, expires);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -151,13 +118,14 @@ public class ContentMetadataBuilder implements Serializable {
|
|||
Objects.equal(contentLanguage, other.contentLanguage) &&
|
||||
Objects.equal(contentLength, other.contentLength) &&
|
||||
Arrays.equals(contentMD5, other.contentMD5) &&
|
||||
Objects.equal(contentType, other.contentType);
|
||||
Objects.equal(contentType, other.contentType) &&
|
||||
Objects.equal(expires, other.expires);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "[contentDisposition=" + contentDisposition + ", contentEncoding=" + contentEncoding
|
||||
+ ", contentLanguage=" + contentLanguage + ", contentLength=" + contentLength + ", contentMD5="
|
||||
+ Arrays.toString(contentMD5) + ", contentType=" + contentType + "]";
|
||||
+ Arrays.toString(contentMD5) + ", contentType=" + contentType + ", expires=" + expires + "]";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,124 @@
|
|||
package org.jclouds.io;
|
||||
|
||||
import static com.google.common.collect.Iterables.any;
|
||||
import static javax.ws.rs.core.HttpHeaders.CONTENT_LENGTH;
|
||||
import static javax.ws.rs.core.HttpHeaders.CONTENT_TYPE;
|
||||
import static javax.ws.rs.core.HttpHeaders.EXPIRES;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.util.Date;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.ws.rs.core.HttpHeaders;
|
||||
|
||||
import org.jclouds.crypto.CryptoStreams;
|
||||
import org.jclouds.date.DateCodec;
|
||||
import org.jclouds.date.DateCodecFactory;
|
||||
import org.jclouds.io.ContentMetadataCodec.DefaultContentMetadataCodec;
|
||||
import org.jclouds.logging.Logger;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.ImmutableMultimap;
|
||||
import com.google.common.collect.ImmutableMultimap.Builder;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.inject.ImplementedBy;
|
||||
import com.google.inject.Inject;
|
||||
|
||||
@ImplementedBy(DefaultContentMetadataCodec.class)
|
||||
public interface ContentMetadataCodec {
|
||||
|
||||
/**
|
||||
* Generates standard HTTP headers for the give metadata.
|
||||
*/
|
||||
public Multimap<String, String> toHeaders(ContentMetadata md);
|
||||
|
||||
/**
|
||||
* Sets properties related to the http headers listed in {@link ContentMetadata#HTTP_HEADERS}
|
||||
*/
|
||||
public void fromHeaders(MutableContentMetadata contentMetadata, Multimap<String, String> headers);
|
||||
|
||||
/**
|
||||
* Parses the 'Expires' header.
|
||||
* If invalid, returns a date in the past (in accordance with HTTP 1.1 client spec).
|
||||
*/
|
||||
public Date parseExpires(String expires);
|
||||
|
||||
/**
|
||||
* Default implementation, in accordance with HTTP 1.1 spec.
|
||||
*
|
||||
* @author aled
|
||||
*/
|
||||
public static class DefaultContentMetadataCodec implements ContentMetadataCodec {
|
||||
|
||||
@Resource
|
||||
protected Logger logger = Logger.NULL;
|
||||
|
||||
private final DateCodec httpExpiresDateCodec;
|
||||
|
||||
@Inject
|
||||
public DefaultContentMetadataCodec(DateCodecFactory dateCodecs) {
|
||||
httpExpiresDateCodec = dateCodecs.rfc1123();
|
||||
}
|
||||
|
||||
protected DateCodec getExpiresDateCodec() {
|
||||
return httpExpiresDateCodec;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Multimap<String, String> toHeaders(ContentMetadata md) {
|
||||
Builder<String, String> builder = ImmutableMultimap.builder();
|
||||
if (md.getContentType() != null)
|
||||
builder.put(HttpHeaders.CONTENT_TYPE, md.getContentType());
|
||||
if (md.getContentDisposition() != null)
|
||||
builder.put("Content-Disposition", md.getContentDisposition());
|
||||
if (md.getContentEncoding() != null)
|
||||
builder.put(HttpHeaders.CONTENT_ENCODING, md.getContentEncoding());
|
||||
if (md.getContentLanguage() != null)
|
||||
builder.put(HttpHeaders.CONTENT_LANGUAGE, md.getContentLanguage());
|
||||
if (md.getContentLength() != null)
|
||||
builder.put(HttpHeaders.CONTENT_LENGTH, md.getContentLength() + "");
|
||||
if (md.getContentMD5() != null)
|
||||
builder.put("Content-MD5", CryptoStreams.base64(md.getContentMD5()));
|
||||
if (md.getExpires() != null)
|
||||
builder.put(HttpHeaders.EXPIRES, getExpiresDateCodec().toString(md.getExpires()));
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fromHeaders(MutableContentMetadata contentMetadata, Multimap<String, String> headers) {
|
||||
boolean chunked = any(headers.entries(), new Predicate<Entry<String, String>>() {
|
||||
@Override
|
||||
public boolean apply(Entry<String, String> input) {
|
||||
return "Transfer-Encoding".equalsIgnoreCase(input.getKey()) && "chunked".equalsIgnoreCase(input.getValue());
|
||||
}
|
||||
});
|
||||
for (Entry<String, String> header : headers.entries()) {
|
||||
if (!chunked && CONTENT_LENGTH.equalsIgnoreCase(header.getKey())) {
|
||||
contentMetadata.setContentLength(new Long(header.getValue()));
|
||||
} else if ("Content-MD5".equalsIgnoreCase(header.getKey())) {
|
||||
contentMetadata.setContentMD5(CryptoStreams.base64(header.getValue()));
|
||||
} else if (CONTENT_TYPE.equalsIgnoreCase(header.getKey())) {
|
||||
contentMetadata.setContentType(header.getValue());
|
||||
} else if ("Content-Disposition".equalsIgnoreCase(header.getKey())) {
|
||||
contentMetadata.setContentDisposition(header.getValue());
|
||||
} else if ("Content-Encoding".equalsIgnoreCase(header.getKey())) {
|
||||
contentMetadata.setContentEncoding(header.getValue());
|
||||
} else if ("Content-Language".equalsIgnoreCase(header.getKey())) {
|
||||
contentMetadata.setContentLanguage(header.getValue());
|
||||
} else if (EXPIRES.equalsIgnoreCase(header.getKey())) {
|
||||
contentMetadata.setExpires(parseExpires(header.getValue()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Date parseExpires(String expires) {
|
||||
try {
|
||||
return (expires != null) ? getExpiresDateCodec().toDate(expires) : null;
|
||||
} catch (ParseException e) {
|
||||
logger.warn(e, "Invalid Expires header (%s); should be in RFC-1123 format; treating as already expired", expires);
|
||||
return new Date(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -18,6 +18,8 @@
|
|||
*/
|
||||
package org.jclouds.io;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import org.jclouds.javax.annotation.Nullable;
|
||||
|
||||
import com.google.common.collect.Multimap;
|
||||
|
@ -26,12 +28,6 @@ import com.google.common.collect.Multimap;
|
|||
* @author Adrian Cole
|
||||
*/
|
||||
public interface MutableContentMetadata extends ContentMetadata {
|
||||
/**
|
||||
* sets properties related to the http headers listed in
|
||||
* {@link ContentMetadata#HTTP_HEADERS}
|
||||
*
|
||||
*/
|
||||
void setPropertiesFromHttpHeaders(Multimap<String, String> headers);
|
||||
|
||||
void setContentLength(@Nullable Long contentLength);
|
||||
|
||||
|
@ -66,4 +62,5 @@ public interface MutableContentMetadata extends ContentMetadata {
|
|||
*/
|
||||
void setContentEncoding(@Nullable String contentEncoding);
|
||||
|
||||
void setExpires(@Nullable Date expires);
|
||||
}
|
|
@ -19,11 +19,16 @@
|
|||
package org.jclouds.io.payloads;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
|
||||
import org.jclouds.io.ContentMetadata;
|
||||
import org.jclouds.io.ContentMetadataBuilder;
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
|
||||
/**
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
|
@ -37,15 +42,17 @@ public class BaseImmutableContentMetadata implements ContentMetadata, Serializab
|
|||
protected String contentDisposition;
|
||||
protected String contentLanguage;
|
||||
protected String contentEncoding;
|
||||
protected Date expires;
|
||||
|
||||
public BaseImmutableContentMetadata(String contentType, Long contentLength, byte[] contentMD5,
|
||||
String contentDisposition, String contentLanguage, String contentEncoding) {
|
||||
String contentDisposition, String contentLanguage, String contentEncoding, Date expires) {
|
||||
this.contentType = contentType;
|
||||
this.contentLength = contentLength;
|
||||
this.contentMD5 = contentMD5;
|
||||
this.contentDisposition = contentDisposition;
|
||||
this.contentLanguage = contentLanguage;
|
||||
this.contentEncoding = contentEncoding;
|
||||
this.expires = expires;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -102,24 +109,25 @@ public class BaseImmutableContentMetadata implements ContentMetadata, Serializab
|
|||
return this.contentEncoding;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public Date getExpires() {
|
||||
return expires;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "[contentType=" + contentType + ", contentLength=" + contentLength + ", contentDisposition="
|
||||
+ contentDisposition + ", contentEncoding=" + contentEncoding + ", contentLanguage=" + contentLanguage
|
||||
+ ", contentMD5=" + Arrays.toString(contentMD5) + "]";
|
||||
+ ", contentMD5=" + Arrays.toString(contentMD5) + ", expires = " + expires + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((contentDisposition == null) ? 0 : contentDisposition.hashCode());
|
||||
result = prime * result + ((contentEncoding == null) ? 0 : contentEncoding.hashCode());
|
||||
result = prime * result + ((contentLanguage == null) ? 0 : contentLanguage.hashCode());
|
||||
result = prime * result + ((contentLength == null) ? 0 : contentLength.hashCode());
|
||||
result = prime * result + Arrays.hashCode(contentMD5);
|
||||
result = prime * result + ((contentType == null) ? 0 : contentType.hashCode());
|
||||
return result;
|
||||
return Objects.hashCode(contentDisposition, contentEncoding, contentLanguage, contentLength,
|
||||
contentMD5, contentType, expires);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -158,6 +166,9 @@ public class BaseImmutableContentMetadata implements ContentMetadata, Serializab
|
|||
return false;
|
||||
} else if (!contentType.equals(other.contentType))
|
||||
return false;
|
||||
if (!Objects.equal(expires, other.expires)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
package org.jclouds.io.payloads;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
import org.jclouds.io.ContentMetadata;
|
||||
import org.jclouds.io.ContentMetadataBuilder;
|
||||
|
@ -34,11 +35,6 @@ public class BaseMutableContentMetadata extends ContentMetadataBuilder implement
|
|||
/** The serialVersionUID */
|
||||
private static final long serialVersionUID = 8364286391963469370L;
|
||||
|
||||
@Override
|
||||
public void setPropertiesFromHttpHeaders(Multimap<String, String> headers) {
|
||||
fromHttpHeaders(headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
|
@ -141,6 +137,22 @@ public class BaseMutableContentMetadata extends ContentMetadataBuilder implement
|
|||
return this.contentEncoding;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void setExpires(@Nullable Date expires) {
|
||||
expires(expires);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public Date getExpires() {
|
||||
return expires;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseMutableContentMetadata toBuilder() {
|
||||
return BaseMutableContentMetadata.fromContentMetadata(this);
|
||||
|
@ -150,6 +162,6 @@ public class BaseMutableContentMetadata extends ContentMetadataBuilder implement
|
|||
return (BaseMutableContentMetadata) new BaseMutableContentMetadata().contentType(in.getContentType())
|
||||
.contentLength(in.getContentLength()).contentMD5(in.getContentMD5()).contentDisposition(
|
||||
in.getContentDisposition()).contentLanguage(in.getContentLanguage()).contentEncoding(
|
||||
in.getContentEncoding());
|
||||
in.getContentEncoding()).expires(in.getExpires());
|
||||
}
|
||||
}
|
|
@ -138,6 +138,7 @@ public abstract class Wire {
|
|||
wiredMd.setContentDisposition(oldMd.getContentDisposition());
|
||||
wiredMd.setContentEncoding(oldMd.getContentEncoding());
|
||||
wiredMd.setContentLanguage(oldMd.getContentLanguage());
|
||||
wiredMd.setExpires(oldMd.getExpires());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
|
|
|
@ -89,6 +89,7 @@ import org.jclouds.http.options.HttpRequestOptions;
|
|||
import org.jclouds.http.utils.ModifyRequest;
|
||||
import org.jclouds.internal.ClassMethodArgs;
|
||||
import org.jclouds.io.ContentMetadata;
|
||||
import org.jclouds.io.ContentMetadataCodec;
|
||||
import org.jclouds.io.Payload;
|
||||
import org.jclouds.io.PayloadEnclosing;
|
||||
import org.jclouds.io.Payloads;
|
||||
|
@ -255,6 +256,7 @@ public class RestAnnotationProcessor<T> {
|
|||
|
||||
private final ParseSax.Factory parserFactory;
|
||||
private final HttpUtils utils;
|
||||
private final ContentMetadataCodec contentMetadataCodec;
|
||||
private final Provider<UriBuilder> uriBuilderProvider;
|
||||
private final LoadingCache<Class<?>, Boolean> seedAnnotationCache;
|
||||
private final String apiVersion;
|
||||
|
@ -321,11 +323,12 @@ public class RestAnnotationProcessor<T> {
|
|||
@Inject
|
||||
public RestAnnotationProcessor(Injector injector, LoadingCache<Class<?>, Boolean> seedAnnotationCache,
|
||||
@ApiVersion String apiVersion, @BuildVersion String buildVersion, ParseSax.Factory parserFactory,
|
||||
HttpUtils utils, TypeLiteral<T> typeLiteral) throws ExecutionException {
|
||||
HttpUtils utils, ContentMetadataCodec contentMetadataCodec, TypeLiteral<T> typeLiteral) throws ExecutionException {
|
||||
this.declaring = (Class<T>) typeLiteral.getRawType();
|
||||
this.injector = injector;
|
||||
this.parserFactory = parserFactory;
|
||||
this.utils = utils;
|
||||
this.contentMetadataCodec = contentMetadataCodec;
|
||||
this.uriBuilderProvider = injector.getProvider(UriBuilder.class);
|
||||
this.seedAnnotationCache = seedAnnotationCache;
|
||||
seedAnnotationCache.get(declaring);
|
||||
|
@ -545,8 +548,9 @@ public class RestAnnotationProcessor<T> {
|
|||
request = decorateRequest(request);
|
||||
}
|
||||
|
||||
if (request.getPayload() != null)
|
||||
request.getPayload().getContentMetadata().setPropertiesFromHttpHeaders(headers);
|
||||
if (request.getPayload() != null) {
|
||||
contentMetadataCodec.fromHeaders(request.getPayload().getContentMetadata(), headers);
|
||||
}
|
||||
utils.checkRequestHasRequiredProperties(request);
|
||||
return request;
|
||||
} catch (ExecutionException e) {
|
||||
|
|
|
@ -34,6 +34,8 @@ import javax.net.ssl.SSLContext;
|
|||
import javax.net.ssl.SSLSession;
|
||||
import javax.ws.rs.core.UriBuilder;
|
||||
|
||||
import org.jclouds.date.internal.SimpleDateCodecFactory;
|
||||
import org.jclouds.date.internal.SimpleDateFormatDateService;
|
||||
import org.jclouds.http.BaseJettyTest;
|
||||
import org.jclouds.http.HttpCommand;
|
||||
import org.jclouds.http.HttpResponse;
|
||||
|
@ -44,7 +46,9 @@ import org.jclouds.http.TransformingHttpCommandImpl;
|
|||
import org.jclouds.http.functions.ReturnStringIf2xx;
|
||||
import org.jclouds.http.internal.HttpWire;
|
||||
import org.jclouds.http.internal.JavaUrlHttpCommandExecutorService;
|
||||
import org.jclouds.io.ContentMetadataCodec;
|
||||
import org.jclouds.io.Payloads;
|
||||
import org.jclouds.io.ContentMetadataCodec.DefaultContentMetadataCodec;
|
||||
import org.jclouds.rest.internal.RestAnnotationProcessor;
|
||||
import org.testng.annotations.BeforeTest;
|
||||
import org.testng.annotations.Test;
|
||||
|
@ -105,15 +109,17 @@ public class BackoffLimitedRetryHandlerTest {
|
|||
}
|
||||
|
||||
};
|
||||
private HttpUtils utils;
|
||||
|
||||
@BeforeTest
|
||||
void setupExecutorService() throws Exception {
|
||||
ExecutorService execService = Executors.newCachedThreadPool();
|
||||
BackoffLimitedRetryHandler backoff = new BackoffLimitedRetryHandler();
|
||||
utils = new HttpUtils(0, 500, 1, 1);
|
||||
HttpUtils utils = new HttpUtils(0, 500, 1, 1);
|
||||
ContentMetadataCodec contentMetadataCodec = new DefaultContentMetadataCodec(
|
||||
new SimpleDateCodecFactory(new SimpleDateFormatDateService()));
|
||||
RedirectionRetryHandler retry = new RedirectionRetryHandler(uriBuilderProvider, backoff);
|
||||
JavaUrlHttpCommandExecutorService httpService = new JavaUrlHttpCommandExecutorService(utils, execService,
|
||||
JavaUrlHttpCommandExecutorService httpService = new JavaUrlHttpCommandExecutorService(utils,
|
||||
contentMetadataCodec, execService,
|
||||
new DelegatingRetryHandler(backoff, retry), new BackoffLimitedRetryHandler(),
|
||||
new DelegatingErrorHandler(), new HttpWire(), new HostnameVerifier() {
|
||||
|
||||
|
|
|
@ -37,6 +37,7 @@ import org.jclouds.http.HttpUtils;
|
|||
import org.jclouds.http.IOExceptionRetryHandler;
|
||||
import org.jclouds.http.handlers.DelegatingErrorHandler;
|
||||
import org.jclouds.http.handlers.DelegatingRetryHandler;
|
||||
import org.jclouds.io.ContentMetadataCodec;
|
||||
import org.jclouds.rest.internal.GeneratedHttpRequest;
|
||||
|
||||
import com.google.common.base.Supplier;
|
||||
|
@ -86,13 +87,13 @@ public class TrackingJavaUrlHttpCommandExecutorService extends JavaUrlHttpComman
|
|||
}
|
||||
|
||||
@Inject
|
||||
public TrackingJavaUrlHttpCommandExecutorService(HttpUtils utils,
|
||||
public TrackingJavaUrlHttpCommandExecutorService(HttpUtils utils, ContentMetadataCodec contentMetadataCodec,
|
||||
@Named(Constants.PROPERTY_IO_WORKER_THREADS) ExecutorService ioWorkerExecutor,
|
||||
DelegatingRetryHandler retryHandler, IOExceptionRetryHandler ioRetryHandler,
|
||||
DelegatingErrorHandler errorHandler, HttpWire wire, @Named("untrusted") HostnameVerifier verifier,
|
||||
@Named("untrusted") Supplier<SSLContext> untrustedSSLContextProvider, List<HttpCommand> commandsInvoked)
|
||||
throws SecurityException, NoSuchFieldException {
|
||||
super(utils, ioWorkerExecutor, retryHandler, ioRetryHandler, errorHandler, wire, verifier,
|
||||
super(utils, contentMetadataCodec, ioWorkerExecutor, retryHandler, ioRetryHandler, errorHandler, wire, verifier,
|
||||
untrustedSSLContextProvider);
|
||||
this.commandsInvoked = commandsInvoked;
|
||||
}
|
||||
|
|
|
@ -25,9 +25,9 @@ import java.io.IOException;
|
|||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Properties;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
@ -49,6 +49,8 @@ import org.jclouds.apis.ApiMetadata;
|
|||
import org.jclouds.concurrent.MoreExecutors;
|
||||
import org.jclouds.concurrent.SingleThreaded;
|
||||
import org.jclouds.concurrent.config.ConfiguresExecutorService;
|
||||
import org.jclouds.date.internal.SimpleDateCodecFactory;
|
||||
import org.jclouds.date.internal.SimpleDateFormatDateService;
|
||||
import org.jclouds.http.HttpCommandExecutorService;
|
||||
import org.jclouds.http.HttpRequest;
|
||||
import org.jclouds.http.HttpResponse;
|
||||
|
@ -59,6 +61,8 @@ import org.jclouds.http.handlers.DelegatingErrorHandler;
|
|||
import org.jclouds.http.handlers.DelegatingRetryHandler;
|
||||
import org.jclouds.http.internal.BaseHttpCommandExecutorService;
|
||||
import org.jclouds.http.internal.HttpWire;
|
||||
import org.jclouds.io.ContentMetadataCodec;
|
||||
import org.jclouds.io.ContentMetadataCodec.DefaultContentMetadataCodec;
|
||||
import org.jclouds.io.CopyInputStreamInputSupplierMap;
|
||||
import org.jclouds.io.Payload;
|
||||
import org.jclouds.io.Payloads;
|
||||
|
@ -118,6 +122,9 @@ public abstract class BaseRestClientExpectTest<S> {
|
|||
|
||||
protected String provider = "mock";
|
||||
|
||||
protected ContentMetadataCodec contentMetadataCodec = new DefaultContentMetadataCodec(
|
||||
new SimpleDateCodecFactory(new SimpleDateFormatDateService()));
|
||||
|
||||
/**
|
||||
* Override this to supply alternative bindings for use in the test. This is commonly used to
|
||||
* override suppliers of dates so that the test results are predicatable.
|
||||
|
@ -188,10 +195,11 @@ public abstract class BaseRestClientExpectTest<S> {
|
|||
|
||||
@Inject
|
||||
public ExpectHttpCommandExecutorService(Function<HttpRequest, HttpResponse> fn, HttpUtils utils,
|
||||
ContentMetadataCodec contentMetadataCodec,
|
||||
@Named(Constants.PROPERTY_IO_WORKER_THREADS) ExecutorService ioExecutor,
|
||||
IOExceptionRetryHandler ioRetryHandler, DelegatingRetryHandler retryHandler,
|
||||
DelegatingErrorHandler errorHandler, HttpWire wire) {
|
||||
super(utils, ioExecutor, retryHandler, ioRetryHandler, errorHandler, wire);
|
||||
super(utils, contentMetadataCodec, ioExecutor, retryHandler, ioRetryHandler, errorHandler, wire);
|
||||
this.fn = checkNotNull(fn, "fn");
|
||||
}
|
||||
|
||||
|
@ -471,7 +479,7 @@ public abstract class BaseRestClientExpectTest<S> {
|
|||
builder.append(header.getKey()).append(": ").append(header.getValue()).append('\n');
|
||||
}
|
||||
if (request.getPayload() != null) {
|
||||
for (Entry<String, String> header : HttpUtils.getContentHeadersFromMetadata(
|
||||
for (Entry<String, String> header : contentMetadataCodec.toHeaders(
|
||||
request.getPayload().getContentMetadata()).entries()) {
|
||||
builder.append(header.getKey()).append(": ").append(header.getValue()).append('\n');
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ import static org.testng.Assert.assertNull;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Date;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
|
||||
import org.jclouds.Constants;
|
||||
|
@ -42,7 +43,6 @@ import org.jclouds.http.functions.ParseSax;
|
|||
import org.jclouds.io.MutableContentMetadata;
|
||||
import org.jclouds.javax.annotation.Nullable;
|
||||
import org.jclouds.rest.functions.MapHttp4xxCodesToExceptions;
|
||||
import org.jclouds.rest.internal.RestAnnotationProcessor;
|
||||
import org.jclouds.util.Strings2;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
|
@ -86,11 +86,22 @@ public abstract class BaseRestClientTest {
|
|||
}
|
||||
|
||||
protected void assertPayloadEquals(HttpRequest request, String toMatch, String contentType, boolean contentMD5) {
|
||||
assertPayloadEquals(request, toMatch, contentType, null, null, null, contentMD5);
|
||||
assertPayloadEquals(request, toMatch, contentType, contentMD5, null);
|
||||
}
|
||||
|
||||
protected void assertPayloadEquals(HttpRequest request, String toMatch, String contentType, boolean contentMD5, Date expires) {
|
||||
assertPayloadEquals(request, toMatch, contentType, null, null, null, contentMD5, expires);
|
||||
}
|
||||
|
||||
protected void assertPayloadEquals(HttpRequest request, String toMatch, String contentType,
|
||||
String contentDispositon, String contentEncoding, String contentLanguage, boolean contentMD5) {
|
||||
assertPayloadEquals(request, toMatch, contentType, contentDispositon, contentEncoding, contentLanguage,
|
||||
contentMD5, null);
|
||||
}
|
||||
|
||||
protected void assertPayloadEquals(HttpRequest request, String toMatch, String contentType,
|
||||
String contentDispositon, String contentEncoding, String contentLanguage, boolean contentMD5,
|
||||
Date expires) {
|
||||
if (request.getPayload() == null) {
|
||||
assertNull(toMatch);
|
||||
} else {
|
||||
|
@ -104,7 +115,7 @@ public abstract class BaseRestClientTest {
|
|||
Long length = new Long(payload.getBytes().length);
|
||||
try {
|
||||
assertContentHeadersEqual(request, contentType, contentDispositon, contentEncoding, contentLanguage,
|
||||
length, contentMD5 ? CryptoStreams.md5(request.getPayload()) : null);
|
||||
length, contentMD5 ? CryptoStreams.md5(request.getPayload()) : null, expires);
|
||||
} catch (IOException e) {
|
||||
propagate(e);
|
||||
}
|
||||
|
@ -112,7 +123,7 @@ public abstract class BaseRestClientTest {
|
|||
}
|
||||
|
||||
protected void assertContentHeadersEqual(HttpRequest request, String contentType, String contentDispositon,
|
||||
String contentEncoding, String contentLanguage, Long length, byte[] contentMD5) {
|
||||
String contentEncoding, String contentLanguage, Long length, byte[] contentMD5, Date expires) {
|
||||
MutableContentMetadata md = request.getPayload().getContentMetadata();
|
||||
if (request.getFirstHeaderOrNull(TRANSFER_ENCODING) == null) {
|
||||
assertEquals(md.getContentLength(), length);
|
||||
|
@ -125,6 +136,7 @@ public abstract class BaseRestClientTest {
|
|||
assertEquals(md.getContentEncoding(), contentEncoding);
|
||||
assertEquals(md.getContentLanguage(), contentLanguage);
|
||||
assertEquals(md.getContentMD5(), contentMD5);
|
||||
assertEquals(md.getExpires(), expires);
|
||||
}
|
||||
|
||||
// FIXME Shouldn't be assertPayloadHeadersEqual?
|
||||
|
|
|
@ -39,6 +39,7 @@ import org.jclouds.http.handlers.DelegatingErrorHandler;
|
|||
import org.jclouds.http.handlers.DelegatingRetryHandler;
|
||||
import org.jclouds.http.internal.BaseHttpCommandExecutorService;
|
||||
import org.jclouds.http.internal.HttpWire;
|
||||
import org.jclouds.io.ContentMetadataCodec;
|
||||
import org.jclouds.io.Payload;
|
||||
import org.jclouds.io.Payloads;
|
||||
import org.jclouds.rest.internal.RestAnnotationProcessor;
|
||||
|
@ -55,19 +56,21 @@ import com.google.inject.Inject;
|
|||
*/
|
||||
public class ApacheHCHttpCommandExecutorService extends BaseHttpCommandExecutorService<HttpUriRequest> {
|
||||
private final HttpClient client;
|
||||
private final ApacheHCUtils apacheHCUtils;
|
||||
|
||||
@Inject
|
||||
ApacheHCHttpCommandExecutorService(HttpUtils utils,
|
||||
ApacheHCHttpCommandExecutorService(HttpUtils utils, ContentMetadataCodec contentMetadataCodec,
|
||||
@Named(Constants.PROPERTY_IO_WORKER_THREADS) ExecutorService ioWorkerExecutor,
|
||||
DelegatingRetryHandler retryHandler, IOExceptionRetryHandler ioRetryHandler,
|
||||
DelegatingErrorHandler errorHandler, HttpWire wire, HttpClient client) {
|
||||
super(utils, ioWorkerExecutor, retryHandler, ioRetryHandler, errorHandler, wire);
|
||||
super(utils, contentMetadataCodec, ioWorkerExecutor, retryHandler, ioRetryHandler, errorHandler, wire);
|
||||
this.client = client;
|
||||
this.apacheHCUtils = new ApacheHCUtils(contentMetadataCodec);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HttpUriRequest convert(HttpRequest request) throws IOException {
|
||||
HttpUriRequest returnVal = ApacheHCUtils.convertToApacheRequest(request);
|
||||
HttpUriRequest returnVal = apacheHCUtils.convertToApacheRequest(request);
|
||||
if (request.getPayload() != null && request.getPayload().getContentMetadata().getContentMD5() != null)
|
||||
returnVal.addHeader("Content-MD5", CryptoStreams.md5Base64(request.getPayload()));
|
||||
return returnVal;
|
||||
|
@ -93,8 +96,9 @@ public class ApacheHCHttpCommandExecutorService extends BaseHttpCommandExecutorS
|
|||
for (Header header : apacheResponse.getAllHeaders()) {
|
||||
headers.put(header.getName(), header.getValue());
|
||||
}
|
||||
if (payload != null)
|
||||
payload.getContentMetadata().setPropertiesFromHttpHeaders(headers);
|
||||
if (payload != null) {
|
||||
contentMetadataCodec.fromHeaders(payload.getContentMetadata(), headers);
|
||||
}
|
||||
return new HttpResponse(apacheResponse.getStatusLine().getStatusCode(), apacheResponse.getStatusLine()
|
||||
.getReasonPhrase(), payload, RestAnnotationProcessor.filterOutContentHeaders(headers));
|
||||
}
|
||||
|
|
|
@ -23,6 +23,8 @@ import java.io.IOException;
|
|||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
import javax.ws.rs.HttpMethod;
|
||||
|
@ -45,6 +47,8 @@ import org.apache.http.entity.StringEntity;
|
|||
import org.apache.http.params.CoreProtocolPNames;
|
||||
import org.jclouds.JcloudsVersion;
|
||||
import org.jclouds.http.HttpRequest;
|
||||
import org.jclouds.io.ContentMetadataCodec;
|
||||
import org.jclouds.io.MutableContentMetadata;
|
||||
import org.jclouds.io.Payload;
|
||||
import org.jclouds.io.payloads.BasePayload;
|
||||
import org.jclouds.io.payloads.ByteArrayPayload;
|
||||
|
@ -53,6 +57,7 @@ import org.jclouds.io.payloads.FilePayload;
|
|||
import org.jclouds.io.payloads.StringPayload;
|
||||
|
||||
import com.google.common.base.Throwables;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -62,8 +67,14 @@ import com.google.common.base.Throwables;
|
|||
public class ApacheHCUtils {
|
||||
//TODO: look up httpclient version
|
||||
public static final String USER_AGENT = String.format("jclouds/%s httpclient/%s", JcloudsVersion.get(), "4.1.1");
|
||||
|
||||
private final ContentMetadataCodec contentMetadataCodec;
|
||||
|
||||
public static HttpUriRequest convertToApacheRequest(HttpRequest request) {
|
||||
public ApacheHCUtils(ContentMetadataCodec contentMetadataCodec) {
|
||||
this.contentMetadataCodec = contentMetadataCodec;
|
||||
}
|
||||
|
||||
public HttpUriRequest convertToApacheRequest(HttpRequest request) {
|
||||
HttpUriRequest apacheRequest;
|
||||
if (request.getMethod().equals(HttpMethod.HEAD)) {
|
||||
apacheRequest = new HttpHead(request.getEndpoint());
|
||||
|
@ -120,7 +131,7 @@ public class ApacheHCUtils {
|
|||
return apacheRequest;
|
||||
}
|
||||
|
||||
public static void addEntityForContent(HttpEntityEnclosingRequest apacheRequest, Payload payload) {
|
||||
public void addEntityForContent(HttpEntityEnclosingRequest apacheRequest, Payload payload) {
|
||||
payload = payload instanceof DelegatingPayload ? DelegatingPayload.class.cast(payload).getDelegate() : payload;
|
||||
if (payload instanceof StringPayload) {
|
||||
StringEntity nStringEntity = null;
|
||||
|
@ -142,16 +153,20 @@ public class ApacheHCUtils {
|
|||
InputStream inputStream = payload.getInput();
|
||||
if (payload.getContentMetadata().getContentLength() == null)
|
||||
throw new IllegalArgumentException("you must specify size when content is an InputStream");
|
||||
InputStreamEntity Entity = new InputStreamEntity(inputStream, payload.getContentMetadata().getContentLength());
|
||||
Entity.setContentType(payload.getContentMetadata().getContentType());
|
||||
apacheRequest.setEntity(Entity);
|
||||
InputStreamEntity entity = new InputStreamEntity(inputStream, payload.getContentMetadata().getContentLength());
|
||||
entity.setContentType(payload.getContentMetadata().getContentType());
|
||||
apacheRequest.setEntity(entity);
|
||||
}
|
||||
if (payload.getContentMetadata().getContentDisposition() != null)
|
||||
apacheRequest.addHeader("Content-Disposition", payload.getContentMetadata().getContentDisposition());
|
||||
if (payload.getContentMetadata().getContentEncoding() != null)
|
||||
apacheRequest.addHeader("Content-Encoding", payload.getContentMetadata().getContentEncoding());
|
||||
if (payload.getContentMetadata().getContentLanguage() != null)
|
||||
apacheRequest.addHeader("Content-Language", payload.getContentMetadata().getContentLanguage());
|
||||
|
||||
// TODO Reproducing old behaviour exactly; ignoring Content-Type, Content-Length and Content-MD5
|
||||
Set<String> desiredHeaders = ImmutableSet.of("Content-Disposition", "Content-Encoding", "Content-Language", "Expires");
|
||||
MutableContentMetadata md = payload.getContentMetadata();
|
||||
for (Map.Entry<String,String> entry : contentMetadataCodec.toHeaders(md).entries()) {
|
||||
if (desiredHeaders.contains(entry.getKey())) {
|
||||
apacheRequest.addHeader(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
assert (apacheRequest.getEntity() != null);
|
||||
}
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@ import javax.ws.rs.core.HttpHeaders;
|
|||
|
||||
import org.jclouds.http.HttpRequest;
|
||||
import org.jclouds.http.HttpUtils;
|
||||
import org.jclouds.io.ContentMetadataCodec;
|
||||
import org.jclouds.io.Payload;
|
||||
|
||||
import com.google.appengine.api.urlfetch.FetchOptions;
|
||||
|
@ -52,14 +53,17 @@ import com.google.common.io.Closeables;
|
|||
@Singleton
|
||||
public class ConvertToGaeRequest implements Function<HttpRequest, HTTPRequest> {
|
||||
public static final String USER_AGENT = "jclouds/1.0 urlfetch/1.4.3";
|
||||
protected final HttpUtils utils;
|
||||
// http://code.google.com/appengine/docs/java/urlfetch/overview.html
|
||||
public final Set<String> prohibitedHeaders = ImmutableSet.of("Accept-Encoding", "Content-Length", "Host", "Var",
|
||||
"X-Forwarded-For");
|
||||
|
||||
protected final HttpUtils utils;
|
||||
protected final ContentMetadataCodec contentMetadataCodec;
|
||||
|
||||
@Inject
|
||||
ConvertToGaeRequest(HttpUtils utils) {
|
||||
ConvertToGaeRequest(HttpUtils utils, ContentMetadataCodec contentMetadataCodec) {
|
||||
this.utils = utils;
|
||||
this.contentMetadataCodec = contentMetadataCodec;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -115,7 +119,7 @@ public class ConvertToGaeRequest implements Function<HttpRequest, HTTPRequest> {
|
|||
Closeables.closeQuietly(input);
|
||||
}
|
||||
|
||||
for (Entry<String, String> header : HttpUtils.getContentHeadersFromMetadata(
|
||||
for (Entry<String, String> header : contentMetadataCodec.toHeaders(
|
||||
request.getPayload().getContentMetadata()).entries()) {
|
||||
if (!prohibitedHeaders.contains(header.getKey()))
|
||||
gaeRequest.setHeader(new HTTPHeader(header.getKey(), header.getValue()));
|
||||
|
|
|
@ -21,6 +21,7 @@ package org.jclouds.gae;
|
|||
import javax.inject.Singleton;
|
||||
|
||||
import org.jclouds.http.HttpResponse;
|
||||
import org.jclouds.io.ContentMetadataCodec;
|
||||
import org.jclouds.io.Payload;
|
||||
import org.jclouds.io.Payloads;
|
||||
import org.jclouds.rest.internal.RestAnnotationProcessor;
|
||||
|
@ -30,6 +31,7 @@ import com.google.appengine.api.urlfetch.HTTPResponse;
|
|||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.LinkedHashMultimap;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.inject.Inject;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -38,6 +40,13 @@ import com.google.common.collect.Multimap;
|
|||
@Singleton
|
||||
public class ConvertToJcloudsResponse implements Function<HTTPResponse, HttpResponse> {
|
||||
|
||||
private final ContentMetadataCodec contentMetadataCodec;
|
||||
|
||||
@Inject
|
||||
public ConvertToJcloudsResponse(ContentMetadataCodec contentMetadataCodec) {
|
||||
this.contentMetadataCodec = contentMetadataCodec;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpResponse apply(HTTPResponse gaeResponse) {
|
||||
Payload payload = gaeResponse.getContent() != null ? Payloads.newByteArrayPayload(gaeResponse.getContent())
|
||||
|
@ -51,8 +60,9 @@ public class ConvertToJcloudsResponse implements Function<HTTPResponse, HttpResp
|
|||
headers.put(header.getName(), header.getValue());
|
||||
}
|
||||
|
||||
if (payload != null)
|
||||
payload.getContentMetadata().setPropertiesFromHttpHeaders(headers);
|
||||
if (payload != null) {
|
||||
contentMetadataCodec.fromHeaders(payload.getContentMetadata(), headers);
|
||||
}
|
||||
return new HttpResponse(gaeResponse.getResponseCode(), message, payload,
|
||||
RestAnnotationProcessor.filterOutContentHeaders(headers));
|
||||
}
|
||||
|
|
|
@ -37,6 +37,7 @@ import org.jclouds.http.handlers.DelegatingErrorHandler;
|
|||
import org.jclouds.http.handlers.DelegatingRetryHandler;
|
||||
import org.jclouds.http.internal.BaseHttpCommandExecutorService;
|
||||
import org.jclouds.http.internal.HttpWire;
|
||||
import org.jclouds.io.ContentMetadataCodec;
|
||||
|
||||
import com.google.appengine.api.urlfetch.HTTPRequest;
|
||||
import com.google.appengine.api.urlfetch.HTTPResponse;
|
||||
|
@ -60,11 +61,12 @@ public class GaeHttpCommandExecutorService extends BaseHttpCommandExecutorServic
|
|||
|
||||
@Inject
|
||||
public GaeHttpCommandExecutorService(URLFetchService urlFetchService, HttpUtils utils,
|
||||
ContentMetadataCodec contentMetadataCodec,
|
||||
@Named(Constants.PROPERTY_IO_WORKER_THREADS) ExecutorService ioExecutor,
|
||||
IOExceptionRetryHandler ioRetryHandler, DelegatingRetryHandler retryHandler,
|
||||
DelegatingErrorHandler errorHandler, HttpWire wire, ConvertToGaeRequest convertToGaeRequest,
|
||||
ConvertToJcloudsResponse convertToJcloudsResponse) {
|
||||
super(utils, ioExecutor, retryHandler, ioRetryHandler, errorHandler, wire);
|
||||
super(utils, contentMetadataCodec, ioExecutor, retryHandler, ioRetryHandler, errorHandler, wire);
|
||||
this.urlFetchService = urlFetchService;
|
||||
this.convertToGaeRequest = convertToGaeRequest;
|
||||
this.convertToJcloudsResponse = convertToJcloudsResponse;
|
||||
|
|
|
@ -32,9 +32,12 @@ import javax.ws.rs.HttpMethod;
|
|||
import javax.ws.rs.core.HttpHeaders;
|
||||
|
||||
import org.jclouds.crypto.Crypto;
|
||||
import org.jclouds.date.internal.SimpleDateCodecFactory;
|
||||
import org.jclouds.date.internal.SimpleDateFormatDateService;
|
||||
import org.jclouds.encryption.internal.JCECrypto;
|
||||
import org.jclouds.http.HttpRequest;
|
||||
import org.jclouds.http.HttpUtils;
|
||||
import org.jclouds.io.ContentMetadataCodec.DefaultContentMetadataCodec;
|
||||
import org.jclouds.io.Payloads;
|
||||
import org.jclouds.util.Strings2;
|
||||
import org.testng.annotations.BeforeTest;
|
||||
|
@ -71,7 +74,9 @@ public class ConvertToGaeRequestTest {
|
|||
@BeforeTest
|
||||
void setupClient() throws MalformedURLException {
|
||||
endPoint = URI.create("http://localhost:80/foo");
|
||||
req = new ConvertToGaeRequest(new HttpUtils(0, 0, 0, 0));
|
||||
req = new ConvertToGaeRequest(new HttpUtils(0, 0, 0, 0), new DefaultContentMetadataCodec(
|
||||
new SimpleDateCodecFactory(new SimpleDateFormatDateService())));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -34,8 +34,11 @@ import java.util.List;
|
|||
import javax.ws.rs.core.HttpHeaders;
|
||||
|
||||
import org.jclouds.crypto.Crypto;
|
||||
import org.jclouds.date.internal.SimpleDateCodecFactory;
|
||||
import org.jclouds.date.internal.SimpleDateFormatDateService;
|
||||
import org.jclouds.encryption.internal.JCECrypto;
|
||||
import org.jclouds.http.HttpResponse;
|
||||
import org.jclouds.io.ContentMetadataCodec.DefaultContentMetadataCodec;
|
||||
import org.jclouds.util.Strings2;
|
||||
import org.testng.annotations.BeforeTest;
|
||||
import org.testng.annotations.Test;
|
||||
|
@ -67,7 +70,8 @@ public class ConvertToJcloudsResponseTest {
|
|||
@BeforeTest
|
||||
void setupClient() throws MalformedURLException {
|
||||
endPoint = URI.create("http://localhost:80/foo");
|
||||
req = new ConvertToJcloudsResponse();
|
||||
req = new ConvertToJcloudsResponse(new DefaultContentMetadataCodec(
|
||||
new SimpleDateCodecFactory(new SimpleDateFormatDateService())));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -53,6 +53,9 @@ public class JodaDateService implements DateService {
|
|||
private static final DateTimeFormatter iso8601DateFormatter = DateTimeFormat.forPattern(
|
||||
"yyyy-MM-dd'T'HH:mm:ss.SSSZ").withLocale(Locale.US).withZone(DateTimeZone.forID("GMT"));
|
||||
|
||||
private static final DateTimeFormatter rfc1123DateFormat = DateTimeFormat.forPattern(
|
||||
"EEE, dd MMM yyyyy HH:mm:ss Z").withLocale(Locale.US).withZone(DateTimeZone.forID("GMT"));
|
||||
|
||||
public final Date fromSeconds(long seconds) {
|
||||
return new Date(seconds * 1000);
|
||||
}
|
||||
|
@ -124,4 +127,19 @@ public class JodaDateService implements DateService {
|
|||
toParse += tz;
|
||||
return iso8601SecondsDateFormatter.parseDateTime(toParse).toDate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String rfc1123DateFormat(Date dateTime) {
|
||||
return rfc1123DateFormat.print(new DateTime(dateTime));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String rfc1123DateFormat() {
|
||||
return rfc1123DateFormat(new Date());
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Date rfc1123DateParse(String toParse) {
|
||||
return rfc1123DateFormat.parseDateTime(toParse).toDate();
|
||||
}
|
||||
}
|
|
@ -23,17 +23,29 @@ import static org.testng.Assert.assertEquals;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.text.ParseException;
|
||||
import java.util.Date;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Set;
|
||||
|
||||
import org.jclouds.blobstore.BlobStore;
|
||||
import org.jclouds.blobstore.BlobStoreContext;
|
||||
import org.jclouds.blobstore.domain.BlobMetadata;
|
||||
import org.jclouds.date.DateCodec;
|
||||
import org.jclouds.date.internal.SimpleDateCodecFactory;
|
||||
import org.jclouds.date.internal.SimpleDateFormatDateService;
|
||||
import org.jclouds.domain.Location;
|
||||
import org.jclouds.io.ContentMetadataCodec;
|
||||
import org.jclouds.io.ContentMetadataCodec.DefaultContentMetadataCodec;
|
||||
import org.jclouds.s3.blobstore.integration.S3ContainerLiveTest;
|
||||
import org.jclouds.util.Strings2;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Module;
|
||||
|
||||
/**
|
||||
* @author Adrian Cole
|
||||
|
@ -44,6 +56,68 @@ public class AWSS3ContainerLiveTest extends S3ContainerLiveTest {
|
|||
provider = "aws-s3";
|
||||
}
|
||||
|
||||
@Test(groups = { "live" })
|
||||
public void testCreateBlobWithExpiry() throws InterruptedException, MalformedURLException, IOException {
|
||||
final String containerName = getScratchContainerName();
|
||||
BlobStore blobStore = view.getBlobStore();
|
||||
try {
|
||||
final String blobName = "hello";
|
||||
final Date expires = new Date( (System.currentTimeMillis() / 1000) * 1000 + 60*1000);
|
||||
|
||||
blobStore.createContainerInLocation(null, containerName, publicRead());
|
||||
blobStore.putBlob(containerName, blobStore.blobBuilder(blobName).payload(TEST_STRING).expires(expires).build());
|
||||
|
||||
assertConsistencyAwareBlobExpiryMetadata(containerName, blobName, expires);
|
||||
|
||||
} finally {
|
||||
recycleContainer(containerName);
|
||||
}
|
||||
}
|
||||
|
||||
@Test(groups = { "live" })
|
||||
public void testCreateBlobWithMalformedExpiry() throws InterruptedException, MalformedURLException, IOException {
|
||||
// Create a blob that has a malformed Expires value; requires overriding the ContentMetadataCodec in Guice...
|
||||
final ContentMetadataCodec contentMetadataCodec = new DefaultContentMetadataCodec(new SimpleDateCodecFactory(new SimpleDateFormatDateService())) {
|
||||
@Override
|
||||
protected DateCodec getExpiresDateCodec() {
|
||||
return new DateCodec() {
|
||||
@Override public Date toDate(String date) throws ParseException {
|
||||
return new Date();
|
||||
}
|
||||
@Override public String toString(Date date) {
|
||||
return "wrong";
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
Module customModule = new AbstractModule() {
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(ContentMetadataCodec.class).toInstance(contentMetadataCodec);
|
||||
}
|
||||
};
|
||||
|
||||
Iterable<Module> modules = Iterables.concat(setupModules(), ImmutableList.of(customModule));
|
||||
BlobStoreContext naughtyBlobStoreContext = createView(setupProperties(), modules);
|
||||
BlobStore naughtyBlobStore = naughtyBlobStoreContext.getBlobStore();
|
||||
|
||||
final String containerName = getScratchContainerName();
|
||||
|
||||
try {
|
||||
final String blobName = "hello";
|
||||
|
||||
naughtyBlobStore.createContainerInLocation(null, containerName, publicRead());
|
||||
naughtyBlobStore.putBlob(containerName, naughtyBlobStore.blobBuilder(blobName)
|
||||
.payload(TEST_STRING).expires(new Date(System.currentTimeMillis() + 60*1000)).build());
|
||||
|
||||
assertConsistencyAwareBlobExpiryMetadata(containerName, blobName, new Date(0));
|
||||
|
||||
} finally {
|
||||
recycleContainer(containerName);
|
||||
}
|
||||
}
|
||||
|
||||
@Test(groups = { "live" })
|
||||
public void testCreateBlobInLocation() throws InterruptedException, MalformedURLException, IOException {
|
||||
String payload = "my data";
|
||||
|
@ -88,5 +162,4 @@ public class AWSS3ContainerLiveTest extends S3ContainerLiveTest {
|
|||
}
|
||||
throw new NoSuchElementException("No location found with id '"+id+"'; contenders were "+locs);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -55,7 +55,8 @@ public class BlobPropertiesImpl implements Serializable, BlobProperties {
|
|||
|
||||
public BlobPropertiesImpl(BlobType type, String name, String container, URI url, Date lastModified, String eTag,
|
||||
long size, String contentType, @Nullable byte[] contentMD5, @Nullable String contentMetadata,
|
||||
@Nullable String contentLanguage, LeaseStatus leaseStatus, Map<String, String> metadata) {
|
||||
@Nullable String contentLanguage, @Nullable Date currentExpires, LeaseStatus leaseStatus,
|
||||
Map<String, String> metadata) {
|
||||
this.type = checkNotNull(type, "type");
|
||||
this.leaseStatus = checkNotNull(leaseStatus, "leaseStatus");
|
||||
this.name = checkNotNull(name, "name");
|
||||
|
@ -64,7 +65,7 @@ public class BlobPropertiesImpl implements Serializable, BlobProperties {
|
|||
this.lastModified = checkNotNull(lastModified, "lastModified");
|
||||
this.eTag = checkNotNull(eTag, "eTag");
|
||||
this.contentMetadata = new BaseImmutableContentMetadata(contentType, size, contentMD5, null, contentLanguage,
|
||||
contentMetadata);
|
||||
contentMetadata, currentExpires);
|
||||
this.metadata.putAll(checkNotNull(metadata, "metadata"));
|
||||
}
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@ import org.jclouds.crypto.CryptoStreams;
|
|||
import org.jclouds.date.DateService;
|
||||
import org.jclouds.http.HttpUtils;
|
||||
import org.jclouds.http.functions.ParseSax;
|
||||
import org.jclouds.io.ContentMetadataCodec;
|
||||
import org.xml.sax.Attributes;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
|
@ -63,6 +64,7 @@ public class ContainerNameEnumerationResultsHandler extends ParseSax.HandlerWith
|
|||
private StringBuilder currentText = new StringBuilder();
|
||||
|
||||
private final DateService dateParser;
|
||||
private final ContentMetadataCodec contentMetadataCodec;
|
||||
private String delimiter;
|
||||
private String currentName;
|
||||
private long currentSize;
|
||||
|
@ -70,6 +72,7 @@ public class ContainerNameEnumerationResultsHandler extends ParseSax.HandlerWith
|
|||
private String currentContentEncoding;
|
||||
private String currentContentLanguage;
|
||||
private BlobType currentBlobType;
|
||||
private Date currentExpires;
|
||||
private boolean inBlob;
|
||||
private boolean inBlobPrefix;
|
||||
private boolean inMetadata;
|
||||
|
@ -79,8 +82,9 @@ public class ContainerNameEnumerationResultsHandler extends ParseSax.HandlerWith
|
|||
private LeaseStatus currentLeaseStatus;
|
||||
|
||||
@Inject
|
||||
public ContainerNameEnumerationResultsHandler(DateService dateParser) {
|
||||
public ContainerNameEnumerationResultsHandler(DateService dateParser, ContentMetadataCodec contentMetadataCodec) {
|
||||
this.dateParser = dateParser;
|
||||
this.contentMetadataCodec = contentMetadataCodec;
|
||||
}
|
||||
|
||||
public ListBlobsResponse getResult() {
|
||||
|
@ -131,8 +135,8 @@ public class ContainerNameEnumerationResultsHandler extends ParseSax.HandlerWith
|
|||
} else if (qName.equals("Blob")) {
|
||||
BlobProperties md = new BlobPropertiesImpl(currentBlobType, currentName, containerUrl.getPath().replace("/",
|
||||
""), currentUrl, currentLastModified, currentETag, currentSize, currentContentType,
|
||||
currentContentMD5, currentContentEncoding, currentContentLanguage, currentLeaseStatus,
|
||||
currentMetadata);
|
||||
currentContentMD5, currentContentEncoding, currentContentLanguage, currentExpires,
|
||||
currentLeaseStatus, currentMetadata);
|
||||
blobMetadata.add(md);
|
||||
currentBlobType = null;
|
||||
currentName = null;
|
||||
|
@ -145,6 +149,7 @@ public class ContainerNameEnumerationResultsHandler extends ParseSax.HandlerWith
|
|||
currentContentLanguage = null;
|
||||
currentContentMD5 = null;
|
||||
currentLeaseStatus = null;
|
||||
currentExpires = null;
|
||||
currentMetadata = Maps.newHashMap();
|
||||
} else if (qName.equals("Url")) {
|
||||
currentUrl = HttpUtils.createUri(currentText.toString().trim());
|
||||
|
@ -172,6 +177,13 @@ public class ContainerNameEnumerationResultsHandler extends ParseSax.HandlerWith
|
|||
currentContentLanguage = currentText.toString().trim();
|
||||
if (currentContentLanguage.equals(""))
|
||||
currentContentLanguage = null;
|
||||
} else if (qName.equals("Expires")) {
|
||||
String trimmedCurrentText = currentText.toString().trim();
|
||||
if (trimmedCurrentText.equals("")) {
|
||||
currentExpires = null;
|
||||
} else {
|
||||
currentExpires = contentMetadataCodec.parseExpires(trimmedCurrentText);
|
||||
}
|
||||
}
|
||||
currentText = new StringBuilder();
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ package org.jclouds.azureblob.blobstore;
|
|||
import static org.testng.Assert.assertEquals;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Date;
|
||||
|
||||
import org.jclouds.azureblob.AzureBlobAsyncClient;
|
||||
import org.jclouds.azureblob.AzureBlobProviderMetadata;
|
||||
|
@ -86,6 +87,7 @@ public class AzureBlobRequestSignerTest extends BaseAsyncClientTest<AzureBlobAsy
|
|||
blob.getPayload().getContentMetadata().setContentLength(2l);
|
||||
blob.getPayload().getContentMetadata().setContentMD5(new byte[] { 0, 2, 4, 8 });
|
||||
blob.getPayload().getContentMetadata().setContentType("text/plain");
|
||||
blob.getPayload().getContentMetadata().setExpires(new Date(1000));
|
||||
|
||||
HttpRequest request = signer.signPutBlob("container", blob);
|
||||
|
||||
|
@ -93,7 +95,7 @@ public class AzureBlobRequestSignerTest extends BaseAsyncClientTest<AzureBlobAsy
|
|||
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", null, null, null, (long) 2l, new byte[] { 0, 2, 4, 8 });
|
||||
assertContentHeadersEqual(request, "text/plain", null, null, null, (long) 2l, new byte[] { 0, 2, 4, 8 }, new Date(1000));
|
||||
|
||||
assertEquals(request.getFilters().size(), 0);
|
||||
}
|
||||
|
|
|
@ -63,17 +63,17 @@ public class ContainerNameEnumerationResultsHandlerTest extends BaseHandlerTest
|
|||
new BlobPropertiesImpl(BlobType.BLOCK_BLOB, "blob1.txt", "mycontainer", URI
|
||||
.create("http://myaccount.blob.core.windows.net/mycontainer/blob1.txt"), dateService
|
||||
.rfc822DateParse("Thu, 18 Sep 2008 18:41:57 GMT"), "0x8CAE7D55D050B8B", 8,
|
||||
"text/plain; charset=UTF-8", null, null, null, LeaseStatus.UNLOCKED, ImmutableMap
|
||||
"text/plain; charset=UTF-8", null, null, null, null, LeaseStatus.UNLOCKED, ImmutableMap
|
||||
.<String, String> of()),
|
||||
new BlobPropertiesImpl(BlobType.BLOCK_BLOB, "blob2.txt", "mycontainer", URI
|
||||
.create("http://myaccount.blob.core.windows.net/mycontainer/blob2.txt"), dateService
|
||||
.rfc822DateParse("Thu, 18 Sep 2008 18:41:57 GMT"), "0x8CAE7D55CF6C339", 14,
|
||||
"text/plain; charset=UTF-8", null, null, null, LeaseStatus.UNLOCKED, ImmutableMap
|
||||
"text/plain; charset=UTF-8", null, null, null, null, LeaseStatus.UNLOCKED, ImmutableMap
|
||||
.<String, String> of()),
|
||||
new BlobPropertiesImpl(BlobType.PAGE_BLOB, "newblob1.txt", "mycontainer", URI
|
||||
.create("http://myaccount.blob.core.windows.net/mycontainer/newblob1.txt"), dateService
|
||||
.rfc822DateParse("Thu, 18 Sep 2008 18:41:57 GMT"), "0x8CAE7D55CF6C339", 25,
|
||||
"text/plain; charset=UTF-8", null, null, null, LeaseStatus.UNLOCKED, ImmutableMap
|
||||
"text/plain; charset=UTF-8", null, null, null, null, LeaseStatus.UNLOCKED, ImmutableMap
|
||||
.<String, String> of()));
|
||||
|
||||
ListBlobsResponse list = new HashSetListBlobsResponse(contents,
|
||||
|
@ -91,7 +91,7 @@ public class ContainerNameEnumerationResultsHandlerTest extends BaseHandlerTest
|
|||
Set<BlobProperties> contents = ImmutableSet.<BlobProperties> of(new BlobPropertiesImpl(BlobType.BLOCK_BLOB, "a",
|
||||
"adriancole-blobstore3", URI.create("https://jclouds.blob.core.windows.net/adriancole-blobstore3/a"),
|
||||
dateService.rfc822DateParse("Sat, 30 Jan 2010 17:46:15 GMT"), "0x8CC6FEB41736428", 8,
|
||||
"application/octet-stream", null, null, null, LeaseStatus.UNLOCKED, ImmutableMap.<String, String> of()));
|
||||
"application/octet-stream", null, null, null, null, LeaseStatus.UNLOCKED, ImmutableMap.<String, String> of()));
|
||||
|
||||
ListBlobsResponse list = new HashSetListBlobsResponse(contents,
|
||||
URI.create("https://jclouds.blob.core.windows.net/adriancole-blobstore3"),
|
||||
|
|
Loading…
Reference in New Issue