Issue 264: fixed blob stuff in rackspace

This commit is contained in:
Adrian Cole 2010-05-24 12:29:39 -07:00
parent 237afa4ddc
commit 0684dc2344
7 changed files with 118 additions and 16 deletions

View File

@ -28,6 +28,7 @@ import java.util.regex.Pattern;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
import org.jclouds.blobstore.AsyncBlobStore;
import org.jclouds.blobstore.BlobStore; import org.jclouds.blobstore.BlobStore;
import org.jclouds.blobstore.domain.Blob; import org.jclouds.blobstore.domain.Blob;
import org.jclouds.blobstore.domain.BlobMetadata; import org.jclouds.blobstore.domain.BlobMetadata;
@ -135,6 +136,10 @@ public class BlobStoreUtilsImpl implements BlobStoreUtils {
return "".equals(prefix) ? null : prefix; return "".equals(prefix) ? null : prefix;
} }
public static String parseDirectoryFromPath(String path) {
return path.substring(0, path.lastIndexOf('/'));
}
private static Pattern keyFromContainer = Pattern.compile("/?[^/]+/(.*)"); private static Pattern keyFromContainer = Pattern.compile("/?[^/]+/(.*)");
public static String getKeyFor(GeneratedHttpRequest<?> request, HttpResponse from) { public static String getKeyFor(GeneratedHttpRequest<?> request, HttpResponse from) {
@ -168,4 +173,12 @@ public class BlobStoreUtilsImpl implements BlobStoreUtils {
throw new IllegalArgumentException("Object type not supported: " + o.getClass().getName()); throw new IllegalArgumentException("Object type not supported: " + o.getClass().getName());
} }
} }
public static void createParentIfNeededAsync(AsyncBlobStore asyncBlobStore, String container,
Blob blob) {
String name = blob.getMetadata().getName();
if (name.indexOf('/') > 0) {
asyncBlobStore.createDirectory(container, parseDirectoryFromPath(name));
}
}
} }

View File

@ -36,7 +36,6 @@ import java.io.InputStream;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.util.Date; import java.util.Date;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.zip.GZIPInputStream; import java.util.zip.GZIPInputStream;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
@ -46,6 +45,7 @@ import org.jclouds.blobstore.domain.Blob;
import org.jclouds.blobstore.domain.BlobMetadata; import org.jclouds.blobstore.domain.BlobMetadata;
import org.jclouds.blobstore.domain.PageSet; import org.jclouds.blobstore.domain.PageSet;
import org.jclouds.blobstore.domain.StorageMetadata; import org.jclouds.blobstore.domain.StorageMetadata;
import org.jclouds.blobstore.domain.StorageType;
import org.jclouds.blobstore.util.internal.BlobStoreUtilsImpl; import org.jclouds.blobstore.util.internal.BlobStoreUtilsImpl;
import org.jclouds.encryption.EncryptionService; import org.jclouds.encryption.EncryptionService;
import org.jclouds.encryption.EncryptionService.MD5InputStreamResult; import org.jclouds.encryption.EncryptionService.MD5InputStreamResult;
@ -61,6 +61,8 @@ import org.testng.annotations.DataProvider;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.google.common.io.ByteStreams; import com.google.common.io.ByteStreams;
import com.google.common.io.InputSupplier; import com.google.common.io.InputSupplier;
@ -371,10 +373,18 @@ public class BaseBlobIntegrationTest extends BaseBlobStoreIntegrationTest {
} }
private void assertContainerEmptyDeleting(String containerName, String key) { private void assertContainerEmptyDeleting(String containerName, String key) {
Set<? extends StorageMetadata> listing = context.getBlobStore().list(containerName); Iterable<? extends StorageMetadata> listing = Iterables.filter(context.getBlobStore().list(
assertEquals(listing.size(), 0, String.format( containerName), new Predicate<StorageMetadata>() {
"deleting %s, we still have %s left in container %s, using encoding %s", key,
listing.size(), containerName, LOCAL_ENCODING)); @Override
public boolean apply(StorageMetadata input) {
return input.getType() == StorageType.BLOB;
}
});
assertEquals(Iterables.size(listing), 0, String.format(
"deleting %s, we still have %s blobs left in container %s, using encoding %s", key,
Iterables.size(listing), containerName, LOCAL_ENCODING));
} }
@Test(groups = { "integration", "live" }) @Test(groups = { "integration", "live" })

