Issue 647: added "Expires" header for ContentMetadata

This commit is contained in:
Aled Sage 2012-05-11 14:50:22 +01:00
parent a2c8993592
commit 9aedf7b6f6
24 changed files with 182 additions and 51 deletions

View File

@ -158,6 +158,16 @@ public class DelegatingMutableContentMetadata implements MutableContentMetadata
delegate.setPropertiesFromHttpHeaders(headers); delegate.setPropertiesFromHttpHeaders(headers);
} }
@Override
public void setExpires(String expires) {
delegate.setExpires(expires);
}
@Override
public String getExpires() {
return delegate.getExpires();
}
@Override @Override
public ContentMetadataBuilder toBuilder() { public ContentMetadataBuilder toBuilder() {
return delegate.toBuilder(); return delegate.toBuilder();

View File

@ -89,6 +89,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");
HttpRequest request = signer.signPutBlob("container", blob); HttpRequest request = signer.signPutBlob("container", blob);
@ -98,7 +99,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 }); assertContentHeadersEqual(request, "text/plain", null, null, null, (long) 2l, new byte[] { 0, 2, 4, 8 }, "Thu, 01 Dec 1994 16:00:00 GMT");
assertEquals(request.getFilters().size(), 0); assertEquals(request.getFilters().size(), 0);
} }

View File

@ -59,7 +59,7 @@ public class BucketListObjectMetadata implements Serializable, ObjectMetadata {
this.lastModified = lastModified; this.lastModified = lastModified;
this.eTag = eTag; this.eTag = eTag;
this.owner = owner; this.owner = owner;
this.contentMetadata = new BaseImmutableContentMetadata(null, contentLength, md5, null, null, null); this.contentMetadata = new BaseImmutableContentMetadata(null, contentLength, md5, null, null, null, null);
this.storageClass = storageClass; this.storageClass = storageClass;
} }

View File

