diff --git a/apis/filesystem/src/main/java/org/jclouds/filesystem/FilesystemAsyncBlobStore.java b/apis/filesystem/src/main/java/org/jclouds/filesystem/FilesystemAsyncBlobStore.java index 33bf078aad..176b291f9a 100644 --- a/apis/filesystem/src/main/java/org/jclouds/filesystem/FilesystemAsyncBlobStore.java +++ b/apis/filesystem/src/main/java/org/jclouds/filesystem/FilesystemAsyncBlobStore.java @@ -95,6 +95,7 @@ import org.jclouds.http.HttpResponseException; import org.jclouds.http.HttpUtils; import org.jclouds.http.options.HttpRequestOptions; import org.jclouds.io.ContentMetadata; +import org.jclouds.io.ContentMetadataCodec; import org.jclouds.io.Payload; import org.jclouds.io.Payloads; import org.jclouds.io.payloads.BaseMutableContentMetadata; @@ -124,6 +125,7 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore { protected final DateService dateService; protected final Crypto crypto; protected final HttpGetOptionsListToGetOptions httpGetOptionsConverter; + protected final ContentMetadataCodec contentMetadataCodec; protected final IfDirectoryReturnNameStrategy ifDirectoryReturnName; protected final Factory blobFactory; protected final FilesystemStorageStrategy storageStrategy; @@ -132,6 +134,7 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore { protected FilesystemAsyncBlobStore(BlobStoreContext context, DateService dateService, Crypto crypto, HttpGetOptionsListToGetOptions httpGetOptionsConverter, + ContentMetadataCodec contentMetadataCodec, IfDirectoryReturnNameStrategy ifDirectoryReturnName, BlobUtils blobUtils, @Named(Constants.PROPERTY_USER_THREADS) ExecutorService service, @@ -143,6 +146,7 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore { this.dateService = dateService; this.crypto = crypto; this.httpGetOptionsConverter = httpGetOptionsConverter; + this.contentMetadataCodec = contentMetadataCodec; this.ifDirectoryReturnName = ifDirectoryReturnName; this.storageStrategy = checkNotNull(storageStrategy, "Storage strategy"); } @@ -475,7 +479,7 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore { } private void copyPayloadHeadersToBlob(Payload payload, Blob blob) { - blob.getAllHeaders().putAll(HttpUtils.getContentHeadersFromMetadata(payload.getContentMetadata())); + blob.getAllHeaders().putAll(contentMetadataCodec.toHeaders(payload.getContentMetadata())); } /** diff --git a/apis/openstack-nova-ec2/src/main/java/org/jclouds/openstack/nova/ec2/config/NovaEC2ParserModule.java b/apis/openstack-nova-ec2/src/main/java/org/jclouds/openstack/nova/ec2/config/NovaEC2ParserModule.java index d8f7931c3f..cb4ddd82db 100644 --- a/apis/openstack-nova-ec2/src/main/java/org/jclouds/openstack/nova/ec2/config/NovaEC2ParserModule.java +++ b/apis/openstack-nova-ec2/src/main/java/org/jclouds/openstack/nova/ec2/config/NovaEC2ParserModule.java @@ -114,6 +114,21 @@ public class NovaEC2ParserModule extends AbstractModule { if (Objects.equal("-", toParse)) return null; return delegate.iso8601SecondsDateParse(toParse); } + + @Override + public String rfc1123DateFormat(Date date) { + return delegate.rfc1123DateFormat(date); + } + + @Override + public String rfc1123DateFormat() { + return delegate.rfc1123DateFormat(); + } + + @Override + public Date rfc1123DateParse(String toParse) { + return delegate.rfc1123DateParse(toParse); + } } } diff --git a/core/src/main/java/org/jclouds/date/DateCodecFactory.java b/core/src/main/java/org/jclouds/date/DateCodecFactory.java new file mode 100644 index 0000000000..4dd0b97a32 --- /dev/null +++ b/core/src/main/java/org/jclouds/date/DateCodecFactory.java @@ -0,0 +1,17 @@ +package org.jclouds.date; + +import org.jclouds.date.internal.SimpleDateCodecFactory; + +import com.google.inject.ImplementedBy; + + +/** + * Codecs for converting from Date->String and vice versa. + * + * @author aled + */ +@ImplementedBy(SimpleDateCodecFactory.class) +public interface DateCodecFactory { + + public DateCodec rfc1123(); +} diff --git a/core/src/main/java/org/jclouds/date/DateCodecs.java b/core/src/main/java/org/jclouds/date/DateCodecs.java deleted file mode 100644 index 440ef8b0d7..0000000000 --- a/core/src/main/java/org/jclouds/date/DateCodecs.java +++ /dev/null @@ -1,47 +0,0 @@ -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); - } - } - } -} diff --git a/core/src/main/java/org/jclouds/date/DateService.java b/core/src/main/java/org/jclouds/date/DateService.java index ca1a699012..a8c0e2d9ed 100644 --- a/core/src/main/java/org/jclouds/date/DateService.java +++ b/core/src/main/java/org/jclouds/date/DateService.java @@ -60,4 +60,10 @@ public interface DateService { Date iso8601SecondsDateParse(String toParse); + String rfc1123DateFormat(Date date); + + String rfc1123DateFormat(); + + Date rfc1123DateParse(String toParse); + } \ No newline at end of file diff --git a/core/src/main/java/org/jclouds/date/internal/SimpleDateCodecFactory.java b/core/src/main/java/org/jclouds/date/internal/SimpleDateCodecFactory.java new file mode 100644 index 0000000000..20a0b7868d --- /dev/null +++ b/core/src/main/java/org/jclouds/date/internal/SimpleDateCodecFactory.java @@ -0,0 +1,41 @@ +package org.jclouds.date.internal; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.text.ParseException; +import java.util.Date; + +import org.jclouds.date.DateCodec; +import org.jclouds.date.DateCodecFactory; +import org.jclouds.date.DateService; + +import com.google.inject.Inject; + +public class SimpleDateCodecFactory implements DateCodecFactory { + + private final DateService dateService; + + private volatile DateCodec rfc1123Codec; + + @Inject + public SimpleDateCodecFactory(final DateService dateService) { + this.dateService = checkNotNull(dateService, "dateService"); + } + + public DateCodec rfc1123() { + if (rfc1123Codec == null) { + rfc1123Codec = new DateCodec() { + @Override + public Date toDate(String date) throws ParseException { + return dateService.rfc1123DateParse(date); + } + + @Override + public String toString(Date date) { + return dateService.rfc1123DateFormat(date); + } + }; + } + return rfc1123Codec; + } +} diff --git a/core/src/main/java/org/jclouds/date/internal/SimpleDateFormatDateService.java b/core/src/main/java/org/jclouds/date/internal/SimpleDateFormatDateService.java index b6a0108dc8..bcff3f0253 100644 --- a/core/src/main/java/org/jclouds/date/internal/SimpleDateFormatDateService.java +++ b/core/src/main/java/org/jclouds/date/internal/SimpleDateFormatDateService.java @@ -51,6 +51,11 @@ public class SimpleDateFormatDateService implements DateService { // @GuardedBy("this") private static final SimpleDateFormat rfc822SimpleDateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss 'GMT'", Locale.US); + // See http://stackoverflow.com/questions/10584647/simpledateformat-parse-is-one-hour-out-using-rfc-1123-gmt-in-summer + // for why not using "zzz" + // @GuardedBy("this") + private static final SimpleDateFormat rfc1123SimpleDateFormat = new SimpleDateFormat("EEE, dd MMM yyyyy HH:mm:ss Z", Locale.US); + // @GuardedBy("this") private static final SimpleDateFormat cSimpleDateFormat = new SimpleDateFormat("EEE MMM dd HH:mm:ss Z yyyy", Locale.US); @@ -180,4 +185,26 @@ public class SimpleDateFormatDateService implements DateService { } } -} \ No newline at end of file + @Override + public final String rfc1123DateFormat(Date date) { + synchronized (rfc1123SimpleDateFormat) { + return rfc1123SimpleDateFormat.format(date); + } + } + + @Override + public final String rfc1123DateFormat() { + return rfc1123DateFormat(new Date()); + } + + @Override + public final Date rfc1123DateParse(String toParse) { + synchronized (rfc1123SimpleDateFormat) { + try { + return rfc1123SimpleDateFormat.parse(toParse); + } catch (ParseException pe) { + throw new RuntimeException("Error parsing data at " + pe.getErrorOffset(), pe); + } + } + } +} diff --git a/core/src/main/java/org/jclouds/io/ContentMetadataCodec.java b/core/src/main/java/org/jclouds/io/ContentMetadataCodec.java index bd9298922a..e10a572870 100644 --- a/core/src/main/java/org/jclouds/io/ContentMetadataCodec.java +++ b/core/src/main/java/org/jclouds/io/ContentMetadataCodec.java @@ -14,7 +14,7 @@ import javax.ws.rs.core.HttpHeaders; import org.jclouds.crypto.CryptoStreams; import org.jclouds.date.DateCodec; -import org.jclouds.date.DateCodecs; +import org.jclouds.date.DateCodecFactory; import org.jclouds.io.ContentMetadataCodec.DefaultContentMetadataCodec; import org.jclouds.logging.Logger; @@ -23,6 +23,7 @@ import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.ImmutableMultimap.Builder; import com.google.common.collect.Multimap; import com.google.inject.ImplementedBy; +import com.google.inject.Inject; @ImplementedBy(DefaultContentMetadataCodec.class) public interface ContentMetadataCodec { @@ -53,8 +54,13 @@ public interface ContentMetadataCodec { @Resource protected Logger logger = Logger.NULL; - private final DateCodec httpExpiresDateCodec = DateCodecs.rfc1123(); + private final DateCodec httpExpiresDateCodec; + @Inject + public DefaultContentMetadataCodec(DateCodecFactory dateCodecs) { + httpExpiresDateCodec = dateCodecs.rfc1123(); + } + protected DateCodec getExpiresDateCodec() { return httpExpiresDateCodec; } diff --git a/core/src/test/java/org/jclouds/http/handlers/BackoffLimitedRetryHandlerTest.java b/core/src/test/java/org/jclouds/http/handlers/BackoffLimitedRetryHandlerTest.java index 7f18803f93..1672e5a72f 100644 --- a/core/src/test/java/org/jclouds/http/handlers/BackoffLimitedRetryHandlerTest.java +++ b/core/src/test/java/org/jclouds/http/handlers/BackoffLimitedRetryHandlerTest.java @@ -34,6 +34,8 @@ import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSession; import javax.ws.rs.core.UriBuilder; +import org.jclouds.date.internal.SimpleDateCodecFactory; +import org.jclouds.date.internal.SimpleDateFormatDateService; import org.jclouds.http.BaseJettyTest; import org.jclouds.http.HttpCommand; import org.jclouds.http.HttpResponse; @@ -113,7 +115,8 @@ public class BackoffLimitedRetryHandlerTest { ExecutorService execService = Executors.newCachedThreadPool(); BackoffLimitedRetryHandler backoff = new BackoffLimitedRetryHandler(); HttpUtils utils = new HttpUtils(0, 500, 1, 1); - ContentMetadataCodec contentMetadataCodec = new DefaultContentMetadataCodec(); + ContentMetadataCodec contentMetadataCodec = new DefaultContentMetadataCodec( + new SimpleDateCodecFactory(new SimpleDateFormatDateService())); RedirectionRetryHandler retry = new RedirectionRetryHandler(uriBuilderProvider, backoff); JavaUrlHttpCommandExecutorService httpService = new JavaUrlHttpCommandExecutorService(utils, contentMetadataCodec, execService, diff --git a/core/src/test/java/org/jclouds/rest/internal/BaseRestClientExpectTest.java b/core/src/test/java/org/jclouds/rest/internal/BaseRestClientExpectTest.java index 45f43a2fb4..7987f33f48 100644 --- a/core/src/test/java/org/jclouds/rest/internal/BaseRestClientExpectTest.java +++ b/core/src/test/java/org/jclouds/rest/internal/BaseRestClientExpectTest.java @@ -49,6 +49,8 @@ import org.jclouds.apis.ApiMetadata; import org.jclouds.concurrent.MoreExecutors; import org.jclouds.concurrent.SingleThreaded; import org.jclouds.concurrent.config.ConfiguresExecutorService; +import org.jclouds.date.internal.SimpleDateCodecFactory; +import org.jclouds.date.internal.SimpleDateFormatDateService; import org.jclouds.http.HttpCommandExecutorService; import org.jclouds.http.HttpRequest; import org.jclouds.http.HttpResponse; @@ -60,10 +62,10 @@ import org.jclouds.http.handlers.DelegatingRetryHandler; import org.jclouds.http.internal.BaseHttpCommandExecutorService; import org.jclouds.http.internal.HttpWire; import org.jclouds.io.ContentMetadataCodec; +import org.jclouds.io.ContentMetadataCodec.DefaultContentMetadataCodec; import org.jclouds.io.CopyInputStreamInputSupplierMap; import org.jclouds.io.Payload; import org.jclouds.io.Payloads; -import org.jclouds.io.ContentMetadataCodec.DefaultContentMetadataCodec; import org.jclouds.logging.config.NullLoggingModule; import org.jclouds.providers.ProviderMetadata; import org.jclouds.rest.RestApiMetadata; @@ -120,7 +122,8 @@ public abstract class BaseRestClientExpectTest { protected String provider = "mock"; - protected ContentMetadataCodec contentMetadataCodec = new DefaultContentMetadataCodec(); + protected ContentMetadataCodec contentMetadataCodec = new DefaultContentMetadataCodec( + new SimpleDateCodecFactory(new SimpleDateFormatDateService())); /** * Override this to supply alternative bindings for use in the test. This is commonly used to diff --git a/drivers/gae/src/test/java/org/jclouds/gae/ConvertToGaeRequestTest.java b/drivers/gae/src/test/java/org/jclouds/gae/ConvertToGaeRequestTest.java index 5596a13d7c..ea10bb2d38 100644 --- a/drivers/gae/src/test/java/org/jclouds/gae/ConvertToGaeRequestTest.java +++ b/drivers/gae/src/test/java/org/jclouds/gae/ConvertToGaeRequestTest.java @@ -32,6 +32,8 @@ import javax.ws.rs.HttpMethod; import javax.ws.rs.core.HttpHeaders; import org.jclouds.crypto.Crypto; +import org.jclouds.date.internal.SimpleDateCodecFactory; +import org.jclouds.date.internal.SimpleDateFormatDateService; import org.jclouds.encryption.internal.JCECrypto; import org.jclouds.http.HttpRequest; import org.jclouds.http.HttpUtils; @@ -72,7 +74,9 @@ public class ConvertToGaeRequestTest { @BeforeTest void setupClient() throws MalformedURLException { endPoint = URI.create("http://localhost:80/foo"); - req = new ConvertToGaeRequest(new HttpUtils(0, 0, 0, 0), new DefaultContentMetadataCodec()); + req = new ConvertToGaeRequest(new HttpUtils(0, 0, 0, 0), new DefaultContentMetadataCodec( + new SimpleDateCodecFactory(new SimpleDateFormatDateService()))); + } @Test diff --git a/drivers/gae/src/test/java/org/jclouds/gae/ConvertToJcloudsResponseTest.java b/drivers/gae/src/test/java/org/jclouds/gae/ConvertToJcloudsResponseTest.java index 1298d81fa8..bce7f1d0b6 100644 --- a/drivers/gae/src/test/java/org/jclouds/gae/ConvertToJcloudsResponseTest.java +++ b/drivers/gae/src/test/java/org/jclouds/gae/ConvertToJcloudsResponseTest.java @@ -34,6 +34,8 @@ import java.util.List; import javax.ws.rs.core.HttpHeaders; import org.jclouds.crypto.Crypto; +import org.jclouds.date.internal.SimpleDateCodecFactory; +import org.jclouds.date.internal.SimpleDateFormatDateService; import org.jclouds.encryption.internal.JCECrypto; import org.jclouds.http.HttpResponse; import org.jclouds.io.ContentMetadataCodec.DefaultContentMetadataCodec; @@ -68,7 +70,8 @@ public class ConvertToJcloudsResponseTest { @BeforeTest void setupClient() throws MalformedURLException { endPoint = URI.create("http://localhost:80/foo"); - req = new ConvertToJcloudsResponse(new DefaultContentMetadataCodec()); + req = new ConvertToJcloudsResponse(new DefaultContentMetadataCodec( + new SimpleDateCodecFactory(new SimpleDateFormatDateService()))); } @Test diff --git a/drivers/joda/src/main/java/org/jclouds/date/joda/JodaDateService.java b/drivers/joda/src/main/java/org/jclouds/date/joda/JodaDateService.java index 3878da429d..4382b4b293 100644 --- a/drivers/joda/src/main/java/org/jclouds/date/joda/JodaDateService.java +++ b/drivers/joda/src/main/java/org/jclouds/date/joda/JodaDateService.java @@ -53,6 +53,9 @@ public class JodaDateService implements DateService { private static final DateTimeFormatter iso8601DateFormatter = DateTimeFormat.forPattern( "yyyy-MM-dd'T'HH:mm:ss.SSSZ").withLocale(Locale.US).withZone(DateTimeZone.forID("GMT")); + private static final DateTimeFormatter rfc1123DateFormat = DateTimeFormat.forPattern( + "EEE, dd MMM yyyyy HH:mm:ss Z").withLocale(Locale.US).withZone(DateTimeZone.forID("GMT")); + public final Date fromSeconds(long seconds) { return new Date(seconds * 1000); } @@ -124,4 +127,19 @@ public class JodaDateService implements DateService { toParse += tz; return iso8601SecondsDateFormatter.parseDateTime(toParse).toDate(); } + + @Override + public final String rfc1123DateFormat(Date dateTime) { + return rfc1123DateFormat.print(new DateTime(dateTime)); + } + + @Override + public final String rfc1123DateFormat() { + return rfc1123DateFormat(new Date()); + } + + @Override + public final Date rfc1123DateParse(String toParse) { + return rfc1123DateFormat.parseDateTime(toParse).toDate(); + } } \ No newline at end of file diff --git a/providers/aws-s3/src/test/java/org/jclouds/aws/s3/blobstore/integration/AWSS3ContainerLiveTest.java b/providers/aws-s3/src/test/java/org/jclouds/aws/s3/blobstore/integration/AWSS3ContainerLiveTest.java index 5ab2d67b32..78788e3ab2 100644 --- a/providers/aws-s3/src/test/java/org/jclouds/aws/s3/blobstore/integration/AWSS3ContainerLiveTest.java +++ b/providers/aws-s3/src/test/java/org/jclouds/aws/s3/blobstore/integration/AWSS3ContainerLiveTest.java @@ -23,27 +23,18 @@ import static org.testng.Assert.assertEquals; import java.io.IOException; import java.net.MalformedURLException; +import java.text.ParseException; +import java.util.Date; import java.util.NoSuchElementException; import java.util.Set; -import org.jclouds.blobstore.BlobStore; -import org.jclouds.blobstore.domain.BlobMetadata; -import org.jclouds.domain.Location; - -import java.io.IOException; -import java.net.MalformedURLException; -import java.text.ParseException; -import java.util.Date; - import org.jclouds.blobstore.BlobStore; import org.jclouds.blobstore.BlobStoreContext; +import org.jclouds.blobstore.domain.BlobMetadata; import org.jclouds.date.DateCodec; -import org.jclouds.date.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.date.internal.SimpleDateCodecFactory; +import org.jclouds.date.internal.SimpleDateFormatDateService; +import org.jclouds.domain.Location; import org.jclouds.io.ContentMetadataCodec; import org.jclouds.io.ContentMetadataCodec.DefaultContentMetadataCodec; import org.jclouds.s3.blobstore.integration.S3ContainerLiveTest; @@ -51,13 +42,10 @@ import org.jclouds.util.Strings2; import org.testng.annotations.Test; import com.google.common.base.Strings; -import com.google.common.base.Throwables; 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 @@ -89,12 +77,12 @@ public class AWSS3ContainerLiveTest extends S3ContainerLiveTest { @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() { + final ContentMetadataCodec contentMetadataCodec = new DefaultContentMetadataCodec(new SimpleDateCodecFactory(new SimpleDateFormatDateService())) { @Override protected DateCodec getExpiresDateCodec() { return new DateCodec() { @Override public Date toDate(String date) throws ParseException { - return DateCodecs.rfc1123().toDate(date); + return new Date(); } @Override public String toString(Date date) { return "wrong";