diff --git a/apis/atmos/src/main/java/org/jclouds/atmos/domain/internal/DelegatingMutableContentMetadata.java b/apis/atmos/src/main/java/org/jclouds/atmos/domain/internal/DelegatingMutableContentMetadata.java index f81b1edb4b..15c19538de 100644 --- a/apis/atmos/src/main/java/org/jclouds/atmos/domain/internal/DelegatingMutableContentMetadata.java +++ b/apis/atmos/src/main/java/org/jclouds/atmos/domain/internal/DelegatingMutableContentMetadata.java @@ -158,6 +158,16 @@ public class DelegatingMutableContentMetadata implements MutableContentMetadata delegate.setPropertiesFromHttpHeaders(headers); } + @Override + public void setExpires(String expires) { + delegate.setExpires(expires); + } + + @Override + public String getExpires() { + return delegate.getExpires(); + } + @Override public ContentMetadataBuilder toBuilder() { return delegate.toBuilder(); diff --git a/apis/atmos/src/test/java/org/jclouds/atmos/blobstore/AtmosBlobRequestSignerTest.java b/apis/atmos/src/test/java/org/jclouds/atmos/blobstore/AtmosBlobRequestSignerTest.java index 79555eae05..bb1465f68e 100644 --- a/apis/atmos/src/test/java/org/jclouds/atmos/blobstore/AtmosBlobRequestSignerTest.java +++ b/apis/atmos/src/test/java/org/jclouds/atmos/blobstore/AtmosBlobRequestSignerTest.java @@ -89,6 +89,7 @@ public class AtmosBlobRequestSignerTest extends BaseAsyncClientTest + */ + @Nullable + String getExpires(); + ContentMetadataBuilder toBuilder(); } \ No newline at end of file diff --git a/core/src/main/java/org/jclouds/io/ContentMetadataBuilder.java b/core/src/main/java/org/jclouds/io/ContentMetadataBuilder.java index 11694871c1..647f6deda3 100644 --- a/core/src/main/java/org/jclouds/io/ContentMetadataBuilder.java +++ b/core/src/main/java/org/jclouds/io/ContentMetadataBuilder.java @@ -51,6 +51,7 @@ public class ContentMetadataBuilder implements Serializable { protected String contentDisposition; protected String contentLanguage; protected String contentEncoding; + protected String expires; public ContentMetadataBuilder fromHttpHeaders(Multimap headers) { boolean chunked = any(headers.entries(), new Predicate>() { @@ -72,6 +73,8 @@ public class ContentMetadataBuilder implements Serializable { contentEncoding(header.getValue()); } else if ("Content-Language".equalsIgnoreCase(header.getKey())) { contentLanguage(header.getValue()); + } else if ("Expires".equalsIgnoreCase(header.getKey())) { + expires(header.getValue()); } } return this; @@ -113,28 +116,26 @@ public class ContentMetadataBuilder implements Serializable { return this; } + public ContentMetadataBuilder expires(@Nullable String expires) { + this.expires = expires; + return this; + } + public ContentMetadata build() { return new BaseImmutableContentMetadata(contentType, contentLength, contentMD5, contentDisposition, - contentLanguage, contentEncoding); + contentLanguage, contentEncoding, expires); } public static ContentMetadataBuilder fromContentMetadata(ContentMetadata in) { return new ContentMetadataBuilder().contentType(in.getContentType()).contentLength(in.getContentLength()) .contentMD5(in.getContentMD5()).contentDisposition(in.getContentDisposition()).contentLanguage( - in.getContentLanguage()).contentEncoding(in.getContentEncoding()); + in.getContentLanguage()).contentEncoding(in.getContentEncoding()).expires(in.getExpires()); } @Override public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((contentDisposition == null) ? 0 : contentDisposition.hashCode()); - result = prime * result + ((contentEncoding == null) ? 0 : contentEncoding.hashCode()); - result = prime * result + ((contentLanguage == null) ? 0 : contentLanguage.hashCode()); - result = prime * result + ((contentLength == null) ? 0 : contentLength.hashCode()); - result = prime * result + Arrays.hashCode(contentMD5); - result = prime * result + ((contentType == null) ? 0 : contentType.hashCode()); - return result; + return Objects.hashCode(contentDisposition, contentEncoding, contentLanguage, contentLength, + contentMD5, contentType, expires); } @Override @@ -151,13 +152,14 @@ public class ContentMetadataBuilder implements Serializable { Objects.equal(contentLanguage, other.contentLanguage) && Objects.equal(contentLength, other.contentLength) && Arrays.equals(contentMD5, other.contentMD5) && - Objects.equal(contentType, other.contentType); + Objects.equal(contentType, other.contentType) && + Objects.equal(expires, other.expires); } @Override public String toString() { return "[contentDisposition=" + contentDisposition + ", contentEncoding=" + contentEncoding + ", contentLanguage=" + contentLanguage + ", contentLength=" + contentLength + ", contentMD5=" - + Arrays.toString(contentMD5) + ", contentType=" + contentType + "]"; + + Arrays.toString(contentMD5) + ", contentType=" + contentType + ", expires=" + expires + "]"; } -} \ No newline at end of file +} diff --git a/core/src/main/java/org/jclouds/io/MutableContentMetadata.java b/core/src/main/java/org/jclouds/io/MutableContentMetadata.java index d2de785d01..e9e0a36901 100644 --- a/core/src/main/java/org/jclouds/io/MutableContentMetadata.java +++ b/core/src/main/java/org/jclouds/io/MutableContentMetadata.java @@ -66,4 +66,5 @@ public interface MutableContentMetadata extends ContentMetadata { */ void setContentEncoding(@Nullable String contentEncoding); + void setExpires(@Nullable String expires); } \ No newline at end of file diff --git a/core/src/main/java/org/jclouds/io/payloads/BaseImmutableContentMetadata.java b/core/src/main/java/org/jclouds/io/payloads/BaseImmutableContentMetadata.java index 6253b87a22..c781366e1f 100644 --- a/core/src/main/java/org/jclouds/io/payloads/BaseImmutableContentMetadata.java +++ b/core/src/main/java/org/jclouds/io/payloads/BaseImmutableContentMetadata.java @@ -24,6 +24,8 @@ import java.util.Arrays; import org.jclouds.io.ContentMetadata; import org.jclouds.io.ContentMetadataBuilder; +import com.google.common.base.Objects; + /** * @author Adrian Cole */ @@ -37,15 +39,17 @@ public class BaseImmutableContentMetadata implements ContentMetadata, Serializab protected String contentDisposition; protected String contentLanguage; protected String contentEncoding; + protected String expires; public BaseImmutableContentMetadata(String contentType, Long contentLength, byte[] contentMD5, - String contentDisposition, String contentLanguage, String contentEncoding) { + String contentDisposition, String contentLanguage, String contentEncoding, String expires) { this.contentType = contentType; this.contentLength = contentLength; this.contentMD5 = contentMD5; this.contentDisposition = contentDisposition; this.contentLanguage = contentLanguage; this.contentEncoding = contentEncoding; + this.expires = expires; } /** @@ -102,24 +106,25 @@ public class BaseImmutableContentMetadata implements ContentMetadata, Serializab return this.contentEncoding; } + /** + * {@inheritDoc} + */ + @Override + public String getExpires() { + return this.expires; + } + @Override public String toString() { return "[contentType=" + contentType + ", contentLength=" + contentLength + ", contentDisposition=" + contentDisposition + ", contentEncoding=" + contentEncoding + ", contentLanguage=" + contentLanguage - + ", contentMD5=" + Arrays.toString(contentMD5) + "]"; + + ", contentMD5=" + Arrays.toString(contentMD5) + ", expires = " + expires + "]"; } @Override public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((contentDisposition == null) ? 0 : contentDisposition.hashCode()); - result = prime * result + ((contentEncoding == null) ? 0 : contentEncoding.hashCode()); - result = prime * result + ((contentLanguage == null) ? 0 : contentLanguage.hashCode()); - result = prime * result + ((contentLength == null) ? 0 : contentLength.hashCode()); - result = prime * result + Arrays.hashCode(contentMD5); - result = prime * result + ((contentType == null) ? 0 : contentType.hashCode()); - return result; + return Objects.hashCode(contentDisposition, contentEncoding, contentLanguage, contentLength, + contentMD5, contentType, expires); } @Override @@ -158,6 +163,9 @@ public class BaseImmutableContentMetadata implements ContentMetadata, Serializab return false; } else if (!contentType.equals(other.contentType)) return false; + if (!Objects.equal(expires, other.expires)) { + return false; + } return true; } diff --git a/core/src/main/java/org/jclouds/io/payloads/BaseMutableContentMetadata.java b/core/src/main/java/org/jclouds/io/payloads/BaseMutableContentMetadata.java index 6f3e3f3b19..50c32e878a 100644 --- a/core/src/main/java/org/jclouds/io/payloads/BaseMutableContentMetadata.java +++ b/core/src/main/java/org/jclouds/io/payloads/BaseMutableContentMetadata.java @@ -141,6 +141,22 @@ public class BaseMutableContentMetadata extends ContentMetadataBuilder implement return this.contentEncoding; } + /** + * {@inheritDoc} + */ + @Override + public void setExpires(@Nullable String expires) { + expires(expires); + } + + /** + * {@inheritDoc} + */ + @Override + public String getExpires() { + return this.expires; + } + @Override public BaseMutableContentMetadata toBuilder() { return BaseMutableContentMetadata.fromContentMetadata(this); @@ -150,6 +166,6 @@ public class BaseMutableContentMetadata extends ContentMetadataBuilder implement return (BaseMutableContentMetadata) new BaseMutableContentMetadata().contentType(in.getContentType()) .contentLength(in.getContentLength()).contentMD5(in.getContentMD5()).contentDisposition( in.getContentDisposition()).contentLanguage(in.getContentLanguage()).contentEncoding( - in.getContentEncoding()); + in.getContentEncoding()).expires(in.getExpires()); } } \ No newline at end of file diff --git a/core/src/main/java/org/jclouds/logging/internal/Wire.java b/core/src/main/java/org/jclouds/logging/internal/Wire.java index 74990e9119..ece0e6ac23 100644 --- a/core/src/main/java/org/jclouds/logging/internal/Wire.java +++ b/core/src/main/java/org/jclouds/logging/internal/Wire.java @@ -138,6 +138,7 @@ public abstract class Wire { wiredMd.setContentDisposition(oldMd.getContentDisposition()); wiredMd.setContentEncoding(oldMd.getContentEncoding()); wiredMd.setContentLanguage(oldMd.getContentLanguage()); + wiredMd.setExpires(oldMd.getExpires()); } @SuppressWarnings("unchecked") diff --git a/core/src/test/java/org/jclouds/rest/internal/BaseRestClientTest.java b/core/src/test/java/org/jclouds/rest/internal/BaseRestClientTest.java index cdee2c240b..ad6d5c5f1b 100644 --- a/core/src/test/java/org/jclouds/rest/internal/BaseRestClientTest.java +++ b/core/src/test/java/org/jclouds/rest/internal/BaseRestClientTest.java @@ -86,11 +86,22 @@ public abstract class BaseRestClientTest { } protected void assertPayloadEquals(HttpRequest request, String toMatch, String contentType, boolean contentMD5) { - assertPayloadEquals(request, toMatch, contentType, null, null, null, contentMD5); + assertPayloadEquals(request, toMatch, contentType, contentMD5, null); + } + + protected void assertPayloadEquals(HttpRequest request, String toMatch, String contentType, boolean contentMD5, String expires) { + assertPayloadEquals(request, toMatch, contentType, null, null, null, contentMD5, expires); } protected void assertPayloadEquals(HttpRequest request, String toMatch, String contentType, String contentDispositon, String contentEncoding, String contentLanguage, boolean contentMD5) { + assertPayloadEquals(request, toMatch, contentType, contentDispositon, contentEncoding, contentLanguage, + contentMD5, null); + } + + protected void assertPayloadEquals(HttpRequest request, String toMatch, String contentType, + String contentDispositon, String contentEncoding, String contentLanguage, boolean contentMD5, + String expires) { if (request.getPayload() == null) { assertNull(toMatch); } else { @@ -104,7 +115,7 @@ public abstract class BaseRestClientTest { Long length = new Long(payload.getBytes().length); try { assertContentHeadersEqual(request, contentType, contentDispositon, contentEncoding, contentLanguage, - length, contentMD5 ? CryptoStreams.md5(request.getPayload()) : null); + length, contentMD5 ? CryptoStreams.md5(request.getPayload()) : null, expires); } catch (IOException e) { propagate(e); } @@ -112,7 +123,7 @@ public abstract class BaseRestClientTest { } protected void assertContentHeadersEqual(HttpRequest request, String contentType, String contentDispositon, - String contentEncoding, String contentLanguage, Long length, byte[] contentMD5) { + String contentEncoding, String contentLanguage, Long length, byte[] contentMD5, String expires) { MutableContentMetadata md = request.getPayload().getContentMetadata(); if (request.getFirstHeaderOrNull(TRANSFER_ENCODING) == null) { assertEquals(md.getContentLength(), length); @@ -125,6 +136,7 @@ public abstract class BaseRestClientTest { assertEquals(md.getContentEncoding(), contentEncoding); assertEquals(md.getContentLanguage(), contentLanguage); assertEquals(md.getContentMD5(), contentMD5); + assertEquals(md.getExpires(), expires); } // FIXME Shouldn't be assertPayloadHeadersEqual? diff --git a/drivers/apachehc/src/main/java/org/jclouds/http/apachehc/ApacheHCUtils.java b/drivers/apachehc/src/main/java/org/jclouds/http/apachehc/ApacheHCUtils.java index 25e84bdfce..8c0a8375fc 100644 --- a/drivers/apachehc/src/main/java/org/jclouds/http/apachehc/ApacheHCUtils.java +++ b/drivers/apachehc/src/main/java/org/jclouds/http/apachehc/ApacheHCUtils.java @@ -152,6 +152,8 @@ public class ApacheHCUtils { apacheRequest.addHeader("Content-Encoding", payload.getContentMetadata().getContentEncoding()); if (payload.getContentMetadata().getContentLanguage() != null) apacheRequest.addHeader("Content-Language", payload.getContentMetadata().getContentLanguage()); + if (payload.getContentMetadata().getExpires() != null) + apacheRequest.addHeader("Expires", payload.getContentMetadata().getExpires()); assert (apacheRequest.getEntity() != null); } 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 c84e1c8d45..24a7d83a53 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 @@ -19,6 +19,7 @@ package org.jclouds.aws.s3.blobstore.integration; import static org.jclouds.blobstore.options.CreateContainerOptions.Builder.publicRead; +<<<<<<< HEAD import static org.testng.Assert.assertEquals; import java.io.IOException; @@ -29,11 +30,24 @@ 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.SimpleDateFormat; +import java.util.Date; + +import org.jclouds.blobstore.BlobStore; +>>>>>>> Issue-647: added "Expires" header for ContentMetadata import org.jclouds.s3.blobstore.integration.S3ContainerLiveTest; import org.jclouds.util.Strings2; import org.testng.annotations.Test; +<<<<<<< HEAD import com.google.common.base.Strings; +======= +import com.google.common.base.Throwables; +>>>>>>> Issue-647: added "Expires" header for ContentMetadata /** * @author Adrian Cole @@ -44,6 +58,34 @@ public class AWSS3ContainerLiveTest extends S3ContainerLiveTest { provider = "aws-s3"; } + @Test(groups = { "live" }) + public void testCreateBlobWithExpiry() throws InterruptedException, MalformedURLException, IOException { + final String containerName = getScratchContainerName(); + BlobStore blobStore = view.getBlobStore(); + try { + final String RFC1123_PATTERN = "EEE, dd MMM yyyyy HH:mm:ss z"; + final String blobName = "hello"; + final String expires = new SimpleDateFormat(RFC1123_PATTERN).format(new Date(System.currentTimeMillis()+(60*1000))); + + blobStore.createContainerInLocation(null, containerName, publicRead()); + blobStore.putBlob(containerName, blobStore.blobBuilder(blobName).payload(TEST_STRING).expires(expires).build()); + + assertConsistencyAware(new Runnable() { + public void run() { + try { + String actualExpires = view.getBlobStore().getBlob(containerName, blobName).getPayload().getContentMetadata().getExpires(); + assert expires.equals(actualExpires) : "expires="+actualExpires+"; expected="+expires; + } catch (Exception e) { + Throwables.propagate(e); + } + } + }); + + } finally { + recycleContainer(containerName); + } + } + @Test(groups = { "live" }) public void testCreateBlobInLocation() throws InterruptedException, MalformedURLException, IOException { String payload = "my data"; @@ -88,5 +130,4 @@ public class AWSS3ContainerLiveTest extends S3ContainerLiveTest { } throw new NoSuchElementException("No location found with id '"+id+"'; contenders were "+locs); } - } diff --git a/providers/azureblob/src/main/java/org/jclouds/azureblob/domain/internal/BlobPropertiesImpl.java b/providers/azureblob/src/main/java/org/jclouds/azureblob/domain/internal/BlobPropertiesImpl.java index e824a01f03..cd770fbba8 100644 --- a/providers/azureblob/src/main/java/org/jclouds/azureblob/domain/internal/BlobPropertiesImpl.java +++ b/providers/azureblob/src/main/java/org/jclouds/azureblob/domain/internal/BlobPropertiesImpl.java @@ -55,7 +55,8 @@ public class BlobPropertiesImpl implements Serializable, BlobProperties { public BlobPropertiesImpl(BlobType type, String name, String container, URI url, Date lastModified, String eTag, long size, String contentType, @Nullable byte[] contentMD5, @Nullable String contentMetadata, - @Nullable String contentLanguage, LeaseStatus leaseStatus, Map metadata) { + @Nullable String contentLanguage, @Nullable String currentExpires, LeaseStatus leaseStatus, + Map metadata) { this.type = checkNotNull(type, "type"); this.leaseStatus = checkNotNull(leaseStatus, "leaseStatus"); this.name = checkNotNull(name, "name"); @@ -64,7 +65,7 @@ public class BlobPropertiesImpl implements Serializable, BlobProperties { this.lastModified = checkNotNull(lastModified, "lastModified"); this.eTag = checkNotNull(eTag, "eTag"); this.contentMetadata = new BaseImmutableContentMetadata(contentType, size, contentMD5, null, contentLanguage, - contentMetadata); + contentMetadata, currentExpires); this.metadata.putAll(checkNotNull(metadata, "metadata")); } diff --git a/providers/azureblob/src/main/java/org/jclouds/azureblob/xml/ContainerNameEnumerationResultsHandler.java b/providers/azureblob/src/main/java/org/jclouds/azureblob/xml/ContainerNameEnumerationResultsHandler.java index f307fd3445..c297847f35 100644 --- a/providers/azureblob/src/main/java/org/jclouds/azureblob/xml/ContainerNameEnumerationResultsHandler.java +++ b/providers/azureblob/src/main/java/org/jclouds/azureblob/xml/ContainerNameEnumerationResultsHandler.java @@ -70,6 +70,7 @@ public class ContainerNameEnumerationResultsHandler extends ParseSax.HandlerWith private String currentContentEncoding; private String currentContentLanguage; private BlobType currentBlobType; + private String currentExpires; private boolean inBlob; private boolean inBlobPrefix; private boolean inMetadata; @@ -131,8 +132,8 @@ public class ContainerNameEnumerationResultsHandler extends ParseSax.HandlerWith } else if (qName.equals("Blob")) { BlobProperties md = new BlobPropertiesImpl(currentBlobType, currentName, containerUrl.getPath().replace("/", ""), currentUrl, currentLastModified, currentETag, currentSize, currentContentType, - currentContentMD5, currentContentEncoding, currentContentLanguage, currentLeaseStatus, - currentMetadata); + currentContentMD5, currentContentEncoding, currentContentLanguage, currentExpires, + currentLeaseStatus, currentMetadata); blobMetadata.add(md); currentBlobType = null; currentName = null; @@ -145,6 +146,7 @@ public class ContainerNameEnumerationResultsHandler extends ParseSax.HandlerWith currentContentLanguage = null; currentContentMD5 = null; currentLeaseStatus = null; + currentExpires = null; currentMetadata = Maps.newHashMap(); } else if (qName.equals("Url")) { currentUrl = HttpUtils.createUri(currentText.toString().trim()); @@ -172,6 +174,10 @@ public class ContainerNameEnumerationResultsHandler extends ParseSax.HandlerWith currentContentLanguage = currentText.toString().trim(); if (currentContentLanguage.equals("")) currentContentLanguage = null; + } else if (qName.equals("Expires")) { + currentExpires = currentText.toString().trim(); + if (currentExpires.equals("")) + currentExpires= null; } currentText = new StringBuilder(); } diff --git a/providers/azureblob/src/test/java/org/jclouds/azureblob/blobstore/AzureBlobRequestSignerTest.java b/providers/azureblob/src/test/java/org/jclouds/azureblob/blobstore/AzureBlobRequestSignerTest.java index 03036714cc..4ee8a121b1 100644 --- a/providers/azureblob/src/test/java/org/jclouds/azureblob/blobstore/AzureBlobRequestSignerTest.java +++ b/providers/azureblob/src/test/java/org/jclouds/azureblob/blobstore/AzureBlobRequestSignerTest.java @@ -86,6 +86,7 @@ public class AzureBlobRequestSignerTest extends BaseAsyncClientTest of()), new BlobPropertiesImpl(BlobType.BLOCK_BLOB, "blob2.txt", "mycontainer", URI .create("http://myaccount.blob.core.windows.net/mycontainer/blob2.txt"), dateService .rfc822DateParse("Thu, 18 Sep 2008 18:41:57 GMT"), "0x8CAE7D55CF6C339", 14, - "text/plain; charset=UTF-8", null, null, null, LeaseStatus.UNLOCKED, ImmutableMap + "text/plain; charset=UTF-8", null, null, null, null, LeaseStatus.UNLOCKED, ImmutableMap . of()), new BlobPropertiesImpl(BlobType.PAGE_BLOB, "newblob1.txt", "mycontainer", URI .create("http://myaccount.blob.core.windows.net/mycontainer/newblob1.txt"), dateService .rfc822DateParse("Thu, 18 Sep 2008 18:41:57 GMT"), "0x8CAE7D55CF6C339", 25, - "text/plain; charset=UTF-8", null, null, null, LeaseStatus.UNLOCKED, ImmutableMap + "text/plain; charset=UTF-8", null, null, null, null, LeaseStatus.UNLOCKED, ImmutableMap . of())); ListBlobsResponse list = new HashSetListBlobsResponse(contents, @@ -91,7 +91,7 @@ public class ContainerNameEnumerationResultsHandlerTest extends BaseHandlerTest Set contents = ImmutableSet. of(new BlobPropertiesImpl(BlobType.BLOCK_BLOB, "a", "adriancole-blobstore3", URI.create("https://jclouds.blob.core.windows.net/adriancole-blobstore3/a"), dateService.rfc822DateParse("Sat, 30 Jan 2010 17:46:15 GMT"), "0x8CC6FEB41736428", 8, - "application/octet-stream", null, null, null, LeaseStatus.UNLOCKED, ImmutableMap. of())); + "application/octet-stream", null, null, null, null, LeaseStatus.UNLOCKED, ImmutableMap. of())); ListBlobsResponse list = new HashSetListBlobsResponse(contents, URI.create("https://jclouds.blob.core.windows.net/adriancole-blobstore3"),