@ -47,7 +47,7 @@ public class CopyObjectResult implements Serializable, ObjectMetadata {
public CopyObjectResult(Date lastModified, String eTag) { public CopyObjectResult(Date lastModified, String eTag) {
this.lastModified = lastModified; this.lastModified = lastModified;
this.eTag = eTag; this.eTag = eTag;
this.contentMetadata = new BaseImmutableContentMetadata(null, null, null, null, null, null); this.contentMetadata = new BaseImmutableContentMetadata(null, null, null, null, null, null, null);
} }
/** /**

View File

@ -89,7 +89,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").build(); "text/plain").expires("Thu, 01 Dec 1994 16:00:00 GMT").build();
HttpRequest request = signer.signPutBlob("container", blob); HttpRequest request = signer.signPutBlob("container", blob);
@ -98,7 +98,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 }); assertContentHeadersEqual(request, "text/plain", null, null, null, (long) 2l, new byte[] { 0, 2, 4, 8 }, "Thu, 01 Dec 1994 16:00:00 GMT");
assertEquals(request.getFilters().size(), 0); assertEquals(request.getFilters().size(), 0);
} }

View File

@ -72,12 +72,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");
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 }); assertContentHeadersEqual(request, "text/plain", null, null, null, (long) 2l, new byte[] { 0, 2, 4, 8 }, "Thu, 01 Dec 1994 16:00:00 GMT");
assertEquals(request.getFilters().size(), 0); assertEquals(request.getFilters().size(), 0);
} }

View File

@ -117,6 +117,8 @@ public interface BlobBuilder {
PayloadBlobBuilder contentEncoding(String contentEncoding); PayloadBlobBuilder contentEncoding(String contentEncoding);
PayloadBlobBuilder expires(String expires);
/** /**
* *
* @see Payloads#calculateMD5 * @see Payloads#calculateMD5

View File

@ -221,6 +221,12 @@ public class BlobBuilderImpl implements BlobBuilder {
return this; return this;
} }
@Override
public PayloadBlobBuilder expires(String expires) {
payload.getContentMetadata().setExpires(expires);
return this;
}
@Override @Override
public PayloadBlobBuilder forSigning() { public PayloadBlobBuilder forSigning() {
return builder.forSigning(); return builder.forSigning();

View File

@ -86,7 +86,7 @@ public class TransientBlobRequestSignerTest extends BaseAsyncClientTest<Transien
assertNonPayloadHeadersEqual( assertNonPayloadHeadersEqual(
request, request,
"Authorization: Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==\nContent-Length: 2\nContent-MD5: AAIECA==\nContent-Type: text/plain\n"); "Authorization: Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==\nContent-Length: 2\nContent-MD5: AAIECA==\nContent-Type: text/plain\n");
assertContentHeadersEqual(request, "text/plain", null, null, null, (long) 2l, new byte[] { 0, 2, 4, 8 }); assertContentHeadersEqual(request, "text/plain", null, null, null, (long) 2l, new byte[] { 0, 2, 4, 8 }, null);
assertEquals(request.getFilters().size(), 0); assertEquals(request.getFilters().size(), 0);
} }
@ -94,9 +94,9 @@ public class TransientBlobRequestSignerTest extends BaseAsyncClientTest<Transien
public void testSignPutBlobWithGenerate() throws ArrayIndexOutOfBoundsException, SecurityException, public void testSignPutBlobWithGenerate() throws ArrayIndexOutOfBoundsException, SecurityException,
IllegalArgumentException, NoSuchMethodException, IOException { IllegalArgumentException, NoSuchMethodException, IOException {
Blob blob = blobFactory.get().name(blobName).payload("foo").calculateMD5().contentType("text/plain").build(); Blob blob = blobFactory.get().name(blobName).payload("foo").calculateMD5().contentType("text/plain").build();
byte[] md5 = new byte[] { -84, -67, 24, -37, 76, -62, -8, 92, -19, -17, 101, 79, -52, -60, -92, -40 };
assertEquals(blob.getPayload().getContentMetadata().getContentMD5(), new byte[] { -84, -67, 24, -37, 76, -62, -8,
92, -19, -17, 101, 79, -52, -60, -92, -40 }); assertEquals(blob.getPayload().getContentMetadata().getContentMD5(), md5);
HttpRequest request = signer.signPutBlob(containerName, blob); HttpRequest request = signer.signPutBlob(containerName, blob);
@ -104,8 +104,7 @@ public class TransientBlobRequestSignerTest extends BaseAsyncClientTest<Transien
assertNonPayloadHeadersEqual( assertNonPayloadHeadersEqual(
request, request,
"Authorization: Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==\nContent-Length: 3\nContent-MD5: rL0Y20zC+Fzt72VPzMSk2A==\nContent-Type: text/plain\n"); "Authorization: Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==\nContent-Length: 3\nContent-MD5: rL0Y20zC+Fzt72VPzMSk2A==\nContent-Type: text/plain\n");
assertContentHeadersEqual(request, "text/plain", null, null, null, (long) 3l, new byte[] { -84, -67, 24, -37, 76, assertContentHeadersEqual(request, "text/plain", null, null, null, (long) 3l, md5, null);
-62, -8, 92, -19, -17, 101, 79, -52, -60, -92, -40 });
assertEquals(request.getFilters().size(), 0); assertEquals(request.getFilters().size(), 0);
} }

View File

@ -32,6 +32,7 @@ import static javax.ws.rs.core.HttpHeaders.CONTENT_ENCODING;
import static javax.ws.rs.core.HttpHeaders.CONTENT_LANGUAGE; import static javax.ws.rs.core.HttpHeaders.CONTENT_LANGUAGE;
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 static org.jclouds.util.Patterns.PATTERN_THAT_BREAKS_URI; import static org.jclouds.util.Patterns.PATTERN_THAT_BREAKS_URI;
import static org.jclouds.util.Patterns.URI_PATTERN; import static org.jclouds.util.Patterns.URI_PATTERN;
@ -197,6 +198,8 @@ public class HttpUtils {
builder.put(HttpHeaders.CONTENT_LENGTH, md.getContentLength() + ""); builder.put(HttpHeaders.CONTENT_LENGTH, md.getContentLength() + "");
if (md.getContentMD5() != null) if (md.getContentMD5() != null)
builder.put("Content-MD5", CryptoStreams.base64(md.getContentMD5())); builder.put("Content-MD5", CryptoStreams.base64(md.getContentMD5()));
if (md.getExpires() != null)
builder.put(HttpHeaders.EXPIRES, md.getExpires());
return builder.build(); return builder.build();
} }
@ -238,6 +241,7 @@ public class HttpUtils {
toMd.setContentDisposition(fromMd.getContentDisposition()); toMd.setContentDisposition(fromMd.getContentDisposition());
toMd.setContentEncoding(fromMd.getContentEncoding()); toMd.setContentEncoding(fromMd.getContentEncoding());
toMd.setContentLanguage(fromMd.getContentLanguage()); toMd.setContentLanguage(fromMd.getContentLanguage());
toMd.setExpires(fromMd.getExpires());
} }
public static URI parseEndPoint(String hostHeader) { public static URI parseEndPoint(String hostHeader) {
@ -342,6 +346,9 @@ public class HttpUtils {
if (message.getPayload().getContentMetadata().getContentLanguage() != null) if (message.getPayload().getContentMetadata().getContentLanguage() != null)
logger.debug("%s %s: %s", prefix, CONTENT_LANGUAGE, message.getPayload().getContentMetadata() logger.debug("%s %s: %s", prefix, CONTENT_LANGUAGE, message.getPayload().getContentMetadata()
.getContentLanguage()); .getContentLanguage());
if (message.getPayload().getContentMetadata().getExpires() != null)
logger.debug("%s %s: %s", prefix, EXPIRES, message.getPayload().getContentMetadata()
.getExpires());
} }
} }
@ -392,6 +399,10 @@ public class HttpUtils {
message.getPayload() == null || message.getFirstHeaderOrNull(CONTENT_LANGUAGE) == null, message.getPayload() == null || message.getFirstHeaderOrNull(CONTENT_LANGUAGE) == null,
"configuration error please use request.getPayload().getContentMetadata().setContentLanguage(value) as opposed to adding a content language header: " "configuration error please use request.getPayload().getContentMetadata().setContentLanguage(value) as opposed to adding a content language header: "
+ message); + message);
checkArgument(
message.getPayload() == null || message.getFirstHeaderOrNull(EXPIRES) == null,
"configuration error please use request.getPayload().getContentMetadata().setExpires(value) as opposed to adding an expires header: "
+ message);
} }
public static void releasePayload(HttpMessage from) { public static void releasePayload(HttpMessage from) {

View File

@ -224,6 +224,8 @@ public class JavaUrlHttpCommandExecutorService extends BaseHttpCommandExecutorSe
connection.setRequestProperty("Content-Encoding", md.getContentEncoding()); connection.setRequestProperty("Content-Encoding", md.getContentEncoding());
if (md.getContentLanguage() != null) if (md.getContentLanguage() != null)
connection.setRequestProperty("Content-Language", md.getContentLanguage()); 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 {

View File

@ -86,6 +86,14 @@ public interface ContentMetadata {
@Nullable @Nullable
String getContentLanguage(); String getContentLanguage();
/**
* Gives the date/time after which the response is considered stale.
*
* @see <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.21"/>
*/
@Nullable
String getExpires();
ContentMetadataBuilder toBuilder(); ContentMetadataBuilder toBuilder();
} }

