Java tutorial
/** * * Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.com> * * ==================================================================== * 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.azure.storage.blob; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertTrue; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.lang.reflect.UndeclaredThrowableException; import java.net.URI; import java.net.URL; import java.security.SecureRandom; import java.util.SortedSet; import org.apache.commons.io.IOUtils; import org.jclouds.azure.storage.AzureStorageResponseException; import org.jclouds.azure.storage.blob.domain.AzureBlob; import org.jclouds.azure.storage.blob.domain.BlobProperties; import org.jclouds.azure.storage.blob.domain.ListBlobsResponse; import org.jclouds.azure.storage.blob.domain.ListableContainerProperties; import org.jclouds.azure.storage.blob.options.CreateContainerOptions; import org.jclouds.azure.storage.domain.BoundedSortedSet; import org.jclouds.azure.storage.options.ListOptions; import org.jclouds.http.HttpResponseException; import org.jclouds.http.HttpUtils; import org.jclouds.http.options.GetOptions; 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; /** * Tests behavior of {@code AzureBlobClient} * * @author Adrian Cole */ @Test(groups = "live", sequential = true, testName = "azureblob.AzureBlobClientLiveTest") public class AzureBlobClientLiveTest { protected AzureBlobClient connection; private String containerPrefix = System.getProperty("user.name") + "-azureblob"; @BeforeGroups(groups = { "live" }) public void setupClient() { account = System.getProperty("jclouds.test.user"); String key = System.getProperty("jclouds.test.key"); connection = AzureBlobContextFactory.createContext(account, key, new Log4JLoggingModule()).getApi(); } @Test public void testListContainers() throws Exception { SortedSet<ListableContainerProperties> response = connection.listContainers(); assert null != response; long initialContainerCount = response.size(); assertTrue(initialContainerCount >= 0); } String privateContainer; String publicContainer; String account; @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(); if (htpe.getResponse().getStatusCode() == 409) continue; throw e; } } SortedSet<ListableContainerProperties> response = connection.listContainers(); assert null != response; long containerCount = response.size(); assertTrue(containerCount >= 1); ListBlobsResponse list = connection.listBlobs(privateContainer); assertEquals(list.getUrl(), URI.create(String.format("https://%s.blob.core.windows.net/%s", account, privateContainer))); // 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(); if (htpe.getResponse().getStatusCode() == 409) continue; throw e; } } URL url = new URL(String.format("http://%s.blob.core.windows.net/%s", account, publicContainer)); Utils.toStringAndClose(url.openStream()); } @Test(timeOut = 5 * 60 * 1000) public void testCreatePublicRootContainer() throws Exception { try { connection.deleteRootContainer(); } catch (AzureStorageResponseException e) { sleepIfWaitingForDeleteToFinish(e); } boolean created = false; while (!created) { try { created = connection.createRootContainer(); } catch (AzureStorageResponseException htpe) { if (htpe.getResponse().getStatusCode() == 409) {// TODO look for specific message Thread.sleep(5000); continue; } else { throw htpe; } } } ListBlobsResponse list = connection.listBlobs(); assertEquals(list.getUrl(), URI.create(String.format("https://%s.blob.core.windows.net/%%24root", account))); } private void sleepIfWaitingForDeleteToFinish(AzureStorageResponseException e) throws InterruptedException { if (e.getResponse().getStatusCode() == 409) { Thread.sleep(5000); } } @Test public void testListContainersWithOptions() throws Exception { BoundedSortedSet<ListableContainerProperties> response = connection .listContainers(ListOptions.Builder.prefix(privateContainer).maxResults(1)); assert null != response; long initialContainerCount = response.size(); assertTrue(initialContainerCount >= 0); assertEquals(privateContainer, response.getPrefix()); assertEquals(1, response.getMaxResults()); } @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 } @Test(timeOut = 5 * 60 * 1000, dependsOnMethods = { "testCreateContainer", "testCreatePublicContainer" }) public void testListOwnedContainers() throws Exception { // Test default listing SortedSet<ListableContainerProperties> response = connection.listContainers(); // assertEquals(response.size(), initialContainerCount + 2);// if the containers already // exist, this will fail // Test listing with options response = connection.listContainers(ListOptions.Builder .prefix(privateContainer.substring(0, privateContainer.length() - 1)).maxResults(1)); assertEquals(response.size(), 1); assertEquals(response.first().getName(), privateContainer); response = connection.listContainers(ListOptions.Builder.prefix(publicContainer).maxResults(1)); assertEquals(response.size(), 1); assertEquals(response.first().getName(), publicContainer); } @Test public void testDeleteOneContainer() throws Exception { connection.deleteContainer("does-not-exist"); } @Test(timeOut = 5 * 60 * 1000, dependsOnMethods = { "testListOwnedContainers", "testObjectOperations" }) public void testDeleteContainer() throws Exception { connection.deleteContainer(privateContainer); connection.deleteContainer(publicContainer); // TODO loop for up to 30 seconds checking if they are really gone } @Test(timeOut = 5 * 60 * 1000, dependsOnMethods = { "testCreateContainer", "testCreatePublicContainer" }) public void testObjectOperations() throws Exception { String data = "Here is my data"; // Test PUT with string data, ETag hash, and a piece of metadata AzureBlob object = connection.newBlob(); object.getProperties().setName("object"); object.setData(data); object.setContentLength(data.length()); object.generateMD5(); object.getProperties().setContentType("text/plain"); object.getProperties().getMetadata().put("Metadata", "metadata-value"); byte[] md5 = object.getProperties().getContentMD5(); String newEtag = connection.putBlob(privateContainer, object); assertEquals(HttpUtils.toHexString(md5), HttpUtils.toHexString(object.getProperties().getContentMD5())); // Test HEAD of missing object try { connection.getBlobProperties(privateContainer, "non-existent-object"); assert false; } catch (Exception e) { e.printStackTrace(); } // Test HEAD of object BlobProperties metadata = connection.getBlobProperties(privateContainer, object.getProperties().getName()); // TODO assertEquals(metadata.getName(), object.getProperties().getName()); // we can't check this while hacking around lack of content-md5, as GET of the first byte will // show incorrect length 1, the returned size, as opposed to the real length. This is an ok // tradeoff, as a container list will contain the correct size of the objects in an // inexpensive fashion // http://code.google.com/p/jclouds/issues/detail?id=92 // assertEquals(metadata.getSize(), data.length()); assertEquals(metadata.getContentType(), "text/plain"); // Azure doesn't return the Content-MD5 on head request... assertEquals(HttpUtils.toHexString(md5), HttpUtils.toHexString(object.getProperties().getContentMD5())); assertEquals(metadata.getETag(), newEtag); assertEquals(metadata.getMetadata().entrySet().size(), 1); assertEquals(metadata.getMetadata().get("metadata"), "metadata-value"); // // Test POST to update object's metadata // Multimap<String, String> userMetadata = LinkedHashMultimap.create(); // userMetadata.put("New-Metadata-1", "value-1"); // userMetadata.put("New-Metadata-2", "value-2"); // assertTrue(connection.setBlobProperties(privateContainer, object.getProperties().getName(), // userMetadata)); // Test GET of missing object try { connection.getBlob(privateContainer, "non-existent-object"); assert false; } catch (Exception e) { e.printStackTrace(); } // Test GET of object (including updated metadata) AzureBlob getBlob = connection.getBlob(privateContainer, object.getProperties().getName()); assertEquals(IOUtils.toString((InputStream) getBlob.getData()), data); // TODO assertEquals(getBlob.getName(), object.getProperties().getName()); assertEquals(getBlob.getContentLength(), new Long(data.length())); assertEquals(getBlob.getProperties().getContentType(), "text/plain"); assertEquals(HttpUtils.toHexString(md5), HttpUtils.toHexString(getBlob.getProperties().getContentMD5())); assertEquals(newEtag, getBlob.getProperties().getETag()); // wait until we can update metadata // assertEquals(getBlob.getProperties().getMetadata().entries().size(), 2); // assertEquals( // Iterables.getLast(getBlob.getProperties().getMetadata().get("New-Metadata-1")), // "value-1"); // assertEquals( // Iterables.getLast(getBlob.getProperties().getMetadata().get("New-Metadata-2")), // "value-2"); assertEquals(metadata.getMetadata().entrySet().size(), 1); assertEquals(metadata.getMetadata().get("metadata"), "metadata-value"); // Test PUT with invalid ETag (as if object's data was corrupted in transit) String correctEtag = newEtag; String incorrectEtag = "0" + correctEtag.substring(1); object.getProperties().setETag(incorrectEtag); try { connection.putBlob(privateContainer, object); } catch (Throwable e) { assertEquals(e.getCause().getClass(), HttpResponseException.class); assertEquals(((HttpResponseException) e.getCause()).getResponse().getStatusCode(), 422); } ByteArrayInputStream bais = new ByteArrayInputStream(data.getBytes("UTF-8")); object = connection.newBlob(); object.getProperties().setName("chunked-object"); object.setData(bais); object.setContentLength(new Long(data.getBytes().length)); newEtag = connection.putBlob(privateContainer, object); assertEquals(HttpUtils.toHexString(md5), HttpUtils.toHexString(getBlob.getProperties().getContentMD5())); // Test GET with options // Non-matching ETag try { connection.getBlob(privateContainer, object.getProperties().getName(), GetOptions.Builder.ifETagDoesntMatch(newEtag)); } catch (Exception e) { assertEquals(e.getCause().getClass(), HttpResponseException.class); assertEquals(((HttpResponseException) e.getCause()).getResponse().getStatusCode(), 304); } // Matching ETag getBlob = connection.getBlob(privateContainer, object.getProperties().getName(), GetOptions.Builder.ifETagMatches(newEtag)); assertEquals(getBlob.getProperties().getETag(), newEtag); // Range // doesn't work per // http://social.msdn.microsoft.com/Forums/en-US/windowsazure/thread/479fa63f-51df-4b66-96b5-33ae362747b6 // getBlob = connection // .getBlob(privateContainer, object.getProperties().getName(), // GetOptions.Builder.startAt(8)).get(120, // TimeUnit.SECONDS); // assertEquals(IOUtils.toString((InputStream) getBlob.getData()), data.substring(8)); connection.deleteBlob(privateContainer, "object"); connection.deleteBlob(privateContainer, "chunked-object"); } }