View File

@ -69,7 +69,6 @@ public class BaseContainerIntegrationTest extends BaseBlobStoreIntegrationTest {
} }
} }
@Test(groups = { "integration", "live" }) @Test(groups = { "integration", "live" })
public void testWithDetails() throws InterruptedException { public void testWithDetails() throws InterruptedException {
String key = "hello"; String key = "hello";
@ -202,6 +201,12 @@ public class BaseContainerIntegrationTest extends BaseBlobStoreIntegrationTest {
assert container.getNextMarker() == null; assert container.getNextMarker() == null;
assert container.size() == 1 : container; assert container.size() == 1 : container;
context.getBlobStore().createDirectory(containerName, directory + "/" + directory);
container = context.getBlobStore().list(containerName, inDirectory(directory).recursive());
assert container.getNextMarker() == null;
assert container.size() == 1 : container;
context.getBlobStore().clearContainer(containerName, inDirectory(directory).recursive()); context.getBlobStore().clearContainer(containerName, inDirectory(directory).recursive());
// should no longer have the 2 level-deep directory above // should no longer have the 2 level-deep directory above

View File

@ -21,10 +21,14 @@ package org.jclouds.blobstore.util;
import static org.easymock.EasyMock.expect; import static org.easymock.EasyMock.expect;
import static org.easymock.classextension.EasyMock.createMock; import static org.easymock.classextension.EasyMock.createMock;
import static org.easymock.classextension.EasyMock.replay; import static org.easymock.classextension.EasyMock.replay;
import static org.easymock.classextension.EasyMock.verify;
import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertEquals;
import java.net.URI; import java.net.URI;
import org.jclouds.blobstore.AsyncBlobStore;
import org.jclouds.blobstore.domain.Blob;
import org.jclouds.blobstore.domain.MutableBlobMetadata;
import org.jclouds.blobstore.util.internal.BlobStoreUtilsImpl; import org.jclouds.blobstore.util.internal.BlobStoreUtilsImpl;
import org.jclouds.http.HttpResponse; import org.jclouds.http.HttpResponse;
import org.jclouds.rest.internal.GeneratedHttpRequest; import org.jclouds.rest.internal.GeneratedHttpRequest;
@ -38,6 +42,68 @@ import org.testng.annotations.Test;
@Test(groups = "unit", testName = "blobstore.BlobStoreUtilsTest") @Test(groups = "unit", testName = "blobstore.BlobStoreUtilsTest")
public class BlobStoreUtilsTest { public class BlobStoreUtilsTest {
public void testCreateParentIfNeededAsyncNoPath() {
AsyncBlobStore asyncBlobStore = createMock(AsyncBlobStore.class);
String container = "container";
Blob blob = createMock(Blob.class);
MutableBlobMetadata md = createMock(MutableBlobMetadata.class);
expect(blob.getMetadata()).andReturn(md).atLeastOnce();
expect(md.getName()).andReturn("hello").atLeastOnce();
replay(asyncBlobStore);
replay(blob);
replay(md);
BlobStoreUtilsImpl.createParentIfNeededAsync(asyncBlobStore, container, blob);
verify(asyncBlobStore);
verify(blob);
verify(md);
}
public void testCreateParentIfNeededAsyncSinglePath() {
AsyncBlobStore asyncBlobStore = createMock(AsyncBlobStore.class);
String container = "container";
Blob blob = createMock(Blob.class);
MutableBlobMetadata md = createMock(MutableBlobMetadata.class);
expect(blob.getMetadata()).andReturn(md).atLeastOnce();
expect(md.getName()).andReturn("rootpath/hello").atLeastOnce();
expect(asyncBlobStore.createDirectory("container", "rootpath")).andReturn(null);
replay(asyncBlobStore);
replay(blob);
replay(md);
BlobStoreUtilsImpl.createParentIfNeededAsync(asyncBlobStore, container, blob);
verify(asyncBlobStore);
verify(blob);
verify(md);
}
public void testCreateParentIfNeededAsyncNestedPath() {
AsyncBlobStore asyncBlobStore = createMock(AsyncBlobStore.class);
String container = "container";
Blob blob = createMock(Blob.class);
MutableBlobMetadata md = createMock(MutableBlobMetadata.class);
expect(blob.getMetadata()).andReturn(md).atLeastOnce();
expect(md.getName()).andReturn("rootpath/subpath/hello").atLeastOnce();
expect(asyncBlobStore.createDirectory("container", "rootpath/subpath")).andReturn(null);
replay(asyncBlobStore);
replay(blob);
replay(md);
BlobStoreUtilsImpl.createParentIfNeededAsync(asyncBlobStore, container, blob);
verify(asyncBlobStore);
verify(blob);
verify(md);
}
public void testGetKeyForAzureS3AndRackspace() { public void testGetKeyForAzureS3AndRackspace() {
GeneratedHttpRequest<?> request = createMock(GeneratedHttpRequest.class); GeneratedHttpRequest<?> request = createMock(GeneratedHttpRequest.class);
@ -45,7 +111,8 @@ public class BlobStoreUtilsTest {
HttpResponse from = createMock(HttpResponse.class); HttpResponse from = createMock(HttpResponse.class);
expect(request.getEndpoint()).andReturn( expect(request.getEndpoint()).andReturn(
URI.create("https://jclouds.blob.core.windows.net/adriancole-blobstore0/five")); URI.create("https://jclouds.blob.core.windows.net/adriancole-blobstore0/five"));
expect(request.getArgs()).andReturn(new Object[] { "adriancole-blobstore0", "five" }).atLeastOnce(); expect(request.getArgs()).andReturn(new Object[] { "adriancole-blobstore0", "five" })
.atLeastOnce();
replay(request); replay(request);
replay(from); replay(from);
@ -58,15 +125,19 @@ public class BlobStoreUtilsTest {
GeneratedHttpRequest<?> request = createMock(GeneratedHttpRequest.class); GeneratedHttpRequest<?> request = createMock(GeneratedHttpRequest.class);
HttpResponse from = createMock(HttpResponse.class); HttpResponse from = createMock(HttpResponse.class);
expect(request.getEndpoint()).andReturn( expect(request.getEndpoint())
URI.create("https://storage4.clouddrive.com/v1/MossoCloudFS_dc1f419c-5059-4c87-a389-3f2e33a77b22/adriancole-blobstore0/four")); .andReturn(
expect(request.getArgs()).andReturn(new Object[] { "adriancole-blobstore0/four" }).atLeastOnce(); URI
.create("https://storage4.clouddrive.com/v1/MossoCloudFS_dc1f419c-5059-4c87-a389-3f2e33a77b22/adriancole-blobstore0/four"));
expect(request.getArgs()).andReturn(new Object[] { "adriancole-blobstore0/four" })
.atLeastOnce();
replay(request); replay(request);
replay(from); replay(from);
assertEquals(BlobStoreUtilsImpl.getKeyFor(request, from), "four"); assertEquals(BlobStoreUtilsImpl.getKeyFor(request, from), "four");
} }
public void testGetContainer() { public void testGetContainer() {
String container = BlobStoreUtilsImpl.parseContainerFromPath("foo"); String container = BlobStoreUtilsImpl.parseContainerFromPath("foo");
assertEquals(container, "foo"); assertEquals(container, "foo");

View File

@ -20,6 +20,7 @@ package org.jclouds.rackspace.cloudfiles.blobstore;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.util.concurrent.Futures.compose; import static com.google.common.util.concurrent.Futures.compose;
import static org.jclouds.blobstore.util.internal.BlobStoreUtilsImpl.createParentIfNeededAsync;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
@ -80,9 +81,8 @@ public class CloudFilesAsyncBlobStore extends BaseAsyncBlobStore {
@Inject @Inject
CloudFilesAsyncBlobStore(BlobStoreContext context, BlobStoreUtils blobUtils, CloudFilesAsyncBlobStore(BlobStoreContext context, BlobStoreUtils blobUtils,
@Named(Constants.PROPERTY_USER_THREADS) ExecutorService service, @Named(Constants.PROPERTY_USER_THREADS) ExecutorService service,
Location defaultLocation, Set<? extends Location> locations, Location defaultLocation, Set<? extends Location> locations, CloudFilesClient sync,
CloudFilesClient sync, CloudFilesAsyncClient async, CloudFilesAsyncClient async, ContainerToResourceMetadata container2ResourceMd,
ContainerToResourceMetadata container2ResourceMd,
BlobStoreListContainerOptionsToListContainerOptions container2ContainerListOptions, BlobStoreListContainerOptionsToListContainerOptions container2ContainerListOptions,
ContainerToResourceList container2ResourceList, ObjectToBlob object2Blob, ContainerToResourceList container2ResourceList, ObjectToBlob object2Blob,
BlobToObject blob2Object, ObjectToBlobMetadata object2BlobMd, BlobToObject blob2Object, ObjectToBlobMetadata object2BlobMd,
@ -216,6 +216,7 @@ public class CloudFilesAsyncBlobStore extends BaseAsyncBlobStore {
*/ */
@Override @Override
public ListenableFuture<String> putBlob(String container, Blob blob) { public ListenableFuture<String> putBlob(String container, Blob blob) {
createParentIfNeededAsync(this, container, blob);
return async.putObject(container, blob2Object.apply(blob)); return async.putObject(container, blob2Object.apply(blob));
} }

View File

@ -19,6 +19,7 @@
package org.jclouds.rackspace.cloudfiles.blobstore; package org.jclouds.rackspace.cloudfiles.blobstore;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import static org.jclouds.blobstore.util.internal.BlobStoreUtilsImpl.createParentIfNeededAsync;
import java.util.Set; import java.util.Set;
@ -69,8 +70,8 @@ public class CloudFilesBlobStore extends BaseBlobStore {
@Inject @Inject
CloudFilesBlobStore(BlobStoreContext context, BlobStoreUtils blobUtils, CloudFilesBlobStore(BlobStoreContext context, BlobStoreUtils blobUtils,
Location defaultLocation, Set<? extends Location> locations, Location defaultLocation, Set<? extends Location> locations, CloudFilesClient sync,
CloudFilesClient sync, ContainerToResourceMetadata container2ResourceMd, ContainerToResourceMetadata container2ResourceMd,
BlobStoreListContainerOptionsToListContainerOptions container2ContainerListOptions, BlobStoreListContainerOptionsToListContainerOptions container2ContainerListOptions,
ContainerToResourceList container2ResourceList, ObjectToBlob object2Blob, ContainerToResourceList container2ResourceList, ObjectToBlob object2Blob,
BlobToObject blob2Object, ObjectToBlobMetadata object2BlobMd, BlobToObject blob2Object, ObjectToBlobMetadata object2BlobMd,
@ -194,6 +195,7 @@ public class CloudFilesBlobStore extends BaseBlobStore {
*/ */
@Override @Override
public String putBlob(String container, Blob blob) { public String putBlob(String container, Blob blob) {
createParentIfNeededAsync(context.getAsyncBlobStore(), container, blob);
return sync.putObject(container, blob2Object.apply(blob)); return sync.putObject(container, blob2Object.apply(blob));
} }

View File

@ -44,7 +44,7 @@ public class BlobStoreListContainerOptionsToListContainerOptions
options.underPath(""); options.underPath("");
} }
if ((from.getDir() != null) && (from.isRecursive())) { if ((from.getDir() != null) && (from.isRecursive())) {
options.withPrefix(from.getDir()); options.withPrefix(from.getDir().endsWith("/") ? from.getDir() : from.getDir() + "/");
} }
if ((from.getDir() != null) && (!from.isRecursive())) { if ((from.getDir() != null) && (!from.isRecursive())) {
options.underPath(from.getDir()); options.underPath(from.getDir());