View File

@ -51,6 +51,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;
public ContentMetadataBuilder fromHttpHeaders(Multimap<String, String> headers) { public ContentMetadataBuilder fromHttpHeaders(Multimap<String, String> headers) {
boolean chunked = any(headers.entries(), new Predicate<Entry<String, String>>() { boolean chunked = any(headers.entries(), new Predicate<Entry<String, String>>() {
@ -72,6 +73,8 @@ public class ContentMetadataBuilder implements Serializable {
contentEncoding(header.getValue()); contentEncoding(header.getValue());
} else if ("Content-Language".equalsIgnoreCase(header.getKey())) { } else if ("Content-Language".equalsIgnoreCase(header.getKey())) {
contentLanguage(header.getValue()); contentLanguage(header.getValue());
} else if ("Expires".equalsIgnoreCase(header.getKey())) {
expires(header.getValue());
} }
} }
return this; return this;
@ -113,28 +116,26 @@ public class ContentMetadataBuilder implements Serializable {
return this; return this;
} }
public ContentMetadataBuilder expires(@Nullable String expires) {
this.expires = expires;
return this;
}
public ContentMetadata build() { public ContentMetadata build() {
return new BaseImmutableContentMetadata(contentType, contentLength, contentMD5, contentDisposition, return new BaseImmutableContentMetadata(contentType, contentLength, contentMD5, contentDisposition,
contentLanguage, contentEncoding); contentLanguage, contentEncoding, expires);
} }
public static ContentMetadataBuilder fromContentMetadata(ContentMetadata in) { public static ContentMetadataBuilder fromContentMetadata(ContentMetadata in) {
return new ContentMetadataBuilder().contentType(in.getContentType()).contentLength(in.getContentLength()) return new ContentMetadataBuilder().contentType(in.getContentType()).contentLength(in.getContentLength())
.contentMD5(in.getContentMD5()).contentDisposition(in.getContentDisposition()).contentLanguage( .contentMD5(in.getContentMD5()).contentDisposition(in.getContentDisposition()).contentLanguage(
in.getContentLanguage()).contentEncoding(in.getContentEncoding()); in.getContentLanguage()).contentEncoding(in.getContentEncoding()).expires(in.getExpires());
} }
@Override @Override
public int hashCode() { public int hashCode() {
final int prime = 31; return Objects.hashCode(contentDisposition, contentEncoding, contentLanguage, contentLength,
int result = 1; contentMD5, contentType, expires);
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;
} }
@Override @Override
@ -151,13 +152,14 @@ public class ContentMetadataBuilder implements Serializable {
Objects.equal(contentLanguage, other.contentLanguage) && Objects.equal(contentLanguage, other.contentLanguage) &&
Objects.equal(contentLength, other.contentLength) && Objects.equal(contentLength, other.contentLength) &&
Arrays.equals(contentMD5, other.contentMD5) && Arrays.equals(contentMD5, other.contentMD5) &&
Objects.equal(contentType, other.contentType); Objects.equal(contentType, other.contentType) &&
Objects.equal(expires, other.expires);
} }
@Override @Override
public String toString() { public String toString() {
return "[contentDisposition=" + contentDisposition + ", contentEncoding=" + contentEncoding return "[contentDisposition=" + contentDisposition + ", contentEncoding=" + contentEncoding
+ ", contentLanguage=" + contentLanguage + ", contentLength=" + contentLength + ", contentMD5=" + ", contentLanguage=" + contentLanguage + ", contentLength=" + contentLength + ", contentMD5="
+ Arrays.toString(contentMD5) + ", contentType=" + contentType + "]"; + Arrays.toString(contentMD5) + ", contentType=" + contentType + ", expires=" + expires + "]";
} }
} }

