mirror of https://github.com/apache/jclouds.git
Issue 647: store Expires as Date; added ContentMetadataCodec for converting to/from HTTP headers
This commit is contained in:
parent
9aedf7b6f6
commit
cd9c830c5a
|
@ -19,6 +19,7 @@
|
||||||
package org.jclouds.atmos.domain.internal;
|
package org.jclouds.atmos.domain.internal;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
import org.jclouds.atmos.domain.MutableContentMetadata;
|
import org.jclouds.atmos.domain.MutableContentMetadata;
|
||||||
import org.jclouds.io.ContentMetadataBuilder;
|
import org.jclouds.io.ContentMetadataBuilder;
|
||||||
|
@ -154,17 +155,12 @@ public class DelegatingMutableContentMetadata implements MutableContentMetadata
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setPropertiesFromHttpHeaders(Multimap<String, String> headers) {
|
public void setExpires(Date expires) {
|
||||||
delegate.setPropertiesFromHttpHeaders(headers);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setExpires(String expires) {
|
|
||||||
delegate.setExpires(expires);
|
delegate.setExpires(expires);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getExpires() {
|
public Date getExpires() {
|
||||||
return delegate.getExpires();
|
return delegate.getExpires();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,9 @@ package org.jclouds.atmos.blobstore;
|
||||||
import static org.testng.Assert.assertEquals;
|
import static org.testng.Assert.assertEquals;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.text.ParseException;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
import org.jclouds.apis.ApiMetadata;
|
import org.jclouds.apis.ApiMetadata;
|
||||||
import org.jclouds.atmos.AtmosApiMetadata;
|
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.blobstore.domain.Blob.Factory;
|
||||||
import org.jclouds.date.TimeStamp;
|
import org.jclouds.date.TimeStamp;
|
||||||
import org.jclouds.http.HttpRequest;
|
import org.jclouds.http.HttpRequest;
|
||||||
|
import org.jclouds.io.ContentMetadata;
|
||||||
import org.jclouds.rest.ConfiguresRestClient;
|
import org.jclouds.rest.ConfiguresRestClient;
|
||||||
import org.jclouds.rest.internal.BaseAsyncClientTest;
|
import org.jclouds.rest.internal.BaseAsyncClientTest;
|
||||||
import org.jclouds.rest.internal.RestAnnotationProcessor;
|
import org.jclouds.rest.internal.RestAnnotationProcessor;
|
||||||
|
@ -89,7 +93,7 @@ public class AtmosBlobRequestSignerTest extends BaseAsyncClientTest<AtmosAsyncCl
|
||||||
blob.getPayload().getContentMetadata().setContentLength(2l);
|
blob.getPayload().getContentMetadata().setContentLength(2l);
|
||||||
blob.getPayload().getContentMetadata().setContentMD5(new byte[] { 0, 2, 4, 8 });
|
blob.getPayload().getContentMetadata().setContentMD5(new byte[] { 0, 2, 4, 8 });
|
||||||
blob.getPayload().getContentMetadata().setContentType("text/plain");
|
blob.getPayload().getContentMetadata().setContentType("text/plain");
|
||||||
blob.getPayload().getContentMetadata().setExpires("Thu, 01 Dec 1994 16:00:00 GMT");
|
blob.getPayload().getContentMetadata().setExpires(new Date(1000));
|
||||||
|
|
||||||
HttpRequest request = signer.signPutBlob("container", blob);
|
HttpRequest request = signer.signPutBlob("container", blob);
|
||||||
|
|
||||||
|
@ -99,7 +103,7 @@ public class AtmosBlobRequestSignerTest extends BaseAsyncClientTest<AtmosAsyncCl
|
||||||
request,
|
request,
|
||||||
"Accept: */*\nDate: Thu, 05 Jun 2008 16:38:19 GMT\nx-emc-signature: aLpB1oQaCA27AXT6Nzam7s0f0pI=\nx-emc-uid: identity\n");
|
"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 }, "Thu, 01 Dec 1994 16:00:00 GMT");
|
assertContentHeadersEqual(request, "text/plain", null, null, null, (long) 2l, new byte[] { 0, 2, 4, 8 }, new Date(1000));
|
||||||
|
|
||||||
assertEquals(request.getFilters().size(), 0);
|
assertEquals(request.getFilters().size(), 0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ package org.jclouds.s3.blobstore;
|
||||||
import static org.testng.Assert.assertEquals;
|
import static org.testng.Assert.assertEquals;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
import javax.inject.Provider;
|
import javax.inject.Provider;
|
||||||
|
|
||||||
|
@ -89,7 +90,7 @@ public class S3BlobRequestSignerTest extends BaseS3AsyncClientTest<S3AsyncClient
|
||||||
public void testSignPutBlob() throws ArrayIndexOutOfBoundsException, SecurityException, IllegalArgumentException,
|
public void testSignPutBlob() throws ArrayIndexOutOfBoundsException, SecurityException, IllegalArgumentException,
|
||||||
NoSuchMethodException, IOException {
|
NoSuchMethodException, IOException {
|
||||||
Blob blob = blobFactory.get().name("name").forSigning().contentLength(2l).contentMD5(new byte[] { 0, 2, 4, 8 }).contentType(
|
Blob blob = blobFactory.get().name("name").forSigning().contentLength(2l).contentMD5(new byte[] { 0, 2, 4, 8 }).contentType(
|
||||||
"text/plain").expires("Thu, 01 Dec 1994 16:00:00 GMT").build();
|
"text/plain").expires(new Date(1000)).build();
|
||||||
|
|
||||||
HttpRequest request = signer.signPutBlob("container", blob);
|
HttpRequest request = signer.signPutBlob("container", blob);
|
||||||
|
|
||||||
|
@ -98,7 +99,7 @@ public class S3BlobRequestSignerTest extends BaseS3AsyncClientTest<S3AsyncClient
|
||||||
request,
|
request,
|
||||||
"Authorization: AWS identity:j9Dy/lmmvlCKjA4lkqZenLxMkR4=\nDate: Thu, 05 Jun 2008 16:38:19 GMT\nHost: container.s3.amazonaws.com\n");
|
"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 }, "Thu, 01 Dec 1994 16:00:00 GMT");
|
assertContentHeadersEqual(request, "text/plain", null, null, null, (long) 2l, new byte[] { 0, 2, 4, 8 }, new Date(1000));
|
||||||
|
|
||||||
assertEquals(request.getFilters().size(), 0);
|
assertEquals(request.getFilters().size(), 0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ package org.jclouds.openstack.swift.blobstore;
|
||||||
import static org.testng.Assert.assertEquals;
|
import static org.testng.Assert.assertEquals;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
import org.jclouds.blobstore.BlobRequestSigner;
|
import org.jclouds.blobstore.BlobRequestSigner;
|
||||||
import org.jclouds.blobstore.domain.Blob;
|
import org.jclouds.blobstore.domain.Blob;
|
||||||
|
@ -72,13 +73,13 @@ public class SwiftBlobRequestSignerTest extends CommonSwiftClientTest {
|
||||||
blob.getPayload().getContentMetadata().setContentLength(2l);
|
blob.getPayload().getContentMetadata().setContentLength(2l);
|
||||||
blob.getPayload().getContentMetadata().setContentMD5(new byte[] { 0, 2, 4, 8 });
|
blob.getPayload().getContentMetadata().setContentMD5(new byte[] { 0, 2, 4, 8 });
|
||||||
blob.getPayload().getContentMetadata().setContentType("text/plain");
|
blob.getPayload().getContentMetadata().setContentType("text/plain");
|
||||||
blob.getPayload().getContentMetadata().setExpires("Thu, 01 Dec 1994 16:00:00 GMT");
|
blob.getPayload().getContentMetadata().setExpires(new Date(1000));
|
||||||
|
|
||||||
HttpRequest request = signer.signPutBlob("container", blob);
|
HttpRequest request = signer.signPutBlob("container", blob);
|
||||||
|
|
||||||
assertRequestLineEquals(request, "PUT http://storage/container/name HTTP/1.1");
|
assertRequestLineEquals(request, "PUT http://storage/container/name HTTP/1.1");
|
||||||
assertNonPayloadHeadersEqual(request, "X-Auth-Token: testtoken\n");
|
assertNonPayloadHeadersEqual(request, "X-Auth-Token: testtoken\n");
|
||||||
assertContentHeadersEqual(request, "text/plain", null, null, null, (long) 2l, new byte[] { 0, 2, 4, 8 }, "Thu, 01 Dec 1994 16:00:00 GMT");
|
assertContentHeadersEqual(request, "text/plain", null, null, null, (long) 2l, new byte[] { 0, 2, 4, 8 }, new Date(1000));
|
||||||
|
|
||||||
assertEquals(request.getFilters().size(), 0);
|
assertEquals(request.getFilters().size(), 0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -92,6 +92,7 @@ import org.jclouds.http.HttpResponseException;
|
||||||
import org.jclouds.http.HttpUtils;
|
import org.jclouds.http.HttpUtils;
|
||||||
import org.jclouds.http.options.HttpRequestOptions;
|
import org.jclouds.http.options.HttpRequestOptions;
|
||||||
import org.jclouds.io.ContentMetadata;
|
import org.jclouds.io.ContentMetadata;
|
||||||
|
import org.jclouds.io.ContentMetadataCodec;
|
||||||
import org.jclouds.io.MutableContentMetadata;
|
import org.jclouds.io.MutableContentMetadata;
|
||||||
import org.jclouds.io.Payload;
|
import org.jclouds.io.Payload;
|
||||||
import org.jclouds.io.Payloads;
|
import org.jclouds.io.Payloads;
|
||||||
|
@ -127,6 +128,7 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore {
|
||||||
protected final IfDirectoryReturnNameStrategy ifDirectoryReturnName;
|
protected final IfDirectoryReturnNameStrategy ifDirectoryReturnName;
|
||||||
protected final Factory blobFactory;
|
protected final Factory blobFactory;
|
||||||
protected final TransientStorageStrategy storageStrategy;
|
protected final TransientStorageStrategy storageStrategy;
|
||||||
|
protected final ContentMetadataCodec contentMetadataCodec;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
protected TransientAsyncBlobStore(BlobStoreContext context,
|
protected TransientAsyncBlobStore(BlobStoreContext context,
|
||||||
|
@ -137,7 +139,8 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore {
|
||||||
@Named(Constants.PROPERTY_USER_THREADS) ExecutorService service,
|
@Named(Constants.PROPERTY_USER_THREADS) ExecutorService service,
|
||||||
Supplier<Location> defaultLocation,
|
Supplier<Location> defaultLocation,
|
||||||
@Memoized Supplier<Set<? extends Location>> locations,
|
@Memoized Supplier<Set<? extends Location>> locations,
|
||||||
Factory blobFactory, Provider<UriBuilder> uriBuilders) {
|
Factory blobFactory, Provider<UriBuilder> uriBuilders,
|
||||||
|
ContentMetadataCodec contentMetadataCodec) {
|
||||||
super(context, blobUtils, service, defaultLocation, locations);
|
super(context, blobUtils, service, defaultLocation, locations);
|
||||||
this.blobFactory = blobFactory;
|
this.blobFactory = blobFactory;
|
||||||
this.dateService = dateService;
|
this.dateService = dateService;
|
||||||
|
@ -146,6 +149,7 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore {
|
||||||
this.httpGetOptionsConverter = httpGetOptionsConverter;
|
this.httpGetOptionsConverter = httpGetOptionsConverter;
|
||||||
this.ifDirectoryReturnName = ifDirectoryReturnName;
|
this.ifDirectoryReturnName = ifDirectoryReturnName;
|
||||||
this.storageStrategy = new TransientStorageStrategy(defaultLocation);
|
this.storageStrategy = new TransientStorageStrategy(defaultLocation);
|
||||||
|
this.contentMetadataCodec = contentMetadataCodec;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -521,7 +525,7 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void copyPayloadHeadersToBlob(Payload payload, Blob blob) {
|
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.HttpRequest;
|
||||||
import org.jclouds.http.HttpUtils;
|
import org.jclouds.http.HttpUtils;
|
||||||
import org.jclouds.http.filters.BasicAuthentication;
|
import org.jclouds.http.filters.BasicAuthentication;
|
||||||
|
import org.jclouds.io.ContentMetadataCodec;
|
||||||
import org.jclouds.location.Provider;
|
import org.jclouds.location.Provider;
|
||||||
|
|
||||||
import com.google.common.base.Supplier;
|
import com.google.common.base.Supplier;
|
||||||
|
@ -45,12 +46,15 @@ public class TransientBlobRequestSigner implements BlobRequestSigner {
|
||||||
private final BasicAuthentication basicAuth;
|
private final BasicAuthentication basicAuth;
|
||||||
private final BlobToHttpGetOptions blob2HttpGetOptions;
|
private final BlobToHttpGetOptions blob2HttpGetOptions;
|
||||||
private final Supplier<URI> endpoint;
|
private final Supplier<URI> endpoint;
|
||||||
|
private final ContentMetadataCodec contentMetadataCodec;
|
||||||
|
|
||||||
@Inject
|
@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.basicAuth = checkNotNull(basicAuth, "basicAuth");
|
||||||
this.blob2HttpGetOptions = checkNotNull(blob2HttpGetOptions, "blob2HttpGetOptions");
|
this.blob2HttpGetOptions = checkNotNull(blob2HttpGetOptions, "blob2HttpGetOptions");
|
||||||
this.endpoint = endpoint;
|
this.endpoint = endpoint;
|
||||||
|
this.contentMetadataCodec = contentMetadataCodec;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -64,7 +68,7 @@ public class TransientBlobRequestSigner implements BlobRequestSigner {
|
||||||
HttpRequest request = HttpRequest.builder().method("PUT").endpoint(
|
HttpRequest request = HttpRequest.builder().method("PUT").endpoint(
|
||||||
URI.create(String.format("%s/%s/%s", endpoint.get(), container, blob.getMetadata().getName()))).payload(
|
URI.create(String.format("%s/%s/%s", endpoint.get(), container, blob.getMetadata().getName()))).payload(
|
||||||
blob.getPayload()).headers(
|
blob.getPayload()).headers(
|
||||||
HttpUtils.getContentHeadersFromMetadata(blob.getMetadata().getContentMetadata())).build();
|
contentMetadataCodec.toHeaders(blob.getMetadata().getContentMetadata())).build();
|
||||||
return basicAuth.filter(request);
|
return basicAuth.filter(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ package org.jclouds.blobstore.domain;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.jclouds.blobstore.domain.internal.BlobBuilderImpl;
|
import org.jclouds.blobstore.domain.internal.BlobBuilderImpl;
|
||||||
|
@ -117,7 +118,7 @@ public interface BlobBuilder {
|
||||||
|
|
||||||
PayloadBlobBuilder contentEncoding(String contentEncoding);
|
PayloadBlobBuilder contentEncoding(String contentEncoding);
|
||||||
|
|
||||||
PayloadBlobBuilder expires(String expires);
|
PayloadBlobBuilder expires(Date expires);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
|
|
@ -24,6 +24,7 @@ import static org.jclouds.io.Payloads.newPayload;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
@ -222,7 +223,7 @@ public class BlobBuilderImpl implements BlobBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PayloadBlobBuilder expires(String expires) {
|
public PayloadBlobBuilder expires(Date expires) {
|
||||||
payload.getContentMetadata().setExpires(expires);
|
payload.getContentMetadata().setExpires(expires);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ import static org.jclouds.blobstore.util.BlobStoreUtils.getContentAsStringOrNull
|
||||||
import static org.testng.Assert.assertEquals;
|
import static org.testng.Assert.assertEquals;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
import java.util.Set;
|
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)
|
protected void assertConsistencyAwareBlobInLocation(final String containerName, final String blobName, final Location loc)
|
||||||
throws InterruptedException {
|
throws InterruptedException {
|
||||||
assertConsistencyAware(new Runnable() {
|
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,47 @@
|
||||||
|
package org.jclouds.date;
|
||||||
|
|
||||||
|
import java.text.ParseException;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
public class DateCodecs {
|
||||||
|
|
||||||
|
// 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";
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Use default Java Date/SimpleDateFormat classes for date manipulation, but be *very* careful to
|
||||||
|
* guard against the lack of thread safety.
|
||||||
|
*/
|
||||||
|
// @GuardedBy("this")
|
||||||
|
private static final SimpleDateFormat rfc1123SimpleDateFormat = new SimpleDateFormat(RFC1123_DATE_PATTERN, Locale.US);
|
||||||
|
|
||||||
|
public static DateCodec rfc1123() {
|
||||||
|
return new SimpleDateCodec(rfc1123SimpleDateFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class SimpleDateCodec implements DateCodec {
|
||||||
|
|
||||||
|
private final SimpleDateFormat dateFormat;
|
||||||
|
|
||||||
|
SimpleDateCodec(SimpleDateFormat dateFormat) {
|
||||||
|
this.dateFormat = dateFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Date toDate(String date) throws ParseException {
|
||||||
|
synchronized (dateFormat) {
|
||||||
|
return dateFormat.parse(date);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString(Date date) {
|
||||||
|
synchronized (dateFormat) {
|
||||||
|
return dateFormat.format(date);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -46,7 +46,6 @@ import java.util.regex.Matcher;
|
||||||
|
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
import javax.ws.rs.core.HttpHeaders;
|
|
||||||
|
|
||||||
import org.jclouds.Constants;
|
import org.jclouds.Constants;
|
||||||
import org.jclouds.crypto.CryptoStreams;
|
import org.jclouds.crypto.CryptoStreams;
|
||||||
|
@ -63,8 +62,6 @@ import org.jclouds.util.Strings2;
|
||||||
import com.google.common.base.Joiner;
|
import com.google.common.base.Joiner;
|
||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
import com.google.common.base.Splitter;
|
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.Multimap;
|
||||||
import com.google.common.collect.SortedSetMultimap;
|
import com.google.common.collect.SortedSetMultimap;
|
||||||
import com.google.common.collect.TreeMultimap;
|
import com.google.common.collect.TreeMultimap;
|
||||||
|
@ -184,25 +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()));
|
|
||||||
if (md.getExpires() != null)
|
|
||||||
builder.put(HttpHeaders.EXPIRES, md.getExpires());
|
|
||||||
return builder.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static byte[] toByteArrayOrNull(PayloadEnclosing response) {
|
public static byte[] toByteArrayOrNull(PayloadEnclosing response) {
|
||||||
if (response.getPayload() != null) {
|
if (response.getPayload() != null) {
|
||||||
InputStream input = response.getPayload().getInput();
|
InputStream input = response.getPayload().getInput();
|
||||||
|
|
|
@ -45,6 +45,7 @@ import org.jclouds.http.HttpUtils;
|
||||||
import org.jclouds.http.IOExceptionRetryHandler;
|
import org.jclouds.http.IOExceptionRetryHandler;
|
||||||
import org.jclouds.http.handlers.DelegatingErrorHandler;
|
import org.jclouds.http.handlers.DelegatingErrorHandler;
|
||||||
import org.jclouds.http.handlers.DelegatingRetryHandler;
|
import org.jclouds.http.handlers.DelegatingRetryHandler;
|
||||||
|
import org.jclouds.io.ContentMetadataCodec;
|
||||||
import org.jclouds.logging.Logger;
|
import org.jclouds.logging.Logger;
|
||||||
import org.jclouds.util.Throwables2;
|
import org.jclouds.util.Throwables2;
|
||||||
|
|
||||||
|
@ -56,7 +57,8 @@ import com.google.common.io.NullOutputStream;
|
||||||
*/
|
*/
|
||||||
public abstract class BaseHttpCommandExecutorService<Q> implements HttpCommandExecutorService {
|
public abstract class BaseHttpCommandExecutorService<Q> implements HttpCommandExecutorService {
|
||||||
protected final HttpUtils utils;
|
protected final HttpUtils utils;
|
||||||
|
protected final ContentMetadataCodec contentMetadataCodec;
|
||||||
|
|
||||||
private final DelegatingRetryHandler retryHandler;
|
private final DelegatingRetryHandler retryHandler;
|
||||||
private final IOExceptionRetryHandler ioRetryHandler;
|
private final IOExceptionRetryHandler ioRetryHandler;
|
||||||
private final DelegatingErrorHandler errorHandler;
|
private final DelegatingErrorHandler errorHandler;
|
||||||
|
@ -71,11 +73,12 @@ public abstract class BaseHttpCommandExecutorService<Q> implements HttpCommandEx
|
||||||
protected final HttpWire wire;
|
protected final HttpWire wire;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
protected BaseHttpCommandExecutorService(HttpUtils utils,
|
protected BaseHttpCommandExecutorService(HttpUtils utils, ContentMetadataCodec contentMetadataCodec,
|
||||||
@Named(Constants.PROPERTY_IO_WORKER_THREADS) ExecutorService ioWorkerExecutor,
|
@Named(Constants.PROPERTY_IO_WORKER_THREADS) ExecutorService ioWorkerExecutor,
|
||||||
DelegatingRetryHandler retryHandler, IOExceptionRetryHandler ioRetryHandler,
|
DelegatingRetryHandler retryHandler, IOExceptionRetryHandler ioRetryHandler,
|
||||||
DelegatingErrorHandler errorHandler, HttpWire wire) {
|
DelegatingErrorHandler errorHandler, HttpWire wire) {
|
||||||
this.utils = checkNotNull(utils, "utils");
|
this.utils = checkNotNull(utils, "utils");
|
||||||
|
this.contentMetadataCodec = checkNotNull(contentMetadataCodec, "contentMetadataCodec");
|
||||||
this.retryHandler = checkNotNull(retryHandler, "retryHandler");
|
this.retryHandler = checkNotNull(retryHandler, "retryHandler");
|
||||||
this.ioRetryHandler = checkNotNull(ioRetryHandler, "ioRetryHandler");
|
this.ioRetryHandler = checkNotNull(ioRetryHandler, "ioRetryHandler");
|
||||||
this.errorHandler = checkNotNull(errorHandler, "errorHandler");
|
this.errorHandler = checkNotNull(errorHandler, "errorHandler");
|
||||||
|
|
|
@ -54,7 +54,6 @@ import javax.ws.rs.core.HttpHeaders;
|
||||||
|
|
||||||
import org.jclouds.Constants;
|
import org.jclouds.Constants;
|
||||||
import org.jclouds.JcloudsVersion;
|
import org.jclouds.JcloudsVersion;
|
||||||
import org.jclouds.crypto.CryptoStreams;
|
|
||||||
import org.jclouds.http.HttpCommandExecutorService;
|
import org.jclouds.http.HttpCommandExecutorService;
|
||||||
import org.jclouds.http.HttpRequest;
|
import org.jclouds.http.HttpRequest;
|
||||||
import org.jclouds.http.HttpResponse;
|
import org.jclouds.http.HttpResponse;
|
||||||
|
@ -62,6 +61,7 @@ import org.jclouds.http.HttpUtils;
|
||||||
import org.jclouds.http.IOExceptionRetryHandler;
|
import org.jclouds.http.IOExceptionRetryHandler;
|
||||||
import org.jclouds.http.handlers.DelegatingErrorHandler;
|
import org.jclouds.http.handlers.DelegatingErrorHandler;
|
||||||
import org.jclouds.http.handlers.DelegatingRetryHandler;
|
import org.jclouds.http.handlers.DelegatingRetryHandler;
|
||||||
|
import org.jclouds.io.ContentMetadataCodec;
|
||||||
import org.jclouds.io.MutableContentMetadata;
|
import org.jclouds.io.MutableContentMetadata;
|
||||||
import org.jclouds.io.Payload;
|
import org.jclouds.io.Payload;
|
||||||
import org.jclouds.logging.Logger;
|
import org.jclouds.logging.Logger;
|
||||||
|
@ -90,13 +90,13 @@ public class JavaUrlHttpCommandExecutorService extends BaseHttpCommandExecutorSe
|
||||||
private final Field methodField;
|
private final Field methodField;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public JavaUrlHttpCommandExecutorService(HttpUtils utils,
|
public JavaUrlHttpCommandExecutorService(HttpUtils utils, ContentMetadataCodec contentMetadataCodec,
|
||||||
@Named(Constants.PROPERTY_IO_WORKER_THREADS) ExecutorService ioWorkerExecutor,
|
@Named(Constants.PROPERTY_IO_WORKER_THREADS) ExecutorService ioWorkerExecutor,
|
||||||
DelegatingRetryHandler retryHandler, IOExceptionRetryHandler ioRetryHandler,
|
DelegatingRetryHandler retryHandler, IOExceptionRetryHandler ioRetryHandler,
|
||||||
DelegatingErrorHandler errorHandler, HttpWire wire, @Named("untrusted") HostnameVerifier verifier,
|
DelegatingErrorHandler errorHandler, HttpWire wire, @Named("untrusted") HostnameVerifier verifier,
|
||||||
@Named("untrusted") Supplier<SSLContext> untrustedSSLContextProvider) throws SecurityException,
|
@Named("untrusted") Supplier<SSLContext> untrustedSSLContextProvider) throws SecurityException,
|
||||||
NoSuchFieldException {
|
NoSuchFieldException {
|
||||||
super(utils, ioWorkerExecutor, retryHandler, ioRetryHandler, errorHandler, wire);
|
super(utils, contentMetadataCodec, ioWorkerExecutor, retryHandler, ioRetryHandler, errorHandler, wire);
|
||||||
if (utils.getMaxConnections() > 0)
|
if (utils.getMaxConnections() > 0)
|
||||||
System.setProperty("http.maxConnections", String.valueOf(checkNotNull(utils, "utils").getMaxConnections()));
|
System.setProperty("http.maxConnections", String.valueOf(checkNotNull(utils, "utils").getMaxConnections()));
|
||||||
this.untrustedSSLContextProvider = checkNotNull(untrustedSSLContextProvider, "untrustedSSLContextProvider");
|
this.untrustedSSLContextProvider = checkNotNull(untrustedSSLContextProvider, "untrustedSSLContextProvider");
|
||||||
|
@ -136,7 +136,7 @@ public class JavaUrlHttpCommandExecutorService extends BaseHttpCommandExecutorSe
|
||||||
ImmutableMultimap<String, String> headers = headerBuilder.build();
|
ImmutableMultimap<String, String> headers = headerBuilder.build();
|
||||||
if (in != null) {
|
if (in != null) {
|
||||||
Payload payload = newInputStreamPayload(in);
|
Payload payload = newInputStreamPayload(in);
|
||||||
payload.getContentMetadata().setPropertiesFromHttpHeaders(headers);
|
contentMetadataCodec.fromHeaders(payload.getContentMetadata(), headers);
|
||||||
builder.payload(payload);
|
builder.payload(payload);
|
||||||
}
|
}
|
||||||
builder.headers(RestAnnotationProcessor.filterOutContentHeaders(headers));
|
builder.headers(RestAnnotationProcessor.filterOutContentHeaders(headers));
|
||||||
|
@ -214,18 +214,9 @@ public class JavaUrlHttpCommandExecutorService extends BaseHttpCommandExecutorSe
|
||||||
|
|
||||||
if (request.getPayload() != null) {
|
if (request.getPayload() != null) {
|
||||||
MutableContentMetadata md = request.getPayload().getContentMetadata();
|
MutableContentMetadata md = request.getPayload().getContentMetadata();
|
||||||
if (md.getContentMD5() != null)
|
for (Map.Entry<String,String> entry : contentMetadataCodec.toHeaders(md).entries()) {
|
||||||
connection.setRequestProperty("Content-MD5", CryptoStreams.base64(md.getContentMD5()));
|
connection.setRequestProperty(entry.getKey(), entry.getValue());
|
||||||
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());
|
|
||||||
if (md.getExpires() != null)
|
|
||||||
connection.setRequestProperty("Expires", md.getExpires());
|
|
||||||
if (chunked) {
|
if (chunked) {
|
||||||
connection.setChunkedStreamingMode(8196);
|
connection.setChunkedStreamingMode(8196);
|
||||||
} else {
|
} 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_LENGTH;
|
||||||
import static javax.ws.rs.core.HttpHeaders.CONTENT_TYPE;
|
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 java.util.Set;
|
||||||
|
|
||||||
import org.jclouds.javax.annotation.Nullable;
|
import org.jclouds.javax.annotation.Nullable;
|
||||||
|
@ -32,8 +34,12 @@ import com.google.common.collect.ImmutableSet;
|
||||||
*/
|
*/
|
||||||
public interface ContentMetadata {
|
public interface ContentMetadata {
|
||||||
public static final Set<String> HTTP_HEADERS = ImmutableSet.of(CONTENT_LENGTH, "Content-MD5", CONTENT_TYPE,
|
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.
|
* Returns the total size of the payload, or the chunk that's available.
|
||||||
* <p/>
|
* <p/>
|
||||||
|
@ -89,11 +95,13 @@ public interface ContentMetadata {
|
||||||
/**
|
/**
|
||||||
* Gives the date/time after which the response is considered stale.
|
* 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"/>
|
* @see <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.21"/>
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
String getExpires();
|
Date getExpires();
|
||||||
|
|
||||||
ContentMetadataBuilder toBuilder();
|
ContentMetadataBuilder toBuilder();
|
||||||
|
|
||||||
}
|
}
|
|
@ -18,21 +18,14 @@
|
||||||
*/
|
*/
|
||||||
package org.jclouds.io;
|
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.io.Serializable;
|
||||||
import java.util.Arrays;
|
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.io.payloads.BaseImmutableContentMetadata;
|
||||||
import org.jclouds.javax.annotation.Nullable;
|
import org.jclouds.javax.annotation.Nullable;
|
||||||
|
|
||||||
import com.google.common.base.Objects;
|
import com.google.common.base.Objects;
|
||||||
import com.google.common.base.Predicate;
|
|
||||||
import com.google.common.collect.Multimap;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Adrian Cole
|
* @author Adrian Cole
|
||||||
|
@ -51,34 +44,7 @@ public class ContentMetadataBuilder implements Serializable {
|
||||||
protected String contentDisposition;
|
protected String contentDisposition;
|
||||||
protected String contentLanguage;
|
protected String contentLanguage;
|
||||||
protected String contentEncoding;
|
protected String contentEncoding;
|
||||||
protected String expires;
|
protected Date expires;
|
||||||
|
|
||||||
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());
|
|
||||||
} else if ("Expires".equalsIgnoreCase(header.getKey())) {
|
|
||||||
expires(header.getValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ContentMetadataBuilder contentLength(@Nullable Long contentLength) {
|
public ContentMetadataBuilder contentLength(@Nullable Long contentLength) {
|
||||||
this.contentLength = contentLength;
|
this.contentLength = contentLength;
|
||||||
|
@ -116,7 +82,7 @@ public class ContentMetadataBuilder implements Serializable {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ContentMetadataBuilder expires(@Nullable String expires) {
|
public ContentMetadataBuilder expires(@Nullable Date expires) {
|
||||||
this.expires = expires;
|
this.expires = expires;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,118 @@
|
||||||
|
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.DateCodecs;
|
||||||
|
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;
|
||||||
|
|
||||||
|
@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 = 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;
|
package org.jclouds.io;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
import org.jclouds.javax.annotation.Nullable;
|
import org.jclouds.javax.annotation.Nullable;
|
||||||
|
|
||||||
import com.google.common.collect.Multimap;
|
import com.google.common.collect.Multimap;
|
||||||
|
@ -26,12 +28,6 @@ import com.google.common.collect.Multimap;
|
||||||
* @author Adrian Cole
|
* @author Adrian Cole
|
||||||
*/
|
*/
|
||||||
public interface MutableContentMetadata extends ContentMetadata {
|
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);
|
void setContentLength(@Nullable Long contentLength);
|
||||||
|
|
||||||
|
@ -66,5 +62,5 @@ public interface MutableContentMetadata extends ContentMetadata {
|
||||||
*/
|
*/
|
||||||
void setContentEncoding(@Nullable String contentEncoding);
|
void setContentEncoding(@Nullable String contentEncoding);
|
||||||
|
|
||||||
void setExpires(@Nullable String expires);
|
void setExpires(@Nullable Date expires);
|
||||||
}
|
}
|
|
@ -19,7 +19,10 @@
|
||||||
package org.jclouds.io.payloads;
|
package org.jclouds.io.payloads;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
import java.text.ParseException;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
import org.jclouds.io.ContentMetadata;
|
import org.jclouds.io.ContentMetadata;
|
||||||
import org.jclouds.io.ContentMetadataBuilder;
|
import org.jclouds.io.ContentMetadataBuilder;
|
||||||
|
@ -39,10 +42,10 @@ public class BaseImmutableContentMetadata implements ContentMetadata, Serializab
|
||||||
protected String contentDisposition;
|
protected String contentDisposition;
|
||||||
protected String contentLanguage;
|
protected String contentLanguage;
|
||||||
protected String contentEncoding;
|
protected String contentEncoding;
|
||||||
protected String expires;
|
protected Date expires;
|
||||||
|
|
||||||
public BaseImmutableContentMetadata(String contentType, Long contentLength, byte[] contentMD5,
|
public BaseImmutableContentMetadata(String contentType, Long contentLength, byte[] contentMD5,
|
||||||
String contentDisposition, String contentLanguage, String contentEncoding, String expires) {
|
String contentDisposition, String contentLanguage, String contentEncoding, Date expires) {
|
||||||
this.contentType = contentType;
|
this.contentType = contentType;
|
||||||
this.contentLength = contentLength;
|
this.contentLength = contentLength;
|
||||||
this.contentMD5 = contentMD5;
|
this.contentMD5 = contentMD5;
|
||||||
|
@ -110,8 +113,8 @@ public class BaseImmutableContentMetadata implements ContentMetadata, Serializab
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public String getExpires() {
|
public Date getExpires() {
|
||||||
return this.expires;
|
return expires;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
package org.jclouds.io.payloads;
|
package org.jclouds.io.payloads;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
import org.jclouds.io.ContentMetadata;
|
import org.jclouds.io.ContentMetadata;
|
||||||
import org.jclouds.io.ContentMetadataBuilder;
|
import org.jclouds.io.ContentMetadataBuilder;
|
||||||
|
@ -34,11 +35,6 @@ public class BaseMutableContentMetadata extends ContentMetadataBuilder implement
|
||||||
/** The serialVersionUID */
|
/** The serialVersionUID */
|
||||||
private static final long serialVersionUID = 8364286391963469370L;
|
private static final long serialVersionUID = 8364286391963469370L;
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setPropertiesFromHttpHeaders(Multimap<String, String> headers) {
|
|
||||||
fromHttpHeaders(headers);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
|
@ -145,7 +141,7 @@ public class BaseMutableContentMetadata extends ContentMetadataBuilder implement
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void setExpires(@Nullable String expires) {
|
public void setExpires(@Nullable Date expires) {
|
||||||
expires(expires);
|
expires(expires);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -153,8 +149,8 @@ public class BaseMutableContentMetadata extends ContentMetadataBuilder implement
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public String getExpires() {
|
public Date getExpires() {
|
||||||
return this.expires;
|
return expires;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -89,6 +89,7 @@ import org.jclouds.http.options.HttpRequestOptions;
|
||||||
import org.jclouds.http.utils.ModifyRequest;
|
import org.jclouds.http.utils.ModifyRequest;
|
||||||
import org.jclouds.internal.ClassMethodArgs;
|
import org.jclouds.internal.ClassMethodArgs;
|
||||||
import org.jclouds.io.ContentMetadata;
|
import org.jclouds.io.ContentMetadata;
|
||||||
|
import org.jclouds.io.ContentMetadataCodec;
|
||||||
import org.jclouds.io.Payload;
|
import org.jclouds.io.Payload;
|
||||||
import org.jclouds.io.PayloadEnclosing;
|
import org.jclouds.io.PayloadEnclosing;
|
||||||
import org.jclouds.io.Payloads;
|
import org.jclouds.io.Payloads;
|
||||||
|
@ -255,6 +256,7 @@ public class RestAnnotationProcessor<T> {
|
||||||
|
|
||||||
private final ParseSax.Factory parserFactory;
|
private final ParseSax.Factory parserFactory;
|
||||||
private final HttpUtils utils;
|
private final HttpUtils utils;
|
||||||
|
private final ContentMetadataCodec contentMetadataCodec;
|
||||||
private final Provider<UriBuilder> uriBuilderProvider;
|
private final Provider<UriBuilder> uriBuilderProvider;
|
||||||
private final LoadingCache<Class<?>, Boolean> seedAnnotationCache;
|
private final LoadingCache<Class<?>, Boolean> seedAnnotationCache;
|
||||||
private final String apiVersion;
|
private final String apiVersion;
|
||||||
|
@ -321,11 +323,12 @@ public class RestAnnotationProcessor<T> {
|
||||||
@Inject
|
@Inject
|
||||||
public RestAnnotationProcessor(Injector injector, LoadingCache<Class<?>, Boolean> seedAnnotationCache,
|
public RestAnnotationProcessor(Injector injector, LoadingCache<Class<?>, Boolean> seedAnnotationCache,
|
||||||
@ApiVersion String apiVersion, @BuildVersion String buildVersion, ParseSax.Factory parserFactory,
|
@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.declaring = (Class<T>) typeLiteral.getRawType();
|
||||||
this.injector = injector;
|
this.injector = injector;
|
||||||
this.parserFactory = parserFactory;
|
this.parserFactory = parserFactory;
|
||||||
this.utils = utils;
|
this.utils = utils;
|
||||||
|
this.contentMetadataCodec = contentMetadataCodec;
|
||||||
this.uriBuilderProvider = injector.getProvider(UriBuilder.class);
|
this.uriBuilderProvider = injector.getProvider(UriBuilder.class);
|
||||||
this.seedAnnotationCache = seedAnnotationCache;
|
this.seedAnnotationCache = seedAnnotationCache;
|
||||||
seedAnnotationCache.get(declaring);
|
seedAnnotationCache.get(declaring);
|
||||||
|
@ -545,8 +548,9 @@ public class RestAnnotationProcessor<T> {
|
||||||
request = decorateRequest(request);
|
request = decorateRequest(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.getPayload() != null)
|
if (request.getPayload() != null) {
|
||||||
request.getPayload().getContentMetadata().setPropertiesFromHttpHeaders(headers);
|
contentMetadataCodec.fromHeaders(request.getPayload().getContentMetadata(), headers);
|
||||||
|
}
|
||||||
utils.checkRequestHasRequiredProperties(request);
|
utils.checkRequestHasRequiredProperties(request);
|
||||||
return request;
|
return request;
|
||||||
} catch (ExecutionException e) {
|
} catch (ExecutionException e) {
|
||||||
|
|
|
@ -44,7 +44,9 @@ import org.jclouds.http.TransformingHttpCommandImpl;
|
||||||
import org.jclouds.http.functions.ReturnStringIf2xx;
|
import org.jclouds.http.functions.ReturnStringIf2xx;
|
||||||
import org.jclouds.http.internal.HttpWire;
|
import org.jclouds.http.internal.HttpWire;
|
||||||
import org.jclouds.http.internal.JavaUrlHttpCommandExecutorService;
|
import org.jclouds.http.internal.JavaUrlHttpCommandExecutorService;
|
||||||
|
import org.jclouds.io.ContentMetadataCodec;
|
||||||
import org.jclouds.io.Payloads;
|
import org.jclouds.io.Payloads;
|
||||||
|
import org.jclouds.io.ContentMetadataCodec.DefaultContentMetadataCodec;
|
||||||
import org.jclouds.rest.internal.RestAnnotationProcessor;
|
import org.jclouds.rest.internal.RestAnnotationProcessor;
|
||||||
import org.testng.annotations.BeforeTest;
|
import org.testng.annotations.BeforeTest;
|
||||||
import org.testng.annotations.Test;
|
import org.testng.annotations.Test;
|
||||||
|
@ -105,15 +107,16 @@ public class BackoffLimitedRetryHandlerTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
private HttpUtils utils;
|
|
||||||
|
|
||||||
@BeforeTest
|
@BeforeTest
|
||||||
void setupExecutorService() throws Exception {
|
void setupExecutorService() throws Exception {
|
||||||
ExecutorService execService = Executors.newCachedThreadPool();
|
ExecutorService execService = Executors.newCachedThreadPool();
|
||||||
BackoffLimitedRetryHandler backoff = new BackoffLimitedRetryHandler();
|
BackoffLimitedRetryHandler backoff = new BackoffLimitedRetryHandler();
|
||||||
utils = new HttpUtils(0, 500, 1, 1);
|
HttpUtils utils = new HttpUtils(0, 500, 1, 1);
|
||||||
|
ContentMetadataCodec contentMetadataCodec = new DefaultContentMetadataCodec();
|
||||||
RedirectionRetryHandler retry = new RedirectionRetryHandler(uriBuilderProvider, backoff);
|
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 DelegatingRetryHandler(backoff, retry), new BackoffLimitedRetryHandler(),
|
||||||
new DelegatingErrorHandler(), new HttpWire(), new HostnameVerifier() {
|
new DelegatingErrorHandler(), new HttpWire(), new HostnameVerifier() {
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,7 @@ import org.jclouds.http.HttpUtils;
|
||||||
import org.jclouds.http.IOExceptionRetryHandler;
|
import org.jclouds.http.IOExceptionRetryHandler;
|
||||||
import org.jclouds.http.handlers.DelegatingErrorHandler;
|
import org.jclouds.http.handlers.DelegatingErrorHandler;
|
||||||
import org.jclouds.http.handlers.DelegatingRetryHandler;
|
import org.jclouds.http.handlers.DelegatingRetryHandler;
|
||||||
|
import org.jclouds.io.ContentMetadataCodec;
|
||||||
import org.jclouds.rest.internal.GeneratedHttpRequest;
|
import org.jclouds.rest.internal.GeneratedHttpRequest;
|
||||||
|
|
||||||
import com.google.common.base.Supplier;
|
import com.google.common.base.Supplier;
|
||||||
|
@ -86,13 +87,13 @@ public class TrackingJavaUrlHttpCommandExecutorService extends JavaUrlHttpComman
|
||||||
}
|
}
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public TrackingJavaUrlHttpCommandExecutorService(HttpUtils utils,
|
public TrackingJavaUrlHttpCommandExecutorService(HttpUtils utils, ContentMetadataCodec contentMetadataCodec,
|
||||||
@Named(Constants.PROPERTY_IO_WORKER_THREADS) ExecutorService ioWorkerExecutor,
|
@Named(Constants.PROPERTY_IO_WORKER_THREADS) ExecutorService ioWorkerExecutor,
|
||||||
DelegatingRetryHandler retryHandler, IOExceptionRetryHandler ioRetryHandler,
|
DelegatingRetryHandler retryHandler, IOExceptionRetryHandler ioRetryHandler,
|
||||||
DelegatingErrorHandler errorHandler, HttpWire wire, @Named("untrusted") HostnameVerifier verifier,
|
DelegatingErrorHandler errorHandler, HttpWire wire, @Named("untrusted") HostnameVerifier verifier,
|
||||||
@Named("untrusted") Supplier<SSLContext> untrustedSSLContextProvider, List<HttpCommand> commandsInvoked)
|
@Named("untrusted") Supplier<SSLContext> untrustedSSLContextProvider, List<HttpCommand> commandsInvoked)
|
||||||
throws SecurityException, NoSuchFieldException {
|
throws SecurityException, NoSuchFieldException {
|
||||||
super(utils, ioWorkerExecutor, retryHandler, ioRetryHandler, errorHandler, wire, verifier,
|
super(utils, contentMetadataCodec, ioWorkerExecutor, retryHandler, ioRetryHandler, errorHandler, wire, verifier,
|
||||||
untrustedSSLContextProvider);
|
untrustedSSLContextProvider);
|
||||||
this.commandsInvoked = commandsInvoked;
|
this.commandsInvoked = commandsInvoked;
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,9 +25,9 @@ import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
import java.util.NoSuchElementException;
|
import java.util.NoSuchElementException;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.Map.Entry;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
@ -59,9 +59,11 @@ import org.jclouds.http.handlers.DelegatingErrorHandler;
|
||||||
import org.jclouds.http.handlers.DelegatingRetryHandler;
|
import org.jclouds.http.handlers.DelegatingRetryHandler;
|
||||||
import org.jclouds.http.internal.BaseHttpCommandExecutorService;
|
import org.jclouds.http.internal.BaseHttpCommandExecutorService;
|
||||||
import org.jclouds.http.internal.HttpWire;
|
import org.jclouds.http.internal.HttpWire;
|
||||||
|
import org.jclouds.io.ContentMetadataCodec;
|
||||||
import org.jclouds.io.CopyInputStreamInputSupplierMap;
|
import org.jclouds.io.CopyInputStreamInputSupplierMap;
|
||||||
import org.jclouds.io.Payload;
|
import org.jclouds.io.Payload;
|
||||||
import org.jclouds.io.Payloads;
|
import org.jclouds.io.Payloads;
|
||||||
|
import org.jclouds.io.ContentMetadataCodec.DefaultContentMetadataCodec;
|
||||||
import org.jclouds.logging.config.NullLoggingModule;
|
import org.jclouds.logging.config.NullLoggingModule;
|
||||||
import org.jclouds.providers.ProviderMetadata;
|
import org.jclouds.providers.ProviderMetadata;
|
||||||
import org.jclouds.rest.RestApiMetadata;
|
import org.jclouds.rest.RestApiMetadata;
|
||||||
|
@ -118,6 +120,8 @@ public abstract class BaseRestClientExpectTest<S> {
|
||||||
|
|
||||||
protected String provider = "mock";
|
protected String provider = "mock";
|
||||||
|
|
||||||
|
protected ContentMetadataCodec contentMetadataCodec = new DefaultContentMetadataCodec();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Override this to supply alternative bindings for use in the test. This is commonly used to
|
* 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.
|
* override suppliers of dates so that the test results are predicatable.
|
||||||
|
@ -188,10 +192,11 @@ public abstract class BaseRestClientExpectTest<S> {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public ExpectHttpCommandExecutorService(Function<HttpRequest, HttpResponse> fn, HttpUtils utils,
|
public ExpectHttpCommandExecutorService(Function<HttpRequest, HttpResponse> fn, HttpUtils utils,
|
||||||
|
ContentMetadataCodec contentMetadataCodec,
|
||||||
@Named(Constants.PROPERTY_IO_WORKER_THREADS) ExecutorService ioExecutor,
|
@Named(Constants.PROPERTY_IO_WORKER_THREADS) ExecutorService ioExecutor,
|
||||||
IOExceptionRetryHandler ioRetryHandler, DelegatingRetryHandler retryHandler,
|
IOExceptionRetryHandler ioRetryHandler, DelegatingRetryHandler retryHandler,
|
||||||
DelegatingErrorHandler errorHandler, HttpWire wire) {
|
DelegatingErrorHandler errorHandler, HttpWire wire) {
|
||||||
super(utils, ioExecutor, retryHandler, ioRetryHandler, errorHandler, wire);
|
super(utils, contentMetadataCodec, ioExecutor, retryHandler, ioRetryHandler, errorHandler, wire);
|
||||||
this.fn = checkNotNull(fn, "fn");
|
this.fn = checkNotNull(fn, "fn");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -471,7 +476,7 @@ public abstract class BaseRestClientExpectTest<S> {
|
||||||
builder.append(header.getKey()).append(": ").append(header.getValue()).append('\n');
|
builder.append(header.getKey()).append(": ").append(header.getValue()).append('\n');
|
||||||
}
|
}
|
||||||
if (request.getPayload() != null) {
|
if (request.getPayload() != null) {
|
||||||
for (Entry<String, String> header : HttpUtils.getContentHeadersFromMetadata(
|
for (Entry<String, String> header : contentMetadataCodec.toHeaders(
|
||||||
request.getPayload().getContentMetadata()).entries()) {
|
request.getPayload().getContentMetadata()).entries()) {
|
||||||
builder.append(header.getKey()).append(": ").append(header.getValue()).append('\n');
|
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.io.IOException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
|
|
||||||
import org.jclouds.Constants;
|
import org.jclouds.Constants;
|
||||||
|
@ -42,7 +43,6 @@ import org.jclouds.http.functions.ParseSax;
|
||||||
import org.jclouds.io.MutableContentMetadata;
|
import org.jclouds.io.MutableContentMetadata;
|
||||||
import org.jclouds.javax.annotation.Nullable;
|
import org.jclouds.javax.annotation.Nullable;
|
||||||
import org.jclouds.rest.functions.MapHttp4xxCodesToExceptions;
|
import org.jclouds.rest.functions.MapHttp4xxCodesToExceptions;
|
||||||
import org.jclouds.rest.internal.RestAnnotationProcessor;
|
|
||||||
import org.jclouds.util.Strings2;
|
import org.jclouds.util.Strings2;
|
||||||
import org.testng.annotations.Test;
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
|
@ -89,7 +89,7 @@ public abstract class BaseRestClientTest {
|
||||||
assertPayloadEquals(request, toMatch, contentType, contentMD5, null);
|
assertPayloadEquals(request, toMatch, contentType, contentMD5, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void assertPayloadEquals(HttpRequest request, String toMatch, String contentType, boolean contentMD5, String expires) {
|
protected void assertPayloadEquals(HttpRequest request, String toMatch, String contentType, boolean contentMD5, Date expires) {
|
||||||
assertPayloadEquals(request, toMatch, contentType, null, null, null, contentMD5, expires);
|
assertPayloadEquals(request, toMatch, contentType, null, null, null, contentMD5, expires);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,7 +101,7 @@ public abstract class BaseRestClientTest {
|
||||||
|
|
||||||
protected void assertPayloadEquals(HttpRequest request, String toMatch, String contentType,
|
protected void assertPayloadEquals(HttpRequest request, String toMatch, String contentType,
|
||||||
String contentDispositon, String contentEncoding, String contentLanguage, boolean contentMD5,
|
String contentDispositon, String contentEncoding, String contentLanguage, boolean contentMD5,
|
||||||
String expires) {
|
Date expires) {
|
||||||
if (request.getPayload() == null) {
|
if (request.getPayload() == null) {
|
||||||
assertNull(toMatch);
|
assertNull(toMatch);
|
||||||
} else {
|
} else {
|
||||||
|
@ -123,7 +123,7 @@ public abstract class BaseRestClientTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void assertContentHeadersEqual(HttpRequest request, String contentType, String contentDispositon,
|
protected void assertContentHeadersEqual(HttpRequest request, String contentType, String contentDispositon,
|
||||||
String contentEncoding, String contentLanguage, Long length, byte[] contentMD5, String expires) {
|
String contentEncoding, String contentLanguage, Long length, byte[] contentMD5, Date expires) {
|
||||||
MutableContentMetadata md = request.getPayload().getContentMetadata();
|
MutableContentMetadata md = request.getPayload().getContentMetadata();
|
||||||
if (request.getFirstHeaderOrNull(TRANSFER_ENCODING) == null) {
|
if (request.getFirstHeaderOrNull(TRANSFER_ENCODING) == null) {
|
||||||
assertEquals(md.getContentLength(), length);
|
assertEquals(md.getContentLength(), length);
|
||||||
|
|
|
@ -39,6 +39,7 @@ import org.jclouds.http.handlers.DelegatingErrorHandler;
|
||||||
import org.jclouds.http.handlers.DelegatingRetryHandler;
|
import org.jclouds.http.handlers.DelegatingRetryHandler;
|
||||||
import org.jclouds.http.internal.BaseHttpCommandExecutorService;
|
import org.jclouds.http.internal.BaseHttpCommandExecutorService;
|
||||||
import org.jclouds.http.internal.HttpWire;
|
import org.jclouds.http.internal.HttpWire;
|
||||||
|
import org.jclouds.io.ContentMetadataCodec;
|
||||||
import org.jclouds.io.Payload;
|
import org.jclouds.io.Payload;
|
||||||
import org.jclouds.io.Payloads;
|
import org.jclouds.io.Payloads;
|
||||||
import org.jclouds.rest.internal.RestAnnotationProcessor;
|
import org.jclouds.rest.internal.RestAnnotationProcessor;
|
||||||
|
@ -55,19 +56,21 @@ import com.google.inject.Inject;
|
||||||
*/
|
*/
|
||||||
public class ApacheHCHttpCommandExecutorService extends BaseHttpCommandExecutorService<HttpUriRequest> {
|
public class ApacheHCHttpCommandExecutorService extends BaseHttpCommandExecutorService<HttpUriRequest> {
|
||||||
private final HttpClient client;
|
private final HttpClient client;
|
||||||
|
private final ApacheHCUtils apacheHCUtils;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
ApacheHCHttpCommandExecutorService(HttpUtils utils,
|
ApacheHCHttpCommandExecutorService(HttpUtils utils, ContentMetadataCodec contentMetadataCodec,
|
||||||
@Named(Constants.PROPERTY_IO_WORKER_THREADS) ExecutorService ioWorkerExecutor,
|
@Named(Constants.PROPERTY_IO_WORKER_THREADS) ExecutorService ioWorkerExecutor,
|
||||||
DelegatingRetryHandler retryHandler, IOExceptionRetryHandler ioRetryHandler,
|
DelegatingRetryHandler retryHandler, IOExceptionRetryHandler ioRetryHandler,
|
||||||
DelegatingErrorHandler errorHandler, HttpWire wire, HttpClient client) {
|
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.client = client;
|
||||||
|
this.apacheHCUtils = new ApacheHCUtils(contentMetadataCodec);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected HttpUriRequest convert(HttpRequest request) throws IOException {
|
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)
|
if (request.getPayload() != null && request.getPayload().getContentMetadata().getContentMD5() != null)
|
||||||
returnVal.addHeader("Content-MD5", CryptoStreams.md5Base64(request.getPayload()));
|
returnVal.addHeader("Content-MD5", CryptoStreams.md5Base64(request.getPayload()));
|
||||||
return returnVal;
|
return returnVal;
|
||||||
|
@ -93,8 +96,9 @@ public class ApacheHCHttpCommandExecutorService extends BaseHttpCommandExecutorS
|
||||||
for (Header header : apacheResponse.getAllHeaders()) {
|
for (Header header : apacheResponse.getAllHeaders()) {
|
||||||
headers.put(header.getName(), header.getValue());
|
headers.put(header.getName(), header.getValue());
|
||||||
}
|
}
|
||||||
if (payload != null)
|
if (payload != null) {
|
||||||
payload.getContentMetadata().setPropertiesFromHttpHeaders(headers);
|
contentMetadataCodec.fromHeaders(payload.getContentMetadata(), headers);
|
||||||
|
}
|
||||||
return new HttpResponse(apacheResponse.getStatusLine().getStatusCode(), apacheResponse.getStatusLine()
|
return new HttpResponse(apacheResponse.getStatusLine().getStatusCode(), apacheResponse.getStatusLine()
|
||||||
.getReasonPhrase(), payload, RestAnnotationProcessor.filterOutContentHeaders(headers));
|
.getReasonPhrase(), payload, RestAnnotationProcessor.filterOutContentHeaders(headers));
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,8 @@ import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
import javax.ws.rs.HttpMethod;
|
import javax.ws.rs.HttpMethod;
|
||||||
|
@ -45,6 +47,8 @@ import org.apache.http.entity.StringEntity;
|
||||||
import org.apache.http.params.CoreProtocolPNames;
|
import org.apache.http.params.CoreProtocolPNames;
|
||||||
import org.jclouds.JcloudsVersion;
|
import org.jclouds.JcloudsVersion;
|
||||||
import org.jclouds.http.HttpRequest;
|
import org.jclouds.http.HttpRequest;
|
||||||
|
import org.jclouds.io.ContentMetadataCodec;
|
||||||
|
import org.jclouds.io.MutableContentMetadata;
|
||||||
import org.jclouds.io.Payload;
|
import org.jclouds.io.Payload;
|
||||||
import org.jclouds.io.payloads.BasePayload;
|
import org.jclouds.io.payloads.BasePayload;
|
||||||
import org.jclouds.io.payloads.ByteArrayPayload;
|
import org.jclouds.io.payloads.ByteArrayPayload;
|
||||||
|
@ -53,6 +57,7 @@ import org.jclouds.io.payloads.FilePayload;
|
||||||
import org.jclouds.io.payloads.StringPayload;
|
import org.jclouds.io.payloads.StringPayload;
|
||||||
|
|
||||||
import com.google.common.base.Throwables;
|
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 {
|
public class ApacheHCUtils {
|
||||||
//TODO: look up httpclient version
|
//TODO: look up httpclient version
|
||||||
public static final String USER_AGENT = String.format("jclouds/%s httpclient/%s", JcloudsVersion.get(), "4.1.1");
|
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;
|
HttpUriRequest apacheRequest;
|
||||||
if (request.getMethod().equals(HttpMethod.HEAD)) {
|
if (request.getMethod().equals(HttpMethod.HEAD)) {
|
||||||
apacheRequest = new HttpHead(request.getEndpoint());
|
apacheRequest = new HttpHead(request.getEndpoint());
|
||||||
|
@ -120,7 +131,7 @@ public class ApacheHCUtils {
|
||||||
return apacheRequest;
|
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;
|
payload = payload instanceof DelegatingPayload ? DelegatingPayload.class.cast(payload).getDelegate() : payload;
|
||||||
if (payload instanceof StringPayload) {
|
if (payload instanceof StringPayload) {
|
||||||
StringEntity nStringEntity = null;
|
StringEntity nStringEntity = null;
|
||||||
|
@ -142,18 +153,20 @@ public class ApacheHCUtils {
|
||||||
InputStream inputStream = payload.getInput();
|
InputStream inputStream = payload.getInput();
|
||||||
if (payload.getContentMetadata().getContentLength() == null)
|
if (payload.getContentMetadata().getContentLength() == null)
|
||||||
throw new IllegalArgumentException("you must specify size when content is an InputStream");
|
throw new IllegalArgumentException("you must specify size when content is an InputStream");
|
||||||
InputStreamEntity Entity = new InputStreamEntity(inputStream, payload.getContentMetadata().getContentLength());
|
InputStreamEntity entity = new InputStreamEntity(inputStream, payload.getContentMetadata().getContentLength());
|
||||||
Entity.setContentType(payload.getContentMetadata().getContentType());
|
entity.setContentType(payload.getContentMetadata().getContentType());
|
||||||
apacheRequest.setEntity(Entity);
|
apacheRequest.setEntity(entity);
|
||||||
}
|
}
|
||||||
if (payload.getContentMetadata().getContentDisposition() != null)
|
|
||||||
apacheRequest.addHeader("Content-Disposition", payload.getContentMetadata().getContentDisposition());
|
// TODO Reproducing old behaviour exactly; ignoring Content-Type, Content-Length and Content-MD5
|
||||||
if (payload.getContentMetadata().getContentEncoding() != null)
|
Set<String> desiredHeaders = ImmutableSet.of("Content-Disposition", "Content-Encoding", "Content-Language", "Expires");
|
||||||
apacheRequest.addHeader("Content-Encoding", payload.getContentMetadata().getContentEncoding());
|
MutableContentMetadata md = payload.getContentMetadata();
|
||||||
if (payload.getContentMetadata().getContentLanguage() != null)
|
for (Map.Entry<String,String> entry : contentMetadataCodec.toHeaders(md).entries()) {
|
||||||
apacheRequest.addHeader("Content-Language", payload.getContentMetadata().getContentLanguage());
|
if (desiredHeaders.contains(entry.getKey())) {
|
||||||
if (payload.getContentMetadata().getExpires() != null)
|
apacheRequest.addHeader(entry.getKey(), entry.getValue());
|
||||||
apacheRequest.addHeader("Expires", payload.getContentMetadata().getExpires());
|
}
|
||||||
|
}
|
||||||
|
|
||||||
assert (apacheRequest.getEntity() != null);
|
assert (apacheRequest.getEntity() != null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,7 @@ import javax.ws.rs.core.HttpHeaders;
|
||||||
|
|
||||||
import org.jclouds.http.HttpRequest;
|
import org.jclouds.http.HttpRequest;
|
||||||
import org.jclouds.http.HttpUtils;
|
import org.jclouds.http.HttpUtils;
|
||||||
|
import org.jclouds.io.ContentMetadataCodec;
|
||||||
import org.jclouds.io.Payload;
|
import org.jclouds.io.Payload;
|
||||||
|
|
||||||
import com.google.appengine.api.urlfetch.FetchOptions;
|
import com.google.appengine.api.urlfetch.FetchOptions;
|
||||||
|
@ -52,14 +53,17 @@ import com.google.common.io.Closeables;
|
||||||
@Singleton
|
@Singleton
|
||||||
public class ConvertToGaeRequest implements Function<HttpRequest, HTTPRequest> {
|
public class ConvertToGaeRequest implements Function<HttpRequest, HTTPRequest> {
|
||||||
public static final String USER_AGENT = "jclouds/1.0 urlfetch/1.4.3";
|
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
|
// http://code.google.com/appengine/docs/java/urlfetch/overview.html
|
||||||
public final Set<String> prohibitedHeaders = ImmutableSet.of("Accept-Encoding", "Content-Length", "Host", "Var",
|
public final Set<String> prohibitedHeaders = ImmutableSet.of("Accept-Encoding", "Content-Length", "Host", "Var",
|
||||||
"X-Forwarded-For");
|
"X-Forwarded-For");
|
||||||
|
|
||||||
|
protected final HttpUtils utils;
|
||||||
|
protected final ContentMetadataCodec contentMetadataCodec;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
ConvertToGaeRequest(HttpUtils utils) {
|
ConvertToGaeRequest(HttpUtils utils, ContentMetadataCodec contentMetadataCodec) {
|
||||||
this.utils = utils;
|
this.utils = utils;
|
||||||
|
this.contentMetadataCodec = contentMetadataCodec;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -115,7 +119,7 @@ public class ConvertToGaeRequest implements Function<HttpRequest, HTTPRequest> {
|
||||||
Closeables.closeQuietly(input);
|
Closeables.closeQuietly(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Entry<String, String> header : HttpUtils.getContentHeadersFromMetadata(
|
for (Entry<String, String> header : contentMetadataCodec.toHeaders(
|
||||||
request.getPayload().getContentMetadata()).entries()) {
|
request.getPayload().getContentMetadata()).entries()) {
|
||||||
if (!prohibitedHeaders.contains(header.getKey()))
|
if (!prohibitedHeaders.contains(header.getKey()))
|
||||||
gaeRequest.setHeader(new HTTPHeader(header.getKey(), header.getValue()));
|
gaeRequest.setHeader(new HTTPHeader(header.getKey(), header.getValue()));
|
||||||
|
|
|
@ -21,6 +21,7 @@ package org.jclouds.gae;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
import org.jclouds.http.HttpResponse;
|
import org.jclouds.http.HttpResponse;
|
||||||
|
import org.jclouds.io.ContentMetadataCodec;
|
||||||
import org.jclouds.io.Payload;
|
import org.jclouds.io.Payload;
|
||||||
import org.jclouds.io.Payloads;
|
import org.jclouds.io.Payloads;
|
||||||
import org.jclouds.rest.internal.RestAnnotationProcessor;
|
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.base.Function;
|
||||||
import com.google.common.collect.LinkedHashMultimap;
|
import com.google.common.collect.LinkedHashMultimap;
|
||||||
import com.google.common.collect.Multimap;
|
import com.google.common.collect.Multimap;
|
||||||
|
import com.google.inject.Inject;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
@ -38,6 +40,13 @@ import com.google.common.collect.Multimap;
|
||||||
@Singleton
|
@Singleton
|
||||||
public class ConvertToJcloudsResponse implements Function<HTTPResponse, HttpResponse> {
|
public class ConvertToJcloudsResponse implements Function<HTTPResponse, HttpResponse> {
|
||||||
|
|
||||||
|
private final ContentMetadataCodec contentMetadataCodec;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public ConvertToJcloudsResponse(ContentMetadataCodec contentMetadataCodec) {
|
||||||
|
this.contentMetadataCodec = contentMetadataCodec;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public HttpResponse apply(HTTPResponse gaeResponse) {
|
public HttpResponse apply(HTTPResponse gaeResponse) {
|
||||||
Payload payload = gaeResponse.getContent() != null ? Payloads.newByteArrayPayload(gaeResponse.getContent())
|
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());
|
headers.put(header.getName(), header.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (payload != null)
|
if (payload != null) {
|
||||||
payload.getContentMetadata().setPropertiesFromHttpHeaders(headers);
|
contentMetadataCodec.fromHeaders(payload.getContentMetadata(), headers);
|
||||||
|
}
|
||||||
return new HttpResponse(gaeResponse.getResponseCode(), message, payload,
|
return new HttpResponse(gaeResponse.getResponseCode(), message, payload,
|
||||||
RestAnnotationProcessor.filterOutContentHeaders(headers));
|
RestAnnotationProcessor.filterOutContentHeaders(headers));
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,6 +37,7 @@ import org.jclouds.http.handlers.DelegatingErrorHandler;
|
||||||
import org.jclouds.http.handlers.DelegatingRetryHandler;
|
import org.jclouds.http.handlers.DelegatingRetryHandler;
|
||||||
import org.jclouds.http.internal.BaseHttpCommandExecutorService;
|
import org.jclouds.http.internal.BaseHttpCommandExecutorService;
|
||||||
import org.jclouds.http.internal.HttpWire;
|
import org.jclouds.http.internal.HttpWire;
|
||||||
|
import org.jclouds.io.ContentMetadataCodec;
|
||||||
|
|
||||||
import com.google.appengine.api.urlfetch.HTTPRequest;
|
import com.google.appengine.api.urlfetch.HTTPRequest;
|
||||||
import com.google.appengine.api.urlfetch.HTTPResponse;
|
import com.google.appengine.api.urlfetch.HTTPResponse;
|
||||||
|
@ -60,11 +61,12 @@ public class GaeHttpCommandExecutorService extends BaseHttpCommandExecutorServic
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public GaeHttpCommandExecutorService(URLFetchService urlFetchService, HttpUtils utils,
|
public GaeHttpCommandExecutorService(URLFetchService urlFetchService, HttpUtils utils,
|
||||||
|
ContentMetadataCodec contentMetadataCodec,
|
||||||
@Named(Constants.PROPERTY_IO_WORKER_THREADS) ExecutorService ioExecutor,
|
@Named(Constants.PROPERTY_IO_WORKER_THREADS) ExecutorService ioExecutor,
|
||||||
IOExceptionRetryHandler ioRetryHandler, DelegatingRetryHandler retryHandler,
|
IOExceptionRetryHandler ioRetryHandler, DelegatingRetryHandler retryHandler,
|
||||||
DelegatingErrorHandler errorHandler, HttpWire wire, ConvertToGaeRequest convertToGaeRequest,
|
DelegatingErrorHandler errorHandler, HttpWire wire, ConvertToGaeRequest convertToGaeRequest,
|
||||||
ConvertToJcloudsResponse convertToJcloudsResponse) {
|
ConvertToJcloudsResponse convertToJcloudsResponse) {
|
||||||
super(utils, ioExecutor, retryHandler, ioRetryHandler, errorHandler, wire);
|
super(utils, contentMetadataCodec, ioExecutor, retryHandler, ioRetryHandler, errorHandler, wire);
|
||||||
this.urlFetchService = urlFetchService;
|
this.urlFetchService = urlFetchService;
|
||||||
this.convertToGaeRequest = convertToGaeRequest;
|
this.convertToGaeRequest = convertToGaeRequest;
|
||||||
this.convertToJcloudsResponse = convertToJcloudsResponse;
|
this.convertToJcloudsResponse = convertToJcloudsResponse;
|
||||||
|
|
|
@ -35,6 +35,7 @@ import org.jclouds.crypto.Crypto;
|
||||||
import org.jclouds.encryption.internal.JCECrypto;
|
import org.jclouds.encryption.internal.JCECrypto;
|
||||||
import org.jclouds.http.HttpRequest;
|
import org.jclouds.http.HttpRequest;
|
||||||
import org.jclouds.http.HttpUtils;
|
import org.jclouds.http.HttpUtils;
|
||||||
|
import org.jclouds.io.ContentMetadataCodec.DefaultContentMetadataCodec;
|
||||||
import org.jclouds.io.Payloads;
|
import org.jclouds.io.Payloads;
|
||||||
import org.jclouds.util.Strings2;
|
import org.jclouds.util.Strings2;
|
||||||
import org.testng.annotations.BeforeTest;
|
import org.testng.annotations.BeforeTest;
|
||||||
|
@ -71,7 +72,7 @@ public class ConvertToGaeRequestTest {
|
||||||
@BeforeTest
|
@BeforeTest
|
||||||
void setupClient() throws MalformedURLException {
|
void setupClient() throws MalformedURLException {
|
||||||
endPoint = URI.create("http://localhost:80/foo");
|
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());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -36,6 +36,7 @@ import javax.ws.rs.core.HttpHeaders;
|
||||||
import org.jclouds.crypto.Crypto;
|
import org.jclouds.crypto.Crypto;
|
||||||
import org.jclouds.encryption.internal.JCECrypto;
|
import org.jclouds.encryption.internal.JCECrypto;
|
||||||
import org.jclouds.http.HttpResponse;
|
import org.jclouds.http.HttpResponse;
|
||||||
|
import org.jclouds.io.ContentMetadataCodec.DefaultContentMetadataCodec;
|
||||||
import org.jclouds.util.Strings2;
|
import org.jclouds.util.Strings2;
|
||||||
import org.testng.annotations.BeforeTest;
|
import org.testng.annotations.BeforeTest;
|
||||||
import org.testng.annotations.Test;
|
import org.testng.annotations.Test;
|
||||||
|
@ -67,7 +68,7 @@ public class ConvertToJcloudsResponseTest {
|
||||||
@BeforeTest
|
@BeforeTest
|
||||||
void setupClient() throws MalformedURLException {
|
void setupClient() throws MalformedURLException {
|
||||||
endPoint = URI.create("http://localhost:80/foo");
|
endPoint = URI.create("http://localhost:80/foo");
|
||||||
req = new ConvertToJcloudsResponse();
|
req = new ConvertToJcloudsResponse(new DefaultContentMetadataCodec());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -19,7 +19,6 @@
|
||||||
package org.jclouds.aws.s3.blobstore.integration;
|
package org.jclouds.aws.s3.blobstore.integration;
|
||||||
|
|
||||||
import static org.jclouds.blobstore.options.CreateContainerOptions.Builder.publicRead;
|
import static org.jclouds.blobstore.options.CreateContainerOptions.Builder.publicRead;
|
||||||
<<<<<<< HEAD
|
|
||||||
import static org.testng.Assert.assertEquals;
|
import static org.testng.Assert.assertEquals;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -30,24 +29,35 @@ import java.util.Set;
|
||||||
import org.jclouds.blobstore.BlobStore;
|
import org.jclouds.blobstore.BlobStore;
|
||||||
import org.jclouds.blobstore.domain.BlobMetadata;
|
import org.jclouds.blobstore.domain.BlobMetadata;
|
||||||
import org.jclouds.domain.Location;
|
import org.jclouds.domain.Location;
|
||||||
=======
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.ParseException;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
import org.jclouds.blobstore.BlobStore;
|
import org.jclouds.blobstore.BlobStore;
|
||||||
>>>>>>> Issue-647: added "Expires" header for ContentMetadata
|
import org.jclouds.blobstore.BlobStoreContext;
|
||||||
|
import org.jclouds.date.DateCodec;
|
||||||
|
import org.jclouds.date.DateCodecs;
|
||||||
|
import org.jclouds.http.HttpCommandExecutorService;
|
||||||
|
import org.jclouds.http.TransformingHttpCommandExecutorService;
|
||||||
|
import org.jclouds.http.TransformingHttpCommandExecutorServiceImpl;
|
||||||
|
import org.jclouds.http.config.SSLModule;
|
||||||
|
import org.jclouds.http.internal.JavaUrlHttpCommandExecutorService;
|
||||||
|
import org.jclouds.io.ContentMetadataCodec;
|
||||||
|
import org.jclouds.io.ContentMetadataCodec.DefaultContentMetadataCodec;
|
||||||
import org.jclouds.s3.blobstore.integration.S3ContainerLiveTest;
|
import org.jclouds.s3.blobstore.integration.S3ContainerLiveTest;
|
||||||
import org.jclouds.util.Strings2;
|
import org.jclouds.util.Strings2;
|
||||||
import org.testng.annotations.Test;
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
import com.google.common.base.Strings;
|
import com.google.common.base.Strings;
|
||||||
=======
|
|
||||||
import com.google.common.base.Throwables;
|
import com.google.common.base.Throwables;
|
||||||
>>>>>>> Issue-647: added "Expires" header for ContentMetadata
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import com.google.common.collect.Iterables;
|
||||||
|
import com.google.inject.AbstractModule;
|
||||||
|
import com.google.inject.Module;
|
||||||
|
import com.google.inject.Scopes;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Adrian Cole
|
* @author Adrian Cole
|
||||||
|
@ -63,23 +73,57 @@ public class AWSS3ContainerLiveTest extends S3ContainerLiveTest {
|
||||||
final String containerName = getScratchContainerName();
|
final String containerName = getScratchContainerName();
|
||||||
BlobStore blobStore = view.getBlobStore();
|
BlobStore blobStore = view.getBlobStore();
|
||||||
try {
|
try {
|
||||||
final String RFC1123_PATTERN = "EEE, dd MMM yyyyy HH:mm:ss z";
|
|
||||||
final String blobName = "hello";
|
final String blobName = "hello";
|
||||||
final String expires = new SimpleDateFormat(RFC1123_PATTERN).format(new Date(System.currentTimeMillis()+(60*1000)));
|
final Date expires = new Date( (System.currentTimeMillis() / 1000) * 1000 + 60*1000);
|
||||||
|
|
||||||
blobStore.createContainerInLocation(null, containerName, publicRead());
|
blobStore.createContainerInLocation(null, containerName, publicRead());
|
||||||
blobStore.putBlob(containerName, blobStore.blobBuilder(blobName).payload(TEST_STRING).expires(expires).build());
|
blobStore.putBlob(containerName, blobStore.blobBuilder(blobName).payload(TEST_STRING).expires(expires).build());
|
||||||
|
|
||||||
assertConsistencyAware(new Runnable() {
|
assertConsistencyAwareBlobExpiryMetadata(containerName, blobName, expires);
|
||||||
public void run() {
|
|
||||||
try {
|
} finally {
|
||||||
String actualExpires = view.getBlobStore().getBlob(containerName, blobName).getPayload().getContentMetadata().getExpires();
|
recycleContainer(containerName);
|
||||||
assert expires.equals(actualExpires) : "expires="+actualExpires+"; expected="+expires;
|
}
|
||||||
} catch (Exception e) {
|
}
|
||||||
Throwables.propagate(e);
|
|
||||||
|
@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() {
|
||||||
|
@Override
|
||||||
|
protected DateCodec getExpiresDateCodec() {
|
||||||
|
return new DateCodec() {
|
||||||
|
@Override public Date toDate(String date) throws ParseException {
|
||||||
|
return DateCodecs.rfc1123().toDate(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 {
|
} finally {
|
||||||
recycleContainer(containerName);
|
recycleContainer(containerName);
|
||||||
|
|
|
@ -55,7 +55,7 @@ public class BlobPropertiesImpl implements Serializable, BlobProperties {
|
||||||
|
|
||||||
public BlobPropertiesImpl(BlobType type, String name, String container, URI url, Date lastModified, String eTag,
|
public BlobPropertiesImpl(BlobType type, String name, String container, URI url, Date lastModified, String eTag,
|
||||||
long size, String contentType, @Nullable byte[] contentMD5, @Nullable String contentMetadata,
|
long size, String contentType, @Nullable byte[] contentMD5, @Nullable String contentMetadata,
|
||||||
@Nullable String contentLanguage, @Nullable String currentExpires, LeaseStatus leaseStatus,
|
@Nullable String contentLanguage, @Nullable Date currentExpires, LeaseStatus leaseStatus,
|
||||||
Map<String, String> metadata) {
|
Map<String, String> metadata) {
|
||||||
this.type = checkNotNull(type, "type");
|
this.type = checkNotNull(type, "type");
|
||||||
this.leaseStatus = checkNotNull(leaseStatus, "leaseStatus");
|
this.leaseStatus = checkNotNull(leaseStatus, "leaseStatus");
|
||||||
|
|
|
@ -35,6 +35,7 @@ import org.jclouds.crypto.CryptoStreams;
|
||||||
import org.jclouds.date.DateService;
|
import org.jclouds.date.DateService;
|
||||||
import org.jclouds.http.HttpUtils;
|
import org.jclouds.http.HttpUtils;
|
||||||
import org.jclouds.http.functions.ParseSax;
|
import org.jclouds.http.functions.ParseSax;
|
||||||
|
import org.jclouds.io.ContentMetadataCodec;
|
||||||
import org.xml.sax.Attributes;
|
import org.xml.sax.Attributes;
|
||||||
import org.xml.sax.SAXException;
|
import org.xml.sax.SAXException;
|
||||||
|
|
||||||
|
@ -63,6 +64,7 @@ public class ContainerNameEnumerationResultsHandler extends ParseSax.HandlerWith
|
||||||
private StringBuilder currentText = new StringBuilder();
|
private StringBuilder currentText = new StringBuilder();
|
||||||
|
|
||||||
private final DateService dateParser;
|
private final DateService dateParser;
|
||||||
|
private final ContentMetadataCodec contentMetadataCodec;
|
||||||
private String delimiter;
|
private String delimiter;
|
||||||
private String currentName;
|
private String currentName;
|
||||||
private long currentSize;
|
private long currentSize;
|
||||||
|
@ -70,7 +72,7 @@ public class ContainerNameEnumerationResultsHandler extends ParseSax.HandlerWith
|
||||||
private String currentContentEncoding;
|
private String currentContentEncoding;
|
||||||
private String currentContentLanguage;
|
private String currentContentLanguage;
|
||||||
private BlobType currentBlobType;
|
private BlobType currentBlobType;
|
||||||
private String currentExpires;
|
private Date currentExpires;
|
||||||
private boolean inBlob;
|
private boolean inBlob;
|
||||||
private boolean inBlobPrefix;
|
private boolean inBlobPrefix;
|
||||||
private boolean inMetadata;
|
private boolean inMetadata;
|
||||||
|
@ -80,8 +82,9 @@ public class ContainerNameEnumerationResultsHandler extends ParseSax.HandlerWith
|
||||||
private LeaseStatus currentLeaseStatus;
|
private LeaseStatus currentLeaseStatus;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public ContainerNameEnumerationResultsHandler(DateService dateParser) {
|
public ContainerNameEnumerationResultsHandler(DateService dateParser, ContentMetadataCodec contentMetadataCodec) {
|
||||||
this.dateParser = dateParser;
|
this.dateParser = dateParser;
|
||||||
|
this.contentMetadataCodec = contentMetadataCodec;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ListBlobsResponse getResult() {
|
public ListBlobsResponse getResult() {
|
||||||
|
@ -175,9 +178,12 @@ public class ContainerNameEnumerationResultsHandler extends ParseSax.HandlerWith
|
||||||
if (currentContentLanguage.equals(""))
|
if (currentContentLanguage.equals(""))
|
||||||
currentContentLanguage = null;
|
currentContentLanguage = null;
|
||||||
} else if (qName.equals("Expires")) {
|
} else if (qName.equals("Expires")) {
|
||||||
currentExpires = currentText.toString().trim();
|
String trimmedCurrentText = currentText.toString().trim();
|
||||||
if (currentExpires.equals(""))
|
if (trimmedCurrentText.equals("")) {
|
||||||
currentExpires= null;
|
currentExpires = null;
|
||||||
|
} else {
|
||||||
|
currentExpires = contentMetadataCodec.parseExpires(trimmedCurrentText);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
currentText = new StringBuilder();
|
currentText = new StringBuilder();
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ package org.jclouds.azureblob.blobstore;
|
||||||
import static org.testng.Assert.assertEquals;
|
import static org.testng.Assert.assertEquals;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
import org.jclouds.azureblob.AzureBlobAsyncClient;
|
import org.jclouds.azureblob.AzureBlobAsyncClient;
|
||||||
import org.jclouds.azureblob.AzureBlobProviderMetadata;
|
import org.jclouds.azureblob.AzureBlobProviderMetadata;
|
||||||
|
@ -86,7 +87,7 @@ public class AzureBlobRequestSignerTest extends BaseAsyncClientTest<AzureBlobAsy
|
||||||
blob.getPayload().getContentMetadata().setContentLength(2l);
|
blob.getPayload().getContentMetadata().setContentLength(2l);
|
||||||
blob.getPayload().getContentMetadata().setContentMD5(new byte[] { 0, 2, 4, 8 });
|
blob.getPayload().getContentMetadata().setContentMD5(new byte[] { 0, 2, 4, 8 });
|
||||||
blob.getPayload().getContentMetadata().setContentType("text/plain");
|
blob.getPayload().getContentMetadata().setContentType("text/plain");
|
||||||
blob.getPayload().getContentMetadata().setExpires("Thu, 01 Dec 1994 16:00:00 GMT");
|
blob.getPayload().getContentMetadata().setExpires(new Date(1000));
|
||||||
|
|
||||||
HttpRequest request = signer.signPutBlob("container", blob);
|
HttpRequest request = signer.signPutBlob("container", blob);
|
||||||
|
|
||||||
|
@ -94,7 +95,7 @@ public class AzureBlobRequestSignerTest extends BaseAsyncClientTest<AzureBlobAsy
|
||||||
assertNonPayloadHeadersEqual(
|
assertNonPayloadHeadersEqual(
|
||||||
request,
|
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");
|
"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 }, "Thu, 01 Dec 1994 16:00:00 GMT");
|
assertContentHeadersEqual(request, "text/plain", null, null, null, (long) 2l, new byte[] { 0, 2, 4, 8 }, new Date(1000));
|
||||||
|
|
||||||
assertEquals(request.getFilters().size(), 0);
|
assertEquals(request.getFilters().size(), 0);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue