JCLOUDS-298. expose container metadata via ObjectList and port Object PUT code from legacy codebase
Project: http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/commit/0426ebf0 Tree: http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/tree/0426ebf0 Diff: http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/diff/0426ebf0 Branch: refs/heads/master Commit: 0426ebf0c00113f3affc608c2ba83da7bb5554be Parents: 523b134 Author: Adrian Cole <[email protected]> Authored: Sun Sep 29 23:49:50 2013 -0700 Committer: Adrian Cole <[email protected]> Committed: Mon Sep 30 00:16:17 2013 -0700 ---------------------------------------------------------------------- .../openstack/swift/v1/binders/SetPayload.java | 49 ++++++++++++++++++++ .../openstack/swift/v1/domain/ObjectList.java | 47 +++++++++++++++++++ .../openstack/swift/v1/features/BulkApi.java | 2 +- .../openstack/swift/v1/features/ObjectApi.java | 22 +++------ .../functions/ParseObjectListFromResponse.java | 21 ++++++--- .../swift/v1/features/ObjectApiLiveTest.java | 5 +- .../swift/v1/features/ObjectApiMockTest.java | 16 ++++--- .../swift/v1/internal/BaseSwiftApiLiveTest.java | 21 +++++---- 8 files changed, 142 insertions(+), 41 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/blob/0426ebf0/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/binders/SetPayload.java ---------------------------------------------------------------------- diff --git a/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/binders/SetPayload.java b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/binders/SetPayload.java new file mode 100644 index 0000000..70aa6f0 --- /dev/null +++ b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/binders/SetPayload.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.openstack.swift.v1.binders; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.io.BaseEncoding.base16; +import static com.google.common.net.HttpHeaders.ETAG; +import static com.google.common.net.HttpHeaders.TRANSFER_ENCODING; + +import org.jclouds.http.HttpRequest; +import org.jclouds.http.HttpRequest.Builder; +import org.jclouds.io.Payload; +import org.jclouds.rest.Binder; + +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); + Long contentLength = payload.getContentMetadata().getContentLength(); + if (contentLength != null && contentLength >= 0) { + checkArgument(contentLength <= 5l * 1024 * 1024 * 1024, "maximum size for put object is 5GB, %s", + contentLength); + } else { + builder.replaceHeader(TRANSFER_ENCODING, "chunked").build(); + } + byte[] md5 = payload.getContentMetadata().getContentMD5(); + if (md5 != null) { + // Swift will validate the md5, if placed as an ETag header + builder.replaceHeader(ETAG, base16().lowerCase().encode(md5)); + } + return (R) builder.payload(payload).build(); + } +} http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/blob/0426ebf0/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/domain/ObjectList.java ---------------------------------------------------------------------- diff --git a/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/domain/ObjectList.java b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/domain/ObjectList.java new file mode 100644 index 0000000..350e8e1 --- /dev/null +++ b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/domain/ObjectList.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.openstack.swift.v1.domain; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.List; + +import com.google.common.collect.ForwardingList; + +public class ObjectList extends ForwardingList<SwiftObject> { + + public static ObjectList create(List<SwiftObject> objects, Container container) { + return new ObjectList(objects, container); + } + + private final List<SwiftObject> objects; + private final Container container; + + protected ObjectList(List<SwiftObject> objects, Container container) { + this.objects = checkNotNull(objects, "objects"); + this.container = checkNotNull(container, "container"); + } + + public Container container() { + return container; + } + + @Override + protected List<SwiftObject> delegate() { + return objects; + } +} http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/blob/0426ebf0/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/features/BulkApi.java ---------------------------------------------------------------------- diff --git a/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/features/BulkApi.java b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/features/BulkApi.java index 0cc4546..60eb851 100644 --- a/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/features/BulkApi.java +++ b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/features/BulkApi.java @@ -33,9 +33,9 @@ import org.jclouds.http.HttpRequest; import org.jclouds.io.Payload; import org.jclouds.io.Payloads; import org.jclouds.openstack.keystone.v2_0.filters.AuthenticateRequest; +import org.jclouds.openstack.swift.v1.binders.SetPayload; import org.jclouds.openstack.swift.v1.domain.BulkDeleteResponse; import org.jclouds.openstack.swift.v1.domain.ExtractArchiveResponse; -import org.jclouds.openstack.swift.v1.features.ObjectApi.SetPayload; import org.jclouds.rest.Binder; import org.jclouds.rest.annotations.BinderParam; import org.jclouds.rest.annotations.QueryParams; http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/blob/0426ebf0/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/features/ObjectApi.java ---------------------------------------------------------------------- diff --git a/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/features/ObjectApi.java b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/features/ObjectApi.java index ebcc41b..14ce2f4 100644 --- a/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/features/ObjectApi.java +++ b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/features/ObjectApi.java @@ -30,31 +30,28 @@ import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; -import org.jclouds.Fallbacks.EmptyFluentIterableOnNotFoundOr404; import org.jclouds.Fallbacks.FalseOnNotFoundOr404; import org.jclouds.Fallbacks.NullOnNotFoundOr404; import org.jclouds.Fallbacks.VoidOnNotFoundOr404; -import org.jclouds.http.HttpRequest; import org.jclouds.http.options.GetOptions; import org.jclouds.io.Payload; import org.jclouds.javax.annotation.Nullable; import org.jclouds.openstack.keystone.v2_0.filters.AuthenticateRequest; import org.jclouds.openstack.swift.v1.binders.BindMetadataToHeaders.BindObjectMetadataToHeaders; import org.jclouds.openstack.swift.v1.binders.BindMetadataToHeaders.BindRemoveObjectMetadataToHeaders; +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.functions.ETagHeader; import org.jclouds.openstack.swift.v1.functions.ParseObjectFromResponse; import org.jclouds.openstack.swift.v1.functions.ParseObjectListFromResponse; import org.jclouds.openstack.swift.v1.options.ListContainerOptions; -import org.jclouds.rest.Binder; import org.jclouds.rest.annotations.BinderParam; import org.jclouds.rest.annotations.Fallback; import org.jclouds.rest.annotations.QueryParams; import org.jclouds.rest.annotations.RequestFilters; import org.jclouds.rest.annotations.ResponseParser; -import com.google.common.collect.FluentIterable; - /** * @see <a href= * "http://docs.openstack.org/api/openstack-object-storage/1.0/content/storage-object-services.html" @@ -67,15 +64,16 @@ public interface ObjectApi { /** * Lists up to 10,000 objects. * - * @return a list of existing storage objects ordered by name. + * @return a list of existing storage objects ordered by name or null. */ @Named("ListObjects") @GET @QueryParams(keys = "format", values = "json") @ResponseParser(ParseObjectListFromResponse.class) - @Fallback(EmptyFluentIterableOnNotFoundOr404.class) + @Fallback(NullOnNotFoundOr404.class) @Path("/") - FluentIterable<SwiftObject> list(ListContainerOptions options); + @Nullable + ObjectList list(ListContainerOptions options); /** * Creates or updates an object. @@ -99,14 +97,6 @@ public interface ObjectApi { String replace(@PathParam("objectName") String objectName, @BinderParam(SetPayload.class) Payload payload, @BinderParam(BindObjectMetadataToHeaders.class) Map<String, String> metadata); - static class SetPayload implements Binder { - @SuppressWarnings("unchecked") - @Override - public <R extends HttpRequest> R bindToRequest(R request, Object input) { - return (R) request.toBuilder().payload(Payload.class.cast(input)).build(); - } - } - /** * Gets the {@link SwiftObject} metadata without its * {@link Payload#getInput() body}. http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/blob/0426ebf0/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/functions/ParseObjectListFromResponse.java ---------------------------------------------------------------------- diff --git a/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/functions/ParseObjectListFromResponse.java b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/functions/ParseObjectListFromResponse.java index 589e192..5cfa562 100644 --- a/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/functions/ParseObjectListFromResponse.java +++ b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/functions/ParseObjectListFromResponse.java @@ -27,13 +27,16 @@ import org.jclouds.http.HttpResponse; import org.jclouds.http.functions.ParseJson; import org.jclouds.io.Payload; import org.jclouds.io.Payloads; +import org.jclouds.openstack.swift.v1.domain.Container; +import org.jclouds.openstack.swift.v1.domain.ObjectList; import org.jclouds.openstack.swift.v1.domain.SwiftObject; import org.jclouds.rest.InvocationContext; +import org.jclouds.rest.internal.GeneratedHttpRequest; import com.google.common.base.Function; -import com.google.common.collect.FluentIterable; +import com.google.common.collect.Lists; -public class ParseObjectListFromResponse implements Function<HttpResponse, FluentIterable<SwiftObject>>, +public class ParseObjectListFromResponse implements Function<HttpResponse, ObjectList>, InvocationContext<ParseObjectListFromResponse> { private static final class InternalObject { @@ -45,18 +48,21 @@ public class ParseObjectListFromResponse implements Function<HttpResponse, Fluen } private final ParseJson<List<InternalObject>> json; + private final ParseContainerFromHeaders parseContainer; @Inject - ParseObjectListFromResponse(ParseJson<List<InternalObject>> json) { + ParseObjectListFromResponse(ParseJson<List<InternalObject>> json, ParseContainerFromHeaders parseContainer) { this.json = json; + this.parseContainer = parseContainer; } private ToSwiftObject toSwiftObject; @Override - public FluentIterable<SwiftObject> apply(HttpResponse from) { - return FluentIterable.from(json.apply(from)) // - .transform(toSwiftObject); + public ObjectList apply(HttpResponse from) { + List<SwiftObject> objects = Lists.transform(json.apply(from), toSwiftObject); + Container container = parseContainer.apply(from); + return ObjectList.create(objects, container); } static class ToSwiftObject implements Function<InternalObject, SwiftObject> { @@ -79,12 +85,13 @@ public class ParseObjectListFromResponse implements Function<HttpResponse, Fluen @Override public ParseObjectListFromResponse setContext(HttpRequest request) { + parseContainer.name = GeneratedHttpRequest.class.cast(request).getCaller().get().getArgs().get(1).toString(); String containerUri = request.getEndpoint().toString(); int queryIndex = containerUri.indexOf('?'); if (queryIndex != -1) { containerUri = containerUri.substring(0, queryIndex); } - this.toSwiftObject = new ToSwiftObject(containerUri); + toSwiftObject = new ToSwiftObject(containerUri); return this; } http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/blob/0426ebf0/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/ObjectApiLiveTest.java ---------------------------------------------------------------------- diff --git a/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/ObjectApiLiveTest.java b/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/ObjectApiLiveTest.java index 8dad19a..cfc7192 100644 --- a/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/ObjectApiLiveTest.java +++ b/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/ObjectApiLiveTest.java @@ -32,6 +32,7 @@ import java.util.Map.Entry; import java.util.concurrent.TimeUnit; import org.jclouds.http.options.GetOptions; +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.CreateContainerOptions; @@ -40,7 +41,6 @@ import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; -import com.google.common.collect.FluentIterable; import com.google.common.collect.ImmutableMap; /** @@ -56,7 +56,8 @@ public class ObjectApiLiveTest extends BaseSwiftApiLiveTest { public void list() throws Exception { for (String regionId : api.configuredRegions()) { ObjectApi objectApi = api.objectApiInRegionForContainer(regionId, containerName); - FluentIterable<SwiftObject> response = objectApi.list(new ListContainerOptions()); + ObjectList response = objectApi.list(new ListContainerOptions()); + assertEquals(response.container(), api.containerApiInRegion(regionId).get(containerName)); assertNotNull(response); for (SwiftObject object : response) { checkObject(object); http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/blob/0426ebf0/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/ObjectApiMockTest.java ---------------------------------------------------------------------- diff --git a/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/ObjectApiMockTest.java b/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/ObjectApiMockTest.java index 789ec00..3b54eac 100644 --- a/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/ObjectApiMockTest.java +++ b/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/ObjectApiMockTest.java @@ -23,6 +23,7 @@ import static org.jclouds.io.Payloads.newStringPayload; import static org.jclouds.openstack.swift.v1.options.ListContainerOptions.Builder.marker; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertTrue; +import static org.jclouds.openstack.swift.v1.features.ContainerApiMockTest.*; import java.net.URI; import java.util.Map; @@ -32,6 +33,7 @@ import org.jclouds.date.internal.SimpleDateFormatDateService; import org.jclouds.io.Payload; import org.jclouds.io.Payloads; import org.jclouds.openstack.swift.v1.SwiftApi; +import org.jclouds.openstack.swift.v1.domain.ObjectList; import org.jclouds.openstack.swift.v1.domain.SwiftObject; import org.jclouds.openstack.swift.v1.internal.BaseSwiftMockTest; import org.jclouds.openstack.swift.v1.options.ListContainerOptions; @@ -82,13 +84,16 @@ public class ObjectApiMockTest extends BaseSwiftMockTest { public void list() throws Exception { MockWebServer server = mockSwiftServer(); server.enqueue(new MockResponse().setBody(access)); - server.enqueue(new MockResponse().setBody(objectList)); + server.enqueue(containerResponse() // + .addHeader("X-Container-Read", ".r:*,.rlistings") // + .setBody(objectList)); try { SwiftApi api = swiftApi(server.getUrl("/").toString()); - ImmutableList<SwiftObject> objects = api.objectApiInRegionForContainer("DFW", "myContainer") - .list(new ListContainerOptions()).toList(); + ObjectList objects = api.objectApiInRegionForContainer("DFW", "myContainer").list(new ListContainerOptions()); assertEquals(objects, parsedObjectsForUrl(server.getUrl("/").toString())); + assertEquals(objects.container().name(), "myContainer"); + assertTrue(objects.container().anybodyRead().get()); assertEquals(server.getRequestCount(), 2); assertEquals(server.takeRequest().getRequestLine(), "POST /tokens HTTP/1.1"); @@ -102,12 +107,11 @@ public class ObjectApiMockTest extends BaseSwiftMockTest { public void listOptions() throws Exception { MockWebServer server = mockSwiftServer(); server.enqueue(new MockResponse().setBody(access)); - server.enqueue(new MockResponse().setBody(objectList)); + server.enqueue(containerResponse().setBody(objectList)); try { SwiftApi api = swiftApi(server.getUrl("/").toString()); - ImmutableList<SwiftObject> objects = api.objectApiInRegionForContainer("DFW", "myContainer") - .list(marker("test")).toList(); + ObjectList objects = api.objectApiInRegionForContainer("DFW", "myContainer").list(marker("test")); assertEquals(objects, parsedObjectsForUrl(server.getUrl("/").toString())); assertEquals(server.getRequestCount(), 2); http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/blob/0426ebf0/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/internal/BaseSwiftApiLiveTest.java ---------------------------------------------------------------------- diff --git a/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/internal/BaseSwiftApiLiveTest.java b/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/internal/BaseSwiftApiLiveTest.java index 491a5f6..8d8166e 100644 --- a/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/internal/BaseSwiftApiLiveTest.java +++ b/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/internal/BaseSwiftApiLiveTest.java @@ -18,17 +18,19 @@ package org.jclouds.openstack.swift.v1.internal; import static com.google.common.base.Preconditions.checkState; +import java.util.List; import java.util.Properties; import org.jclouds.apis.BaseApiLiveTest; import org.jclouds.openstack.keystone.v2_0.config.KeystoneProperties; import org.jclouds.openstack.swift.v1.SwiftApi; import org.jclouds.openstack.swift.v1.domain.BulkDeleteResponse; +import org.jclouds.openstack.swift.v1.domain.ObjectList; import org.jclouds.openstack.swift.v1.domain.SwiftObject; import org.jclouds.openstack.swift.v1.options.ListContainerOptions; import com.google.common.base.Function; -import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; public class BaseSwiftApiLiveTest extends BaseApiLiveTest<SwiftApi> { @@ -44,14 +46,15 @@ public class BaseSwiftApiLiveTest extends BaseApiLiveTest<SwiftApi> { } protected void deleteAllObjectsInContainer(String regionId, final String containerName) { - ImmutableList<String> pathsToDelete = api.objectApiInRegionForContainer(regionId, containerName) - .list(new ListContainerOptions()).transform(new Function<SwiftObject, String>() { - - public String apply(SwiftObject input) { - return containerName + "/" + input.name(); - } - - }).toList(); + ObjectList objects = api.objectApiInRegionForContainer(regionId, containerName).list(new ListContainerOptions()); + if (objects == null) { + return; + } + List<String> pathsToDelete = Lists.transform(objects, new Function<SwiftObject, String>() { + public String apply(SwiftObject input) { + return containerName + "/" + input.name(); + } + }); if (!pathsToDelete.isEmpty()) { BulkDeleteResponse response = api.bulkApiInRegion(regionId).bulkDelete(pathsToDelete); checkState(response.errors().isEmpty(), "Errors deleting paths %s: %s", pathsToDelete, response);
