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 f5d662cfce..04c79fa566 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 @@ -26,6 +26,7 @@ package org.jclouds.azure.storage.blob; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; +import javax.ws.rs.DELETE; import javax.ws.rs.GET; import javax.ws.rs.PUT; import javax.ws.rs.Path; @@ -87,4 +88,48 @@ public interface AzureBlobConnection { @Query(key = "restype", value = "container") boolean createContainer(@PathParam("container") String container, CreateContainerOptions... options); + + /** + * The Delete Container operation marks the specified container for deletion. The container and + * any blobs contained within it are later deleted during garbage collection. + *
+ * When a container is deleted, a container with the same name cannot be created for at least 30 + * seconds; the container may not be available for more than 30 seconds if the service is still + * processing the request. While the container is being deleted, attempts to create a container + * of the same name will fail with status code 409 (Conflict), with the service returning + * additional error information indicating that the container is being deleted. All other + * operations, including operations on any blobs under the container, will fail with status code + * 404 (Not Found) while the container is being deleted. + * + */ + @DELETE + @Path("{container}") + @Query(key = "restype", value = "container") + boolean deleteContainer(@PathParam("container") String container); + + /** + * The root container is a default container that may be inferred from a URL requesting a blob + * resource. The root container makes it possible to reference a blob from the top level of the + * storage account hierarchy, without referencing the container name. + * + * 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("$root") + @Query(key = "restype", value = "container") + boolean createRootContainer(CreateContainerOptions... options);// TODO public is not supported! + + /** + * + * @see deleteContainer(String) + * @see createRootContainer(CreateContainerOptions) + */ + @DELETE + @Path("$root") + @Query(key = "restype", value = "container") + boolean deleteRootContainer(); } 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 939e1cc9b7..5e229f3267 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 @@ -7,10 +7,12 @@ import java.lang.reflect.UndeclaredThrowableException; import java.net.URL; import java.security.SecureRandom; +import org.jclouds.azure.storage.AzureStorageResponseException; 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.http.HttpResponseException; import org.jclouds.logging.log4j.config.Log4JLoggingModule; import org.jclouds.util.Utils; import org.testng.annotations.BeforeGroups; @@ -65,8 +67,9 @@ public class AzureBlobConnectionLiveTest { 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 + HttpResponseException htpe = (HttpResponseException) e.getCause().getCause(); + if (htpe.getResponse().getStatusCode() == 409) + continue; throw e; } } @@ -86,8 +89,9 @@ public class AzureBlobConnectionLiveTest { 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 + HttpResponseException htpe = (HttpResponseException) e.getCause().getCause(); + if (htpe.getResponse().getStatusCode() == 409) + continue; throw e; } } @@ -97,6 +101,30 @@ public class AzureBlobConnectionLiveTest { Utils.toStringAndClose(url.openStream()); } + @Test(timeOut = 5 * 60 * 1000) + public void testCreatePublicRootContainer() throws Exception { + try { + connection.deleteRootContainer(); + } catch (Exception e) { + // don't care.. we wish to recreate it. + } + boolean created = false; + while (!created) { + try { + created = connection.createRootContainer(); + } catch (UndeclaredThrowableException e) { + AzureStorageResponseException htpe = (AzureStorageResponseException) e.getCause() + .getCause(); + if (htpe.getError().getCode().equals("ContainerBeingDeleted")) { + Thread.sleep(5000); + continue; + } + throw e; + } + } + // TODO check if it really exists. + } + @Test public void testListContainersWithOptions() throws Exception { @@ -107,6 +135,19 @@ public class AzureBlobConnectionLiveTest { assertTrue(initialContainerCount >= 0); assertEquals(privateContainer, response.getPrefix()); assertEquals(1, response.getMaxResults()); + } + @Test(timeOut = 5 * 60 * 1000, dependsOnMethods = { "testCreateContainer", + "testCreatePublicContainer" }) + public void testDeleteContainer() throws Exception { + assert connection.deleteContainer(privateContainer); + assert connection.deleteContainer(publicContainer); + // TODO loop for up to 30 seconds checking if they are really gone + } + + @Test(timeOut = 5 * 60 * 1000, dependsOnMethods = { "testCreatePublicRootContainer" }) + public void testDeleteRootContainer() throws Exception { + assert connection.deleteRootContainer(); + // TODO loop for up to 30 seconds checking if they are really gone } } 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 6669189f5a..546a563ee8 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 @@ -82,7 +82,7 @@ public class AzureBlobConnectionTest { assertEquals(processor.createExceptionParserOrNullIfNotFound(method), null); } - public void testCreateContainers() throws SecurityException, NoSuchMethodException { + public void testCreateContainer() throws SecurityException, NoSuchMethodException { Method method = AzureBlobConnection.class.getMethod("createContainer", String.class, createContainerOptionsVarargsClass); URI endpoint = URI.create("http://localhost"); @@ -101,7 +101,24 @@ public class AzureBlobConnectionTest { assertEquals(processor.createExceptionParserOrNullIfNotFound(method), null); } - public void testCreateContainersOptions() throws SecurityException, NoSuchMethodException { + public void testDeleteContainer() throws SecurityException, NoSuchMethodException { + Method method = AzureBlobConnection.class.getMethod("deleteContainer", String.class); + 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.DELETE); + assertEquals(httpMethod.getHeaders().size(), 1); + assertEquals(httpMethod.getHeaders().get("x-ms-version"), Collections + .singletonList("2009-07-17")); + assertEquals(processor.createResponseParser(method).getClass(), ReturnTrueIf2xx.class); + // TODO check generic type of response parser + assertEquals(processor.createExceptionParserOrNullIfNotFound(method), null); + } + + public void testCreateContainerOptions() throws SecurityException, NoSuchMethodException { Method method = AzureBlobConnection.class.getMethod("createContainer", String.class, createContainerOptionsVarargsClass); URI endpoint = URI.create("http://localhost"); @@ -123,6 +140,62 @@ public class AzureBlobConnectionTest { assertEquals(processor.createExceptionParserOrNullIfNotFound(method), null); } + public void testCreateRootContainer() throws SecurityException, NoSuchMethodException { + Method method = AzureBlobConnection.class.getMethod("createRootContainer", + createContainerOptionsVarargsClass); + URI endpoint = URI.create("http://localhost"); + HttpRequest httpMethod = processor.createRequest(endpoint, method, new Object[] {}); + assertEquals(httpMethod.getEndpoint().getHost(), "localhost"); + assertEquals(httpMethod.getEndpoint().getPath(), "/$root"); + 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 testDeleteRootContainer() throws SecurityException, NoSuchMethodException { + Method method = AzureBlobConnection.class.getMethod("deleteRootContainer"); + URI endpoint = URI.create("http://localhost"); + HttpRequest httpMethod = processor.createRequest(endpoint, method, new Object[] {}); + assertEquals(httpMethod.getEndpoint().getHost(), "localhost"); + assertEquals(httpMethod.getEndpoint().getPath(), "/$root"); + assertEquals(httpMethod.getEndpoint().getQuery(), "restype=container"); + assertEquals(httpMethod.getMethod(), HttpMethod.DELETE); + assertEquals(httpMethod.getHeaders().size(), 1); + assertEquals(httpMethod.getHeaders().get("x-ms-version"), Collections + .singletonList("2009-07-17")); + assertEquals(processor.createResponseParser(method).getClass(), ReturnTrueIf2xx.class); + // TODO check generic type of response parser + assertEquals(processor.createExceptionParserOrNullIfNotFound(method), null); + } + + public void testCreateRootContainerOptions() throws SecurityException, NoSuchMethodException { + Method method = AzureBlobConnection.class.getMethod("createRootContainer", + createContainerOptionsVarargsClass); + URI endpoint = URI.create("http://localhost"); + HttpRequest httpMethod = processor.createRequest(endpoint, method, + new Object[] { withPublicAcl().withMetadata(ImmutableMultimap.of("foo", "bar")) }); + assertEquals(httpMethod.getEndpoint().getHost(), "localhost"); + assertEquals(httpMethod.getEndpoint().getPath(), "/$root"); + 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