Skip to content
Permalink
Browse files
JCLOUDS-630: Map Payload ContentMetadata expires to Swift X-Delete-At…
… header for object expiration.
  • Loading branch information
Jeremy Daggett committed Aug 21, 2014
1 parent d9c2380 commit 007a3b7355286a8d335fb97b0384957e4d1681a0
Showing 7 changed files with 89 additions and 87 deletions.
@@ -32,14 +32,18 @@
import org.jclouds.io.Payload;
import org.jclouds.rest.Binder;

import com.google.common.hash.HashCode;

public class SetPayload implements Binder {

@SuppressWarnings("unchecked")
@Override
public <R extends HttpRequest> R bindToRequest(R request, Object input) {
Builder<?> builder = request.toBuilder();
Payload payload = Payload.class.cast(input);

if (payload.getContentMetadata().getContentType() == null) {
// TODO: use `X-Detect-Content-Type` here. Should be configurable via a property.
payload.getContentMetadata().setContentType(MediaType.APPLICATION_OCTET_STREAM);
}

@@ -51,18 +55,18 @@ public <R extends HttpRequest> R bindToRequest(R request, Object input) {
builder.replaceHeader(TRANSFER_ENCODING, "chunked").build();
}

byte[] md5 = payload.getContentMetadata().getContentMD5();
HashCode md5 = payload.getContentMetadata().getContentMD5AsHashCode();
if (md5 != null) {
// Swift will validate the md5, if placed as an ETag header
builder.replaceHeader(ETAG, base16().lowerCase().encode(md5));
builder.replaceHeader(ETAG, base16().lowerCase().encode(md5.asBytes()));
}

Date expires = payload.getContentMetadata().getExpires();
if (expires != null) {
builder.replaceHeader(OBJECT_DELETE_AT, String.valueOf(
MILLISECONDS.toSeconds(expires.getTime())))
.build();
builder.addHeader(OBJECT_DELETE_AT,
String.valueOf(MILLISECONDS.toSeconds(expires.getTime()))).build();
}

return (R) builder.payload(payload).build();
}
}
@@ -17,7 +17,6 @@
package org.jclouds.openstack.swift.v1.blobstore.functions;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.io.BaseEncoding.base16;