View File

@ -66,4 +66,5 @@ public interface MutableContentMetadata extends ContentMetadata {
*/ */
void setContentEncoding(@Nullable String contentEncoding); void setContentEncoding(@Nullable String contentEncoding);
void setExpires(@Nullable String expires);
} }

View File

@ -24,6 +24,8 @@ import java.util.Arrays;
import org.jclouds.io.ContentMetadata; import org.jclouds.io.ContentMetadata;
import org.jclouds.io.ContentMetadataBuilder; import org.jclouds.io.ContentMetadataBuilder;
import com.google.common.base.Objects;
/** /**
* @author Adrian Cole * @author Adrian Cole
*/ */
@ -37,15 +39,17 @@ 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;
public BaseImmutableContentMetadata(String contentType, Long contentLength, byte[] contentMD5, 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.contentType = contentType;
this.contentLength = contentLength; this.contentLength = contentLength;
this.contentMD5 = contentMD5; this.contentMD5 = contentMD5;
this.contentDisposition = contentDisposition; this.contentDisposition = contentDisposition;
this.contentLanguage = contentLanguage; this.contentLanguage = contentLanguage;
this.contentEncoding = contentEncoding; this.contentEncoding = contentEncoding;
this.expires = expires;
} }
/** /**
@ -102,24 +106,25 @@ public class BaseImmutableContentMetadata implements ContentMetadata, Serializab
return this.contentEncoding; return this.contentEncoding;
} }
/**
* {@inheritDoc}
*/
@Override
public String getExpires() {
return this.expires;
}
@Override @Override
public String toString() { public String toString() {
return "[contentType=" + contentType + ", contentLength=" + contentLength + ", contentDisposition=" return "[contentType=" + contentType + ", contentLength=" + contentLength + ", contentDisposition="
+ contentDisposition + ", contentEncoding=" + contentEncoding + ", contentLanguage=" + contentLanguage + contentDisposition + ", contentEncoding=" + contentEncoding + ", contentLanguage=" + contentLanguage
+ ", contentMD5=" + Arrays.toString(contentMD5) + "]"; + ", contentMD5=" + Arrays.toString(contentMD5) + ", expires = " + expires + "]";
} }
@Override @Override
public int hashCode() { public int hashCode() {
final int prime = 31; return Objects.hashCode(contentDisposition, contentEncoding, contentLanguage, contentLength,
int result = 1; contentMD5, contentType, expires);
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;
} }
@Override @Override
@ -158,6 +163,9 @@ public class BaseImmutableContentMetadata implements ContentMetadata, Serializab
return false; return false;
} else if (!contentType.equals(other.contentType)) } else if (!contentType.equals(other.contentType))
return false; return false;
if (!Objects.equal(expires, other.expires)) {
return false;
}
return true; return true;
} }

