diff --git a/azure/storage/blob/core/src/main/java/org/jclouds/azure/storage/blob/AzureBlobConnection.java b/azure/storage/blob/core/src/main/java/org/jclouds/azure/storage/blob/AzureBlobConnection.java index ea11d7be46..f5d662cfce 100644 --- a/azure/storage/blob/core/src/main/java/org/jclouds/azure/storage/blob/AzureBlobConnection.java +++ b/azure/storage/blob/core/src/main/java/org/jclouds/azure/storage/blob/AzureBlobConnection.java @@ -27,9 +27,12 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import javax.ws.rs.GET; +import javax.ws.rs.PUT; import javax.ws.rs.Path; +import javax.ws.rs.PathParam; import org.jclouds.azure.storage.blob.domain.ContainerMetadataList; +import org.jclouds.azure.storage.blob.options.CreateContainerOptions; import org.jclouds.azure.storage.blob.xml.AccountNameEnumerationResultsHandler; import org.jclouds.azure.storage.filters.SharedKeyAuthentication; import org.jclouds.azure.storage.options.ListOptions; @@ -56,6 +59,8 @@ public interface AzureBlobConnection { /** * The List Containers operation returns a list of the containers under the specified account. + *
+ * The 2009-07-17 version of the List Containers operation times out after 30 seconds. * * @param listOptions * controls the number or type of results requested @@ -67,4 +72,19 @@ public interface AzureBlobConnection { @Query(key = "comp", value = "list") ContainerMetadataList listContainers(ListOptions... listOptions); + /** + * The Create Container operation creates a new container under the specified account. If the + * container with the same name already exists, the operation fails. + * + * The container resource includes metadata and properties for that container. It does not + * include a list of the blobs contained by the container. + * + * @see CreateContainerOptions + * + */ + @PUT + @Path("{container}") + @Query(key = "restype", value = "container") + boolean createContainer(@PathParam("container") String container, + CreateContainerOptions... options); } diff --git a/azure/storage/blob/core/src/main/java/org/jclouds/azure/storage/blob/domain/ContainerMetadataList.java b/azure/storage/blob/core/src/main/java/org/jclouds/azure/storage/blob/domain/ContainerMetadataList.java index 673a21f459..3fd5292ede 100644 --- a/azure/storage/blob/core/src/main/java/org/jclouds/azure/storage/blob/domain/ContainerMetadataList.java +++ b/azure/storage/blob/core/src/main/java/org/jclouds/azure/storage/blob/domain/ContainerMetadataList.java @@ -31,36 +31,37 @@ import java.util.List; * */ public class ContainerMetadataList { + private String prefix; + private String marker; private int maxResults; private List
+ * import static org.jclouds.azure.storage.blob.options.PutBucketOptions.Builder.*
+ * import org.jclouds.azure.storage.blob.AzureBlobConnection;
+ *
+ * AzureBlobConnection connection = // get connection
+ * boolean createdWithPublicAcl = connection.createContainer("containerName", withPublicAcl());
+ * *
+ *
+ * @see
+ * @author Adrian Cole
+ */
+public class CreateContainerOptions extends BaseHttpRequestOptions {
+ public static final CreateContainerOptions NONE = new CreateContainerOptions();
+
+ /**
+ * Indicates whether a container may be accessed publicly
+ */
+ public CreateContainerOptions withPublicAcl() {
+ this.headers.put("x-ms-prop-publicaccess", "true");
+ return this;
+ }
+
+ /**
+ * A name-value pair to associate with the container as metadata.
+ *
+ * Note that these are stored at the server under the prefix: x-ms-meta-
+ */
+ public CreateContainerOptions withMetadata(Multimap metadata) {
+ for (Entry entry : metadata.entries()) {
+ if (entry.getKey().startsWith(AzureStorageHeaders.USER_METADATA_PREFIX))
+ headers.put(entry.getKey(), entry.getValue());
+ else
+ headers
+ .put(AzureStorageHeaders.USER_METADATA_PREFIX + entry.getKey(), entry
+ .getValue());
+ }
+ return this;
+ }
+
+ public static class Builder {
+
+ /**
+ * @see CreateContainerOptions#withPublicAcl()
+ */
+ public static CreateContainerOptions withPublicAcl() {
+ CreateContainerOptions options = new CreateContainerOptions();
+ return options.withPublicAcl();
+ }
+
+ /**
+ * @see CreateContainerOptions#withMetadata(Multimap)
+ */
+ public static CreateContainerOptions withMetadata(Multimap metadata) {
+ CreateContainerOptions options = new CreateContainerOptions();
+ return options.withMetadata(metadata);
+ }
+
+ }
+}
diff --git a/azure/storage/blob/core/src/main/java/org/jclouds/azure/storage/blob/xml/AccountNameEnumerationResultsHandler.java b/azure/storage/blob/core/src/main/java/org/jclouds/azure/storage/blob/xml/AccountNameEnumerationResultsHandler.java
index 562ba25bc7..cab4a3db58 100755
--- a/azure/storage/blob/core/src/main/java/org/jclouds/azure/storage/blob/xml/AccountNameEnumerationResultsHandler.java
+++ b/azure/storage/blob/core/src/main/java/org/jclouds/azure/storage/blob/xml/AccountNameEnumerationResultsHandler.java
@@ -47,7 +47,9 @@ import com.google.inject.Inject;
public class AccountNameEnumerationResultsHandler extends
ParseSax.HandlerWithResult {
- private List containers = new ArrayList();
+ private List containerMetadata = new ArrayList();
+ private String prefix;
+ private String marker;
private int maxResults;
private String nextMarker;
private URI currentUrl;
@@ -64,16 +66,20 @@ public class AccountNameEnumerationResultsHandler extends
}
public ContainerMetadataList getResult() {
- return new ContainerMetadataList(maxResults, containers, nextMarker);
+ return new ContainerMetadataList(prefix, marker, maxResults, containerMetadata, nextMarker);
}
public void endElement(String uri, String name, String qName) {
if (qName.equals("MaxResults")) {
maxResults = Integer.parseInt(currentText.toString().trim());
+ } else if (qName.equals("Marker")) {
+ marker = currentText.toString().trim();
+ } else if (qName.equals("Prefix")) {
+ prefix = currentText.toString().trim();
} else if (qName.equals("NextMarker")) {
nextMarker = currentText.toString().trim();
} else if (qName.equals("Container")) {
- containers.add(new ContainerMetadata(currentUrl, currentLastModified, currentETag));
+ containerMetadata.add(new ContainerMetadata(currentUrl, currentLastModified, currentETag));
currentUrl = null;
currentLastModified = null;
currentETag = null;
diff --git a/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/AzureBlobConnectionLiveTest.java b/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/AzureBlobConnectionLiveTest.java
index 8c9f06e2a2..939e1cc9b7 100644
--- a/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/AzureBlobConnectionLiveTest.java
+++ b/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/AzureBlobConnectionLiveTest.java
@@ -1,13 +1,22 @@
package org.jclouds.azure.storage.blob;
+import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
+import java.lang.reflect.UndeclaredThrowableException;
+import java.net.URL;
+import java.security.SecureRandom;
+
import org.jclouds.azure.storage.blob.domain.ContainerMetadataList;
+import org.jclouds.azure.storage.blob.options.CreateContainerOptions;
+import org.jclouds.azure.storage.options.ListOptions;
import org.jclouds.azure.storage.reference.AzureStorageConstants;
import org.jclouds.logging.log4j.config.Log4JLoggingModule;
+import org.jclouds.util.Utils;
import org.testng.annotations.BeforeGroups;
import org.testng.annotations.Test;
+import com.google.common.collect.ImmutableMultimap;
import com.google.inject.Injector;
/**
@@ -24,6 +33,8 @@ public class AzureBlobConnectionLiveTest {
.getProperty(AzureStorageConstants.PROPERTY_AZURESTORAGE_KEY);
protected AzureBlobConnection connection;
+ private String containerPrefix = System.getProperty("user.name") + "-azureblob";
+
@BeforeGroups(groups = { "live" })
public void setupConnection() {
Injector injector = AzureBlobContextBuilder.newBuilder(sysAzureStorageAccount,
@@ -42,4 +53,60 @@ public class AzureBlobConnectionLiveTest {
}
+ String privateContainer;
+ String publicContainer;
+
+ @Test(timeOut = 5 * 60 * 1000)
+ public void testCreateContainer() throws Exception {
+ boolean created = false;
+ while (!created) {
+ privateContainer = containerPrefix + new SecureRandom().nextInt();
+ try {
+ created = connection.createContainer(privateContainer, CreateContainerOptions.Builder
+ .withMetadata(ImmutableMultimap.of("foo", "bar")));
+ } catch (UndeclaredThrowableException e) {
+ // HttpResponseException htpe = (HttpResponseException) e.getCause().getCause();
+ // TODO: check if already created and see what the error is, and continue
+ throw e;
+ }
+ }
+ ContainerMetadataList response = connection.listContainers();
+ assert null != response;
+ long containerCount = response.getContainerMetadata().size();
+ assertTrue(containerCount >= 1);
+ // TODO ... check to see the container actually exists
+ }
+
+ @Test(timeOut = 5 * 60 * 1000)
+ public void testCreatePublicContainer() throws Exception {
+ boolean created = false;
+ while (!created) {
+ publicContainer = containerPrefix + new SecureRandom().nextInt();
+ try {
+ created = connection.createContainer(publicContainer, CreateContainerOptions.Builder
+ .withPublicAcl());
+ } catch (UndeclaredThrowableException e) {
+ // HttpResponseException htpe = (HttpResponseException) e.getCause().getCause();
+ // TODO: check if already created and see what the error is, and continue
+ throw e;
+ }
+ }
+
+ URL url = new URL(String.format("http://%s.blob.core.windows.net/%s", sysAzureStorageAccount,
+ publicContainer));
+ Utils.toStringAndClose(url.openStream());
+ }
+
+ @Test
+ public void testListContainersWithOptions() throws Exception {
+
+ ContainerMetadataList response = connection.listContainers(ListOptions.Builder.prefix(
+ privateContainer).maxResults(1));
+ assert null != response;
+ long initialContainerCount = response.getContainerMetadata().size();
+ assertTrue(initialContainerCount >= 0);
+ assertEquals(privateContainer, response.getPrefix());
+ assertEquals(1, response.getMaxResults());
+
+ }
}
diff --git a/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/AzureBlobConnectionTest.java b/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/AzureBlobConnectionTest.java
index fd0e8e120c..6669189f5a 100644
--- a/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/AzureBlobConnectionTest.java
+++ b/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/AzureBlobConnectionTest.java
@@ -1,5 +1,6 @@
package org.jclouds.azure.storage.blob;
+import static org.jclouds.azure.storage.blob.options.CreateContainerOptions.Builder.withPublicAcl;
import static org.jclouds.azure.storage.options.ListOptions.Builder.maxResults;
import static org.testng.Assert.assertEquals;
@@ -9,6 +10,7 @@ import java.util.Collections;
import javax.ws.rs.HttpMethod;
+import org.jclouds.azure.storage.blob.options.CreateContainerOptions;
import org.jclouds.azure.storage.blob.xml.config.AzureBlobParserModule;
import org.jclouds.azure.storage.options.ListOptions;
import org.jclouds.azure.storage.reference.AzureStorageConstants;
@@ -18,11 +20,13 @@ import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpUtils;
import org.jclouds.http.config.JavaUrlHttpCommandExecutorServiceModule;
import org.jclouds.http.functions.ParseSax;
+import org.jclouds.http.functions.ReturnTrueIf2xx;
import org.jclouds.rest.JaxrsAnnotationProcessor;
import org.jclouds.rest.config.JaxrsModule;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
+import com.google.common.collect.ImmutableMultimap;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.name.Names;
@@ -39,8 +43,10 @@ public class AzureBlobConnectionTest {
private static final Class extends ListOptions[]> listOptionsVarargsClass = new ListOptions[] {}
.getClass();
+ private static final Class extends CreateContainerOptions[]> createContainerOptionsVarargsClass = new CreateContainerOptions[] {}
+ .getClass();
- public void testListServers() throws SecurityException, NoSuchMethodException {
+ public void testListContainers() throws SecurityException, NoSuchMethodException {
Method method = AzureBlobConnection.class
.getMethod("listContainers", listOptionsVarargsClass);
URI endpoint = URI.create("http://localhost");
@@ -57,7 +63,7 @@ public class AzureBlobConnectionTest {
assertEquals(processor.createExceptionParserOrNullIfNotFound(method), null);
}
- public void testListServersOptions() throws SecurityException, NoSuchMethodException {
+ public void testListContainersOptions() throws SecurityException, NoSuchMethodException {
Method method = AzureBlobConnection.class
.getMethod("listContainers", listOptionsVarargsClass);
URI endpoint = URI.create("http://localhost");
@@ -76,6 +82,47 @@ public class AzureBlobConnectionTest {
assertEquals(processor.createExceptionParserOrNullIfNotFound(method), null);
}
+ public void testCreateContainers() throws SecurityException, NoSuchMethodException {
+ Method method = AzureBlobConnection.class.getMethod("createContainer", String.class,
+ createContainerOptionsVarargsClass);
+ URI endpoint = URI.create("http://localhost");
+ HttpRequest httpMethod = processor.createRequest(endpoint, method,
+ new Object[] { "container" });
+ assertEquals(httpMethod.getEndpoint().getHost(), "localhost");
+ assertEquals(httpMethod.getEndpoint().getPath(), "/container");
+ assertEquals(httpMethod.getEndpoint().getQuery(), "restype=container");
+ assertEquals(httpMethod.getMethod(), HttpMethod.PUT);
+ assertEquals(httpMethod.getHeaders().size(), 2);
+ assertEquals(httpMethod.getHeaders().get("x-ms-version"), Collections
+ .singletonList("2009-07-17"));
+ assertEquals(httpMethod.getHeaders().get("Content-Length"), Collections.singletonList("0"));
+ assertEquals(processor.createResponseParser(method).getClass(), ReturnTrueIf2xx.class);
+ // TODO check generic type of response parser
+ assertEquals(processor.createExceptionParserOrNullIfNotFound(method), null);
+ }
+
+ public void testCreateContainersOptions() throws SecurityException, NoSuchMethodException {
+ Method method = AzureBlobConnection.class.getMethod("createContainer", String.class,
+ createContainerOptionsVarargsClass);
+ URI endpoint = URI.create("http://localhost");
+ HttpRequest httpMethod = processor.createRequest(endpoint, method, new Object[] {
+ "container", withPublicAcl().withMetadata(ImmutableMultimap.of("foo", "bar")) });
+ assertEquals(httpMethod.getEndpoint().getHost(), "localhost");
+ assertEquals(httpMethod.getEndpoint().getPath(), "/container");
+ assertEquals(httpMethod.getEndpoint().getQuery(), "restype=container");
+ assertEquals(httpMethod.getMethod(), HttpMethod.PUT);
+ assertEquals(httpMethod.getHeaders().size(), 4);
+ assertEquals(httpMethod.getHeaders().get("x-ms-version"), Collections
+ .singletonList("2009-07-17"));
+ assertEquals(httpMethod.getHeaders().get("x-ms-meta-foo"), Collections.singletonList("bar"));
+ assertEquals(httpMethod.getHeaders().get("x-ms-prop-publicaccess"), Collections
+ .singletonList("true"));
+ assertEquals(httpMethod.getHeaders().get("Content-Length"), Collections.singletonList("0"));
+ assertEquals(processor.createResponseParser(method).getClass(), ReturnTrueIf2xx.class);
+ // TODO check generic type of response parser
+ assertEquals(processor.createExceptionParserOrNullIfNotFound(method), null);
+ }
+
JaxrsAnnotationProcessor processor;
@BeforeClass
diff --git a/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/options/CreateContainerOptionsTest.java b/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/options/CreateContainerOptionsTest.java
new file mode 100644
index 0000000000..8ecf05a6af
--- /dev/null
+++ b/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/options/CreateContainerOptionsTest.java
@@ -0,0 +1,52 @@
+package org.jclouds.azure.storage.blob.options;
+
+import static org.testng.Assert.assertEquals;
+
+import org.jclouds.azure.storage.reference.AzureStorageHeaders;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMultimap;
+
+/**
+ * Tests behavior of {@code CreateContainerOptions}
+ *
+ * @author Adrian Cole
+ */
+@Test(groups = "unit", testName = "azurestorage.CreateContainerOptionsTest")
+public class CreateContainerOptionsTest {
+
+ public void testPublicAcl() {
+ CreateContainerOptions options = new CreateContainerOptions().withPublicAcl();
+ assertEquals(ImmutableList.of("true"), options.buildRequestHeaders().get(
+ "x-ms-prop-publicaccess"));
+ }
+
+ public void testPublicAclStatic() {
+ CreateContainerOptions options = CreateContainerOptions.Builder.withPublicAcl();
+ assertEquals(ImmutableList.of("true"), options.buildRequestHeaders().get(
+ "x-ms-prop-publicaccess"));
+ }
+
+ public void testMetadata() {
+ CreateContainerOptions options = new CreateContainerOptions().withMetadata(ImmutableMultimap
+ .of("test", "foo"));
+ assertEquals(ImmutableList.of("foo"), options.buildRequestHeaders().get(
+ AzureStorageHeaders.USER_METADATA_PREFIX + "test"));
+ }
+
+ public void testMetadataAlreadyPrefixed() {
+ CreateContainerOptions options = new CreateContainerOptions().withMetadata(ImmutableMultimap
+ .of(AzureStorageHeaders.USER_METADATA_PREFIX + "test", "foo"));
+ assertEquals(ImmutableList.of("foo"), options.buildRequestHeaders().get(
+ AzureStorageHeaders.USER_METADATA_PREFIX + "test"));
+ }
+
+ public void testMetadataStatic() {
+ CreateContainerOptions options = CreateContainerOptions.Builder
+ .withMetadata(ImmutableMultimap.of("test", "foo"));
+ assertEquals(ImmutableList.of("foo"), options.buildRequestHeaders().get(
+ AzureStorageHeaders.USER_METADATA_PREFIX + "test"));
+ }
+
+}
diff --git a/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/xml/AccountNameEnumerationResultsHandlerTest.java b/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/xml/AccountNameEnumerationResultsHandlerTest.java
index 1773754eda..f2fd18e3c9 100644
--- a/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/xml/AccountNameEnumerationResultsHandlerTest.java
+++ b/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/xml/AccountNameEnumerationResultsHandlerTest.java
@@ -46,7 +46,7 @@ public class AccountNameEnumerationResultsHandlerTest extends BaseHandlerTest {
public void testApplyInputStream() {
InputStream is = getClass().getResourceAsStream("/test_list_containers.xml");
- ContainerMetadataList list = new ContainerMetadataList(3, ImmutableList.of(
+ ContainerMetadataList list = new ContainerMetadataList(null, null, 3, ImmutableList.of(
new ContainerMetadata(URI.create("http://myaccount.blob.core.windows.net/audio"),
dateService.rfc822DateParse("Wed, 13 Aug 2008 20:39:39 GMT"), HttpUtils
.fromHexString("0x8CACB9BD7C6B1B2")), new ContainerMetadata(URI
@@ -62,4 +62,24 @@ public class AccountNameEnumerationResultsHandlerTest extends BaseHandlerTest {
ContainerMetadataList result = parser.parse(is);
assertEquals(result, list);
}
+
+ public void testApplyInputStreamWithOptions() {
+ InputStream is = getClass().getResourceAsStream("/test_list_containers_options.xml");
+ ContainerMetadataList list = new ContainerMetadataList("prefix", "marker", 1, ImmutableList
+ .of(new ContainerMetadata(
+ URI.create("http://myaccount.blob.core.windows.net/audio"), dateService
+ .rfc822DateParse("Wed, 13 Aug 2008 20:39:39 GMT"), HttpUtils
+ .fromHexString("0x8CACB9BD7C6B1B2")), new ContainerMetadata(URI
+ .create("http://myaccount.blob.core.windows.net/images"), dateService
+ .rfc822DateParse("Wed, 14 Aug 2008 20:39:39 GMT"), HttpUtils
+ .fromHexString("0x8CACB9BD7C1EEEC")), new ContainerMetadata(URI
+ .create("http://myaccount.blob.core.windows.net/textfiles"), dateService
+ .rfc822DateParse("Wed, 15 Aug 2008 20:39:39 GMT"), HttpUtils
+ .fromHexString("0x8CACB9BD7BACAC3"))
+
+ ), "video");
+ ParseSax parser = parserFactory.createContainerMetadataListParser();
+ ContainerMetadataList result = parser.parse(is);
+ assertEquals(result, list);
+ }
}
diff --git a/azure/storage/blob/core/src/test/resources/test_list_containers_options.xml b/azure/storage/blob/core/src/test/resources/test_list_containers_options.xml
new file mode 100644
index 0000000000..115b6a85da
--- /dev/null
+++ b/azure/storage/blob/core/src/test/resources/test_list_containers_options.xml
@@ -0,0 +1,24 @@
+
+
+ prefix
+ marker
+ 1
+
+
+ http://myaccount.blob.core.windows.net/audio
+ Wed, 13 Aug 2008 20:39:39 GMT
+ 0x8CACB9BD7C6B1B2
+
+
+ http://myaccount.blob.core.windows.net/images
+ Wed, 14 Aug 2008 20:39:39 GMT
+ 0x8CACB9BD7C1EEEC
+
+
+ http://myaccount.blob.core.windows.net/textfiles
+ Wed, 15 Aug 2008 20:39:39 GMT
+ 0x8CACB9BD7BACAC3
+
+
+ video
+
\ No newline at end of file