Repository: jclouds Updated Branches: refs/heads/1.7.x 3b2f02b72 -> 8ce9f79fd
Try iso8601SecondsDateParse if iso8601DateParse fails. S3 compatible blobStores sometimes return date in the format: "2014-07-23T20:53:17+0000" instead of the more common "2014-07-23T18:09:39.944Z". This caused jclouds to barf with an IllegalArgumentException. This commit tries to parse both the formats for S3. The exception is thrown if both fail. Added unit tests for the same. Project: http://git-wip-us.apache.org/repos/asf/jclouds/repo Commit: http://git-wip-us.apache.org/repos/asf/jclouds/commit/8ce9f79f Tree: http://git-wip-us.apache.org/repos/asf/jclouds/tree/8ce9f79f Diff: http://git-wip-us.apache.org/repos/asf/jclouds/diff/8ce9f79f Branch: refs/heads/1.7.x Commit: 8ce9f79fde4497d1131f4996c1a0f1006c59d6e8 Parents: 3b2f02b Author: Shri Javadekar <[email protected]> Authored: Fri Jul 25 16:59:18 2014 -0700 Committer: Andrew Gaul <[email protected]> Committed: Sun Jul 27 18:29:43 2014 -0700 ---------------------------------------------------------------------- .../org/jclouds/s3/xml/CopyObjectHandler.java | 3 ++- .../jclouds/s3/xml/ListAllMyBucketsHandler.java | 3 ++- .../org/jclouds/s3/xml/ListBucketHandler.java | 3 ++- .../jclouds/s3/xml/CopyObjectHandlerTest.java | 19 +++++++++++++++++++ .../jclouds/s3/xml/ListBucketHandlerTest.java | 20 +++++++++++++++++++- .../main/java/org/jclouds/date/DateService.java | 15 +++++++++++++++ .../internal/SimpleDateFormatDateService.java | 18 +++++++++++++++++- .../java/org/jclouds/date/DateServiceTest.java | 9 +++++++++ .../org/jclouds/date/joda/JodaDateService.java | 14 ++++++++++++++ 9 files changed, 99 insertions(+), 5 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/jclouds/blob/8ce9f79f/apis/s3/src/main/java/org/jclouds/s3/xml/CopyObjectHandler.java ---------------------------------------------------------------------- diff --git a/apis/s3/src/main/java/org/jclouds/s3/xml/CopyObjectHandler.java b/apis/s3/src/main/java/org/jclouds/s3/xml/CopyObjectHandler.java index 3be7618..c9bcf31 100644 --- a/apis/s3/src/main/java/org/jclouds/s3/xml/CopyObjectHandler.java +++ b/apis/s3/src/main/java/org/jclouds/s3/xml/CopyObjectHandler.java @@ -52,7 +52,8 @@ public class CopyObjectHandler extends ParseSax.HandlerWithResult<ObjectMetadata if (qName.equals("ETag")) { this.currentETag = currentOrNull(currentText); } else if (qName.equals("LastModified")) { - this.currentLastModified = dateParser.iso8601DateParse(currentOrNull(currentText)); + this.currentLastModified = dateParser + .iso8601DateParseWithOptionalTZ(currentOrNull(currentText)); } else if (qName.equals("CopyObjectResult")) { metadata = new CopyObjectResult(currentLastModified, currentETag); } http://git-wip-us.apache.org/repos/asf/jclouds/blob/8ce9f79f/apis/s3/src/main/java/org/jclouds/s3/xml/ListAllMyBucketsHandler.java ---------------------------------------------------------------------- diff --git a/apis/s3/src/main/java/org/jclouds/s3/xml/ListAllMyBucketsHandler.java b/apis/s3/src/main/java/org/jclouds/s3/xml/ListAllMyBucketsHandler.java index 0dabd19..bc65dbd 100644 --- a/apis/s3/src/main/java/org/jclouds/s3/xml/ListAllMyBucketsHandler.java +++ b/apis/s3/src/main/java/org/jclouds/s3/xml/ListAllMyBucketsHandler.java @@ -69,7 +69,8 @@ public class ListAllMyBucketsHandler extends ParseSax.HandlerWithResult<Set<Buck } else if (qName.equals("Name")) { currentName = currentOrNull(currentText); } else if (qName.equals("CreationDate")) { - currentCreationDate = dateParser.iso8601DateParse(currentOrNull(currentText)); + currentCreationDate = dateParser + .iso8601DateParseWithOptionalTZ(currentOrNull(currentText)); } currentText = new StringBuilder(); } http://git-wip-us.apache.org/repos/asf/jclouds/blob/8ce9f79f/apis/s3/src/main/java/org/jclouds/s3/xml/ListBucketHandler.java ---------------------------------------------------------------------- diff --git a/apis/s3/src/main/java/org/jclouds/s3/xml/ListBucketHandler.java b/apis/s3/src/main/java/org/jclouds/s3/xml/ListBucketHandler.java index 99c0335..feadc42 100644 --- a/apis/s3/src/main/java/org/jclouds/s3/xml/ListBucketHandler.java +++ b/apis/s3/src/main/java/org/jclouds/s3/xml/ListBucketHandler.java @@ -97,7 +97,8 @@ public class ListBucketHandler extends ParseSax.HandlerWithResult<ListBucketResp builder.key(currentKey); builder.uri(uriBuilder(getRequest().getEndpoint()).clearQuery().appendPath(currentKey).build()); } else if (qName.equals("LastModified")) { - builder.lastModified(dateParser.iso8601DateParse(currentOrNull(currentText))); + builder.lastModified(dateParser + .iso8601DateParseWithOptionalTZ(currentOrNull(currentText))); } else if (qName.equals("ETag")) { String currentETag = currentOrNull(currentText); builder.eTag(currentETag); http://git-wip-us.apache.org/repos/asf/jclouds/blob/8ce9f79f/apis/s3/src/test/java/org/jclouds/s3/xml/CopyObjectHandlerTest.java ---------------------------------------------------------------------- diff --git a/apis/s3/src/test/java/org/jclouds/s3/xml/CopyObjectHandlerTest.java b/apis/s3/src/test/java/org/jclouds/s3/xml/CopyObjectHandlerTest.java index 62744dc..9899ffa 100644 --- a/apis/s3/src/test/java/org/jclouds/s3/xml/CopyObjectHandlerTest.java +++ b/apis/s3/src/test/java/org/jclouds/s3/xml/CopyObjectHandlerTest.java @@ -25,6 +25,7 @@ import org.jclouds.date.internal.SimpleDateFormatDateService; import org.jclouds.http.functions.BaseHandlerTest; import org.jclouds.s3.domain.ObjectMetadata; import org.jclouds.s3.domain.internal.CopyObjectResult; +import org.jclouds.util.Strings2; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; @@ -39,6 +40,8 @@ public class CopyObjectHandlerTest extends BaseHandlerTest { private DateService dateService; + private final String copyObjectXML = "<CopyObjectResult xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\"><LastModified>2014-07-23T20:53:17+0000</LastModified><ETag>\"92836a3ea45a6984d1b4d23a747d46bb\"</ETag></CopyObjectResult>"; + @BeforeTest @Override protected void setUpInjector() { @@ -59,4 +62,20 @@ public class CopyObjectHandlerTest extends BaseHandlerTest { assertEquals(result, expected); } + /** + * Verifies that the parser doesn't barf if the timestamp in the copy object + * xml has time zone designators. + */ + public void testTimeStampWithTZ() { + InputStream is = Strings2.toInputStream(copyObjectXML); + ObjectMetadata expected = new CopyObjectResult( + new SimpleDateFormatDateService() + .iso8601SecondsDateParse("2014-07-23T20:53:17+0000"), + "\"92836a3ea45a6984d1b4d23a747d46bb\""); + + ObjectMetadata result = factory.create( + injector.getInstance(CopyObjectHandler.class)).parse(is); + + assertEquals(result, expected); + } } http://git-wip-us.apache.org/repos/asf/jclouds/blob/8ce9f79f/apis/s3/src/test/java/org/jclouds/s3/xml/ListBucketHandlerTest.java ---------------------------------------------------------------------- diff --git a/apis/s3/src/test/java/org/jclouds/s3/xml/ListBucketHandlerTest.java b/apis/s3/src/test/java/org/jclouds/s3/xml/ListBucketHandlerTest.java index 21c87f4..7b77e28 100644 --- a/apis/s3/src/test/java/org/jclouds/s3/xml/ListBucketHandlerTest.java +++ b/apis/s3/src/test/java/org/jclouds/s3/xml/ListBucketHandlerTest.java @@ -33,6 +33,7 @@ import org.jclouds.s3.domain.CanonicalUser; import org.jclouds.s3.domain.ListBucketResponse; import org.jclouds.s3.domain.ObjectMetadata; import org.jclouds.s3.domain.ObjectMetadataBuilder; +import org.jclouds.s3.domain.internal.CopyObjectResult; import org.jclouds.s3.domain.internal.ListBucketResponseImpl; import org.jclouds.util.Strings2; import org.testng.annotations.Test; @@ -48,6 +49,7 @@ import com.google.common.collect.ImmutableList; @Test(groups = "unit", testName = "ListBucketHandlerTest") public class ListBucketHandlerTest extends BaseHandlerTest { public static final String listBucketWithPrefixAppsSlash = "<ListBucketResult xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\"><Name>adriancole.org.jclouds.s3.amazons3testdelimiter</Name><Prefix>apps/</Prefix><Marker></Marker><MaxKeys>1000</MaxKeys><IsTruncated>false</IsTruncated><Contents><Key>apps/0</Key><LastModified>2009-05-07T18:27:08.000Z</LastModified><ETag>"c82e6a0025c31c5de5947fda62ac51ab"</ETag><Size>8</Size><Owner><ID>e1a5f66a480ca99a4fdfe8e318c3020446c9989d7004e7778029fbcc5d990fa0</ID><DisplayName>ferncam</DisplayName></Owner><StorageClass>STANDARD</StorageClass></Contents><Contents><Key>apps/1</Key><LastModified>2009-05-07T18:27:09.000Z</LastModified><ETag>"944fab2c5a9a6bacf07db5e688310d7a"</ETag><Size>8</Size><Owner><ID>e1a5f66a480ca99a4fdfe8e318c3020446c9989d7004e7778029fbcc5d990fa0</ID><DisplayName>ferncam</DisplayName></Owner><StorageClass>STANDARD</StorageClass></Contents><Contents><Key>apps/2</Key><LastModified>2009-05-07T18:27:09.000Z</Las tModified><ETag>"a227b8888045c8fd159fb495214000f0"</ETag><Size>8</Size><Owner><ID>e1a5f66a480ca99a4fdfe8e318c3020446c9989d7004e7778029fbcc5d990fa0</ID><DisplayName>ferncam</DisplayName></Owner><StorageClass>STANDARD</StorageClass></Contents><Contents><Key>apps/3</Key><LastModified>2009-05-07T18:27:09.000Z</LastModified><ETag>"c9caa76c3dec53e2a192608ce73eef03"</ETag><Size>8</Size><Owner><ID>e1a5f66a480ca99a4fdfe8e318c3020446c9989d7004e7778029fbcc5d990fa0</ID><DisplayName>ferncam</DisplayName></Owner><StorageClass>STANDARD</StorageClass></Contents><Contents><Key>apps/4</Key><LastModified>2009-05-07T18:27:09.000Z</LastModified><ETag>"1ce5d0dcc6154a647ea90c7bdf82a224"</ETag><Size>8</Size><Owner><ID>e1a5f66a480ca99a4fdfe8e318c3020446c9989d7004e7778029fbcc5d990fa0</ID><DisplayName>ferncam</DisplayName></Owner><StorageClass>STANDARD</StorageClass></Contents><Contents><Key>apps/5</Key><LastModified>2009-05-07T18:27:09.000Z</LastModified><ETag>"79433524d874 62ee05708a8ef894ed55"</ETag><Size>8</Size><Owner><ID>e1a5f66a480ca99a4fdfe8e318c3020446c9989d7004e7778029fbcc5d990fa0</ID><DisplayName>ferncam</DisplayName></Owner><StorageClass>STANDARD</StorageClass></Contents><Contents><Key>apps/6</Key><LastModified>2009-05-07T18:27:10.000Z</LastModified><ETag>"dd00a060b28ddca8bc5a21a49e306f67"</ETag><Size>8</Size><Owner><ID>e1a5f66a480ca99a4fdfe8e318c3020446c9989d7004e7778029fbcc5d990fa0</ID><DisplayName>ferncam</DisplayName></Owner><StorageClass>STANDARD</StorageClass></Contents><Contents><Key>apps/7</Key><LastModified>2009-05-07T18:27:10.000Z</LastModified><ETag>"8cd06eca6e819a927b07a285d750b100"</ETag><Size>8</Size><Owner><ID>e1a5f66a480ca99a4fdfe8e318c3020446c9989d7004e7778029fbcc5d990fa0</ID><DisplayName>ferncam</DisplayName></Owner><StorageClass>STANDARD</StorageClass></Contents><Contents><Key>apps/8</Key><LastModified>2009-05-07T18:27:10.000Z</LastModified><ETag>"174495094d0633b92cbe46603eee6bad"</ETag>< Size>8</Size><Owner><ID>e1a5f66a480ca99a4fdfe8e318c3020446c9989d7004e7778029fbcc5d990fa0</ID><DisplayName>ferncam</DisplayName></Owner><StorageClass>STANDARD</StorageClass></Contents><Contents><Key>apps/9</Key><LastModified>2009-05-07T18:27:10.000Z</LastModified><ETag>"cd8a19b26fea8a827276df0ad11c580d"</ETag><Size>8</Size><Owner><ID>e1a5f66a480ca99a4fdfe8e318c3020446c9989d7004e7778029fbcc5d990fa0</ID><DisplayName>ferncam</DisplayName></Owner><StorageClass>STANDARD</StorageClass></Contents></ListBucketResult>"; + public static final String listBucketWithTSTimeZone = "<ListBucketResult xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\"><Name>adriancole.org.jclouds.s3.amazons3testdelimiter</Name><Prefix>apps/</Prefix><Marker></Marker><MaxKeys>1000</MaxKeys><IsTruncated>false</IsTruncated><Contents><Key>apps/9</Key><LastModified>2014-07-23T20:53:17+0000</LastModified><ETag>"cd8a19b26fea8a827276df0ad11c580d"</ETag><Size>8</Size><Owner><ID>e1a5f66a480ca99a4fdfe8e318c3020446c9989d7004e7778029fbcc5d990fa0</ID><DisplayName>ferncam</DisplayName></Owner><StorageClass>STANDARD</StorageClass></Contents></ListBucketResult>"; public static final String listBucketWithSlashDelimiterAndCommonPrefixApps = "<ListBucketResult xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\"> <Delimiter>/</Delimiter> <CommonPrefixes><Prefix>apps/</Prefix></CommonPrefixes></ListBucketResult>"; private DateService dateService = new SimpleDateFormatDateService(); @@ -132,7 +134,23 @@ public class ListBucketHandlerTest extends BaseHandlerTest { assertEquals(bucket.getPrefix(), "apps/"); assertEquals(bucket.getMaxKeys(), 1000); assert bucket.getMarker() == null; - } + /** + * Verifies that the parser doesn't barf if the timestamp returned in the + * list bucket response has time zone designators in it. + */ + @Test + public void testListMyBucketsWithTZ() { + ListBucketResponse bucket = createParser().parse( + Strings2.toInputStream(listBucketWithTSTimeZone)); + ObjectMetadata expected = new CopyObjectResult( + new SimpleDateFormatDateService() + .iso8601SecondsDateParse("2014-07-23T20:53:17+0000"), + "\"92836a3ea45a6984d1b4d23a747d46bb\""); + + // Verify that the date was parsed successfully. + ObjectMetadata metadata = bucket.iterator().next(); + assertEquals(metadata.getLastModified(), expected.getLastModified()); + } } http://git-wip-us.apache.org/repos/asf/jclouds/blob/8ce9f79f/core/src/main/java/org/jclouds/date/DateService.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/jclouds/date/DateService.java b/core/src/main/java/org/jclouds/date/DateService.java index 53652e6..7d5b3bd 100644 --- a/core/src/main/java/org/jclouds/date/DateService.java +++ b/core/src/main/java/org/jclouds/date/DateService.java @@ -76,6 +76,21 @@ public interface DateService { */ Date iso8601SecondsDateParse(String toParse) throws IllegalArgumentException; + /** + * Parse a given date in either of two iso8601 formats: + * "yyyy-MM-dd'T'HH:mm:ssZ" or "yyyy-MM-dd'T'HH:mm:ss.SSSZ". The latter one + * has the timezone designator, e.g. 2014-07-23T20:53:17+0000. At least one + * S3 compatible blobstore uses both these formats when returning + * container/object metadata. + * + * @param toParse + * The string to parse. + * @return the Date object of the parsed string. + * @throws IllegalArgumentException + */ + Date iso8601DateParseWithOptionalTZ(String toParse) + throws IllegalArgumentException; + String rfc1123DateFormat(Date date); String rfc1123DateFormat(); http://git-wip-us.apache.org/repos/asf/jclouds/blob/8ce9f79f/core/src/main/java/org/jclouds/date/internal/SimpleDateFormatDateService.java ---------------------------------------------------------------------- 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 d1611ac..e3ce913 100644 --- a/core/src/main/java/org/jclouds/date/internal/SimpleDateFormatDateService.java +++ b/core/src/main/java/org/jclouds/date/internal/SimpleDateFormatDateService.java @@ -18,6 +18,7 @@ package org.jclouds.date.internal; import static org.jclouds.date.internal.DateUtils.findTZ; import static org.jclouds.date.internal.DateUtils.trimTZ; import static org.jclouds.date.internal.DateUtils.trimToMillis; +import static org.jclouds.util.SaxUtils.currentOrNull; import java.text.ParseException; import java.text.SimpleDateFormat; @@ -152,7 +153,8 @@ public class SimpleDateFormatDateService implements DateService { } @Override - public final Date iso8601SecondsDateParse(String toParse) { + public final Date iso8601SecondsDateParse(String toParse) + throws IllegalArgumentException { if (toParse.length() < 10) throw new IllegalArgumentException("incorrect date format " + toParse); String tz = findTZ(toParse); @@ -204,4 +206,18 @@ public class SimpleDateFormatDateService implements DateService { } } } + + @Override + public Date iso8601DateParseWithOptionalTZ(String toParse) + throws IllegalArgumentException { + try { + return iso8601DateParse(toParse); + } catch (IllegalArgumentException orig) { + try { + return iso8601SecondsDateParse(toParse); + } catch (IllegalArgumentException ie) { + throw orig; + } + } + } } http://git-wip-us.apache.org/repos/asf/jclouds/blob/8ce9f79f/core/src/test/java/org/jclouds/date/DateServiceTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/jclouds/date/DateServiceTest.java b/core/src/test/java/org/jclouds/date/DateServiceTest.java index b945275..e8c61a3 100644 --- a/core/src/test/java/org/jclouds/date/DateServiceTest.java +++ b/core/src/test/java/org/jclouds/date/DateServiceTest.java @@ -110,6 +110,15 @@ public class DateServiceTest extends PerformanceTest { } @Test + public void testIso8601OptionalTZDateParse() { + Date dsDate = dateService + .iso8601DateParseWithOptionalTZ(testData[0].iso8601SecondsDateString); + Date secondsDate = dateService + .iso8601SecondsDateParse(testData[0].iso8601SecondsDateString); + assertEquals(dsDate, secondsDate); + } + + @Test public void testIso8601SecondsDateParse() { Date dsDate = dateService.iso8601SecondsDateParse(testData[0].iso8601SecondsDateString); assertEquals(dsDate, testData[0].date); http://git-wip-us.apache.org/repos/asf/jclouds/blob/8ce9f79f/drivers/joda/src/main/java/org/jclouds/date/joda/JodaDateService.java ---------------------------------------------------------------------- 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 1658fed..7bb1519 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 @@ -146,4 +146,18 @@ public class JodaDateService implements DateService { public final Date rfc1123DateParse(String toParse) { return rfc1123DateFormat.parseDateTime(toParse).toDate(); } + + @Override + public Date iso8601DateParseWithOptionalTZ(String toParse) + throws IllegalArgumentException { + try { + return iso8601DateParse(toParse); + } catch (IllegalArgumentException orig) { + try { + return iso8601SecondsDateParse(toParse); + } catch (IllegalArgumentException ie) { + throw orig; + } + } + } }