View File

@ -141,6 +141,22 @@ public class BaseMutableContentMetadata extends ContentMetadataBuilder implement
return this.contentEncoding; return this.contentEncoding;
} }
/**
* {@inheritDoc}
*/
@Override
public void setExpires(@Nullable String expires) {
expires(expires);
}
/**
* {@inheritDoc}
*/
@Override
public String getExpires() {
return this.expires;
}
@Override @Override
public BaseMutableContentMetadata toBuilder() { public BaseMutableContentMetadata toBuilder() {
return BaseMutableContentMetadata.fromContentMetadata(this); return BaseMutableContentMetadata.fromContentMetadata(this);
@ -150,6 +166,6 @@ public class BaseMutableContentMetadata extends ContentMetadataBuilder implement
return (BaseMutableContentMetadata) new BaseMutableContentMetadata().contentType(in.getContentType()) return (BaseMutableContentMetadata) new BaseMutableContentMetadata().contentType(in.getContentType())
.contentLength(in.getContentLength()).contentMD5(in.getContentMD5()).contentDisposition( .contentLength(in.getContentLength()).contentMD5(in.getContentMD5()).contentDisposition(
in.getContentDisposition()).contentLanguage(in.getContentLanguage()).contentEncoding( in.getContentDisposition()).contentLanguage(in.getContentLanguage()).contentEncoding(
in.getContentEncoding()); in.getContentEncoding()).expires(in.getExpires());
} }
} }

View File