import org.jclouds.blobstore.domain.MutableBlobMetadata;
import org.jclouds.blobstore.domain.StorageType;
@@ -48,11 +47,12 @@ public MutableBlobMetadata apply(SwiftObject from) {
to.setPublicUri(from.getUri());
}
to.setUri(from.getUri());
to.setETag(from.getEtag());
to.setETag(from.getETag());
to.setName(from.getName());
to.setLastModified(from.getLastModified());
to.setContentMetadata(from.getPayload().getContentMetadata());
to.getContentMetadata().setContentMD5(base16().lowerCase().decode(from.getEtag()));
to.getContentMetadata().setContentMD5(from.getPayload().getContentMetadata().getContentMD5AsHashCode());
to.getContentMetadata().setExpires(from.getPayload().getContentMetadata().getExpires());
to.setUserMetadata(from.getMetadata());
String directoryName = ifDirectoryReturnName.execute(to);
if (directoryName != null) {

This file was deleted.

@@ -18,14 +18,18 @@

import static com.google.common.net.HttpHeaders.ETAG;
import static com.google.common.net.HttpHeaders.LAST_MODIFIED;
import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.OBJECT_DELETE_AT;

import java.net.URI;
import java.util.Date;

import javax.inject.Inject;

import org.jclouds.date.DateService;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse;
import org.jclouds.io.MutableContentMetadata;
import org.jclouds.io.Payload;
import org.jclouds.openstack.swift.v1.domain.SwiftObject;
import org.jclouds.rest.InvocationContext;
import org.jclouds.rest.internal.GeneratedHttpRequest;
@@ -46,11 +50,22 @@ public class ParseObjectFromResponse implements Function<HttpResponse, SwiftObje

@Override
public SwiftObject apply(HttpResponse from) {

Payload payload = from.getPayload();
MutableContentMetadata contentMeta = payload.getContentMetadata();

String deleteAt = from.getFirstHeaderOrNull(OBJECT_DELETE_AT);
if (deleteAt != null) {
long fromEpoch = Long.parseLong(from.getFirstHeaderOrNull(OBJECT_DELETE_AT)) * 1000;
contentMeta.setExpires(new Date(fromEpoch));
payload.setContentMetadata(contentMeta);
}

return SwiftObject.builder()
.uri(URI.create(uri))
.name(name)
.etag(from.getFirstHeaderOrNull(ETAG))
.payload(from.getPayload())
.payload(payload)
.lastModified(dates.rfc822DateParse(from.getFirstHeaderOrNull(LAST_MODIFIED)))
.headers(from.getHeaders())
.metadata(EntriesWithoutMetaPrefix.INSTANCE.apply(from.getHeaders())).build();
@@ -47,6 +47,7 @@ private static final class InternalObject {
long bytes;
String content_type;
Date last_modified;
Date expires;
}

private final ParseJson<List<InternalObject>> json;
@@ -80,7 +81,7 @@ public SwiftObject apply(InternalObject input) {
.uri(uriBuilder(containerUri).clearQuery().appendPath(input.name).build())
.name(input.name)
.etag(input.hash)
.payload(payload(input.bytes, input.content_type))
.payload(payload(input.bytes, input.content_type, input.expires))
.lastModified(input.last_modified).build();
}
}
@@ -97,10 +98,11 @@ public ParseObjectListFromResponse setContext(HttpRequest request) {
return this;
}

private static Payload payload(long bytes, String contentType) {
private static Payload payload(long bytes, String contentType, Date expires) {
Payload payload = Payloads.newByteSourcePayload(ByteSource.empty());
payload.getContentMetadata().setContentLength(bytes);
payload.getContentMetadata().setContentType(contentType);
payload.getContentMetadata().setExpires(expires);
return payload;
}
}
@@ -27,18 +27,23 @@
import static org.testng.Assert.fail;

import java.io.IOException;
import java.util.Date;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.TimeUnit;

import javax.ws.rs.PathParam;

import org.jclouds.http.options.GetOptions;
import org.jclouds.io.Payload;
import org.jclouds.openstack.swift.v1.CopyObjectException;
import org.jclouds.openstack.swift.v1.SwiftApi;
import org.jclouds.openstack.swift.v1.binders.SetPayload;
import org.jclouds.openstack.swift.v1.domain.ObjectList;
import org.jclouds.openstack.swift.v1.domain.SwiftObject;
import org.jclouds.openstack.swift.v1.internal.BaseSwiftApiLiveTest;
import org.jclouds.openstack.swift.v1.options.ListContainerOptions;
import org.jclouds.rest.annotations.BinderParam;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
@@ -74,6 +79,28 @@ public void testCreateWithSpacesAndSpecialCharacters() throws Exception {
}
}

public void testPutWithExpiration() throws Exception {
String objectName = "test-expiration";

long expireMillis = new Date().getTime() + 1000 * 60 * 60 * 24;
Date expireAt = new Date(expireMillis);

Payload payload = newByteSourcePayload(ByteSource.wrap("swifty".getBytes()));
payload.getContentMetadata().setExpires(expireAt);

for (String regionId : regions) {
String etag = api.getObjectApiForRegionAndContainer(regionId, containerName)
.put(objectName, payload);
assertNotNull(etag);

SwiftObject object = api.getObjectApiForRegionAndContainer(regionId, containerName).get(objectName);
assertEquals(object.getName(), objectName);
checkObject(object);
assertEquals(toStringAndClose(object.getPayload().openStream()), "swifty");

api.getObjectApiForRegionAndContainer(regionId, containerName).delete(objectName);
}
}
public void testCopyObject() throws Exception {
for (String regionId : regions) {
// source

0 comments on commit 007a3b7

Please sign in to comment.