@ -138,6 +138,7 @@ public abstract class Wire {
wiredMd.setContentDisposition(oldMd.getContentDisposition()); wiredMd.setContentDisposition(oldMd.getContentDisposition());
wiredMd.setContentEncoding(oldMd.getContentEncoding()); wiredMd.setContentEncoding(oldMd.getContentEncoding());
wiredMd.setContentLanguage(oldMd.getContentLanguage()); wiredMd.setContentLanguage(oldMd.getContentLanguage());
wiredMd.setExpires(oldMd.getExpires());
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")

View File

@ -86,11 +86,22 @@ public abstract class BaseRestClientTest {
} }
protected void assertPayloadEquals(HttpRequest request, String toMatch, String contentType, boolean contentMD5) { 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, 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) {
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) { if (request.getPayload() == null) {
assertNull(toMatch); assertNull(toMatch);
} else { } else {
@ -104,7 +115,7 @@ public abstract class BaseRestClientTest {
Long length = new Long(payload.getBytes().length); Long length = new Long(payload.getBytes().length);
try { try {
assertContentHeadersEqual(request, contentType, contentDispositon, contentEncoding, contentLanguage, assertContentHeadersEqual(request, contentType, contentDispositon, contentEncoding, contentLanguage,
length, contentMD5 ? CryptoStreams.md5(request.getPayload()) : null); length, contentMD5 ? CryptoStreams.md5(request.getPayload()) : null, expires);
} catch (IOException e) { } catch (IOException e) {
propagate(e); propagate(e);
} }
@ -112,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 contentEncoding, String contentLanguage, Long length, byte[] contentMD5, String 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);
@ -125,6 +136,7 @@ public abstract class BaseRestClientTest {
assertEquals(md.getContentEncoding(), contentEncoding); assertEquals(md.getContentEncoding(), contentEncoding);
assertEquals(md.getContentLanguage(), contentLanguage); assertEquals(md.getContentLanguage(), contentLanguage);
assertEquals(md.getContentMD5(), contentMD5); assertEquals(md.getContentMD5(), contentMD5);
assertEquals(md.getExpires(), expires);
} }
// FIXME Shouldn't be assertPayloadHeadersEqual? // FIXME Shouldn't be assertPayloadHeadersEqual?

View File

@ -152,6 +152,8 @@ public class ApacheHCUtils {
apacheRequest.addHeader("Content-Encoding", payload.getContentMetadata().getContentEncoding()); apacheRequest.addHeader("Content-Encoding", payload.getContentMetadata().getContentEncoding());
if (payload.getContentMetadata().getContentLanguage() != null) if (payload.getContentMetadata().getContentLanguage() != null)
apacheRequest.addHeader("Content-Language", payload.getContentMetadata().getContentLanguage()); apacheRequest.addHeader("Content-Language", payload.getContentMetadata().getContentLanguage());
if (payload.getContentMetadata().getExpires() != null)
apacheRequest.addHeader("Expires", payload.getContentMetadata().getExpires());
assert (apacheRequest.getEntity() != null); assert (apacheRequest.getEntity() != null);
} }

View File

@ -19,6 +19,7 @@
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;
@ -29,11 +30,24 @@ 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.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.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;
>>>>>>> Issue-647: added "Expires" header for ContentMetadata
/** /**
* @author Adrian Cole * @author Adrian Cole
@ -44,6 +58,34 @@ public class AWSS3ContainerLiveTest extends S3ContainerLiveTest {
provider = "aws-s3"; 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" }) @Test(groups = { "live" })
public void testCreateBlobInLocation() throws InterruptedException, MalformedURLException, IOException { public void testCreateBlobInLocation() throws InterruptedException, MalformedURLException, IOException {
String payload = "my data"; 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); throw new NoSuchElementException("No location found with id '"+id+"'; contenders were "+locs);
} }
} }

View File

@ -55,7 +55,8 @@ 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, LeaseStatus leaseStatus, Map<String, String> metadata) { @Nullable String contentLanguage, @Nullable String currentExpires, LeaseStatus leaseStatus,
Map<String, String> metadata) {
this.type = checkNotNull(type, "type"); this.type = checkNotNull(type, "type");
this.leaseStatus = checkNotNull(leaseStatus, "leaseStatus"); this.leaseStatus = checkNotNull(leaseStatus, "leaseStatus");
this.name = checkNotNull(name, "name"); this.name = checkNotNull(name, "name");
@ -64,7 +65,7 @@ public class BlobPropertiesImpl implements Serializable, BlobProperties {
this.lastModified = checkNotNull(lastModified, "lastModified"); this.lastModified = checkNotNull(lastModified, "lastModified");
this.eTag = checkNotNull(eTag, "eTag"); this.eTag = checkNotNull(eTag, "eTag");
this.contentMetadata = new BaseImmutableContentMetadata(contentType, size, contentMD5, null, contentLanguage, this.contentMetadata = new BaseImmutableContentMetadata(contentType, size, contentMD5, null, contentLanguage,
contentMetadata); contentMetadata, currentExpires);
this.metadata.putAll(checkNotNull(metadata, "metadata")); this.metadata.putAll(checkNotNull(metadata, "metadata"));
} }

View File

@ -70,6 +70,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 boolean inBlob; private boolean inBlob;
private boolean inBlobPrefix; private boolean inBlobPrefix;
private boolean inMetadata; private boolean inMetadata;
@ -131,8 +132,8 @@ public class ContainerNameEnumerationResultsHandler extends ParseSax.HandlerWith
} else if (qName.equals("Blob")) { } else if (qName.equals("Blob")) {
BlobProperties md = new BlobPropertiesImpl(currentBlobType, currentName, containerUrl.getPath().replace("/", BlobProperties md = new BlobPropertiesImpl(currentBlobType, currentName, containerUrl.getPath().replace("/",
""), currentUrl, currentLastModified, currentETag, currentSize, currentContentType, ""), currentUrl, currentLastModified, currentETag, currentSize, currentContentType,
currentContentMD5, currentContentEncoding, currentContentLanguage, currentLeaseStatus, currentContentMD5, currentContentEncoding, currentContentLanguage, currentExpires,
currentMetadata); currentLeaseStatus, currentMetadata);
blobMetadata.add(md); blobMetadata.add(md);
currentBlobType = null; currentBlobType = null;
currentName = null; currentName = null;
@ -145,6 +146,7 @@ public class ContainerNameEnumerationResultsHandler extends ParseSax.HandlerWith
currentContentLanguage = null; currentContentLanguage = null;
currentContentMD5 = null; currentContentMD5 = null;
currentLeaseStatus = null; currentLeaseStatus = null;
currentExpires = null;
currentMetadata = Maps.newHashMap(); currentMetadata = Maps.newHashMap();
} else if (qName.equals("Url")) { } else if (qName.equals("Url")) {
currentUrl = HttpUtils.createUri(currentText.toString().trim()); currentUrl = HttpUtils.createUri(currentText.toString().trim());
@ -172,6 +174,10 @@ public class ContainerNameEnumerationResultsHandler extends ParseSax.HandlerWith
currentContentLanguage = currentText.toString().trim(); currentContentLanguage = currentText.toString().trim();
if (currentContentLanguage.equals("")) if (currentContentLanguage.equals(""))
currentContentLanguage = null; currentContentLanguage = null;
} else if (qName.equals("Expires")) {
currentExpires = currentText.toString().trim();
if (currentExpires.equals(""))
currentExpires= null;
} }
currentText = new StringBuilder(); currentText = new StringBuilder();
} }

View File

@ -86,6 +86,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");
HttpRequest request = signer.signPutBlob("container", blob); HttpRequest request = signer.signPutBlob("container", blob);
@ -93,7 +94,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 }); assertContentHeadersEqual(request, "text/plain", null, null, null, (long) 2l, new byte[] { 0, 2, 4, 8 }, "Thu, 01 Dec 1994 16:00:00 GMT");
assertEquals(request.getFilters().size(), 0); assertEquals(request.getFilters().size(), 0);
} }

View File

@ -63,17 +63,17 @@ public class ContainerNameEnumerationResultsHandlerTest extends BaseHandlerTest
new BlobPropertiesImpl(BlobType.BLOCK_BLOB, "blob1.txt", "mycontainer", URI new BlobPropertiesImpl(BlobType.BLOCK_BLOB, "blob1.txt", "mycontainer", URI
.create("http://myaccount.blob.core.windows.net/mycontainer/blob1.txt"), dateService .create("http://myaccount.blob.core.windows.net/mycontainer/blob1.txt"), dateService
.rfc822DateParse("Thu, 18 Sep 2008 18:41:57 GMT"), "0x8CAE7D55D050B8B", 8, .rfc822DateParse("Thu, 18 Sep 2008 18:41:57 GMT"), "0x8CAE7D55D050B8B", 8,
"text/plain; charset=UTF-8", null, null, null, LeaseStatus.UNLOCKED, ImmutableMap "text/plain; charset=UTF-8", null, null, null, null, LeaseStatus.UNLOCKED, ImmutableMap
.<String, String> of()), .<String, String> of()),
new BlobPropertiesImpl(BlobType.BLOCK_BLOB, "blob2.txt", "mycontainer", URI new BlobPropertiesImpl(BlobType.BLOCK_BLOB, "blob2.txt", "mycontainer", URI
.create("http://myaccount.blob.core.windows.net/mycontainer/blob2.txt"), dateService .create("http://myaccount.blob.core.windows.net/mycontainer/blob2.txt"), dateService
.rfc822DateParse("Thu, 18 Sep 2008 18:41:57 GMT"), "0x8CAE7D55CF6C339", 14, .rfc822DateParse("Thu, 18 Sep 2008 18:41:57 GMT"), "0x8CAE7D55CF6C339", 14,
"text/plain; charset=UTF-8", null, null, null, LeaseStatus.UNLOCKED, ImmutableMap "text/plain; charset=UTF-8", null, null, null, null, LeaseStatus.UNLOCKED, ImmutableMap
.<String, String> of()), .<String, String> of()),
new BlobPropertiesImpl(BlobType.PAGE_BLOB, "newblob1.txt", "mycontainer", URI new BlobPropertiesImpl(BlobType.PAGE_BLOB, "newblob1.txt", "mycontainer", URI
.create("http://myaccount.blob.core.windows.net/mycontainer/newblob1.txt"), dateService .create("http://myaccount.blob.core.windows.net/mycontainer/newblob1.txt"), dateService
.rfc822DateParse("Thu, 18 Sep 2008 18:41:57 GMT"), "0x8CAE7D55CF6C339", 25, .rfc822DateParse("Thu, 18 Sep 2008 18:41:57 GMT"), "0x8CAE7D55CF6C339", 25,
"text/plain; charset=UTF-8", null, null, null, LeaseStatus.UNLOCKED, ImmutableMap "text/plain; charset=UTF-8", null, null, null, null, LeaseStatus.UNLOCKED, ImmutableMap
.<String, String> of())); .<String, String> of()));
ListBlobsResponse list = new HashSetListBlobsResponse(contents, ListBlobsResponse list = new HashSetListBlobsResponse(contents,
@ -91,7 +91,7 @@ public class ContainerNameEnumerationResultsHandlerTest extends BaseHandlerTest
Set<BlobProperties> contents = ImmutableSet.<BlobProperties> of(new BlobPropertiesImpl(BlobType.BLOCK_BLOB, "a", Set<BlobProperties> contents = ImmutableSet.<BlobProperties> of(new BlobPropertiesImpl(BlobType.BLOCK_BLOB, "a",
"adriancole-blobstore3", URI.create("https://jclouds.blob.core.windows.net/adriancole-blobstore3/a"), "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, dateService.rfc822DateParse("Sat, 30 Jan 2010 17:46:15 GMT"), "0x8CC6FEB41736428", 8,
"application/octet-stream", null, null, null, LeaseStatus.UNLOCKED, ImmutableMap.<String, String> of())); "application/octet-stream", null, null, null, null, LeaseStatus.UNLOCKED, ImmutableMap.<String, String> of()));
ListBlobsResponse list = new HashSetListBlobsResponse(contents, ListBlobsResponse list = new HashSetListBlobsResponse(contents,
URI.create("https://jclouds.blob.core.windows.net/adriancole-blobstore3"), URI.create("https://jclouds.blob.core.windows.net/adriancole-blobstore3"),