mirror of https://github.com/apache/jclouds.git
Issue 111: fixed blobstore related bugs and accomodated eventual consistency
git-svn-id: http://jclouds.googlecode.com/svn/trunk@2040 3d8758e0-26b5-11de-8745-db77d3ebf521
This commit is contained in:
parent
f3cd3f2db9
commit
197801204e
|
@ -173,7 +173,7 @@ public class AtmosBlobStore implements BlobStore {
|
||||||
public Future<? extends ListContainerResponse<? extends ResourceMetadata>> list(
|
public Future<? extends ListContainerResponse<? extends ResourceMetadata>> list(
|
||||||
String container, org.jclouds.blobstore.options.ListContainerOptions... optionsList) {
|
String container, org.jclouds.blobstore.options.ListContainerOptions... optionsList) {
|
||||||
if (optionsList.length == 1) {
|
if (optionsList.length == 1) {
|
||||||
if (!optionsList[0].isRecursive()) {
|
if (optionsList[0].isRecursive()) {
|
||||||
throw new UnsupportedOperationException("recursive not currently supported in emcsaas");
|
throw new UnsupportedOperationException("recursive not currently supported in emcsaas");
|
||||||
}
|
}
|
||||||
if (optionsList[0].getPath() != null) {
|
if (optionsList[0].getPath() != null) {
|
||||||
|
@ -184,6 +184,9 @@ public class AtmosBlobStore implements BlobStore {
|
||||||
return wrapFuture(connection.listDirectory(container, nativeOptions), container2ResourceList);
|
return wrapFuture(connection.listDirectory(container, nativeOptions), container2ResourceList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Since there is no etag support in atmos, we just return the path.
|
||||||
|
*/
|
||||||
public Future<String> putBlob(final String container, final Blob blob) {
|
public Future<String> putBlob(final String container, final Blob blob) {
|
||||||
final String path = container + "/" + blob.getMetadata().getName();
|
final String path = container + "/" + blob.getMetadata().getName();
|
||||||
|
|
||||||
|
@ -191,19 +194,24 @@ public class AtmosBlobStore implements BlobStore {
|
||||||
.deletePath(path), new Function<Void, String>() {
|
.deletePath(path), new Function<Void, String>() {
|
||||||
|
|
||||||
public String apply(Void from) {
|
public String apply(Void from) {
|
||||||
boolean exists = connection.pathExists(path);
|
|
||||||
if (!exists)
|
|
||||||
try {
|
try {
|
||||||
|
if (!Utils.enventuallyTrue(new Supplier<Boolean>() {
|
||||||
|
public Boolean get() {
|
||||||
|
return !connection.pathExists(path);
|
||||||
|
}
|
||||||
|
}, requestTimeoutMilliseconds)) {
|
||||||
|
throw new IllegalStateException(path + " still exists after deleting!");
|
||||||
|
}
|
||||||
if (blob.getMetadata().getContentMD5() != null)
|
if (blob.getMetadata().getContentMD5() != null)
|
||||||
blob.getMetadata().getUserMetadata().put("content-md5",
|
blob.getMetadata().getUserMetadata().put("content-md5",
|
||||||
HttpUtils.toHexString(blob.getMetadata().getContentMD5()));
|
HttpUtils.toHexString(blob.getMetadata().getContentMD5()));
|
||||||
connection.createFile(container, blob2Object.apply(blob)).get();
|
connection.createFile(container, blob2Object.apply(blob)).get();
|
||||||
|
return path;
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
} catch (ExecutionException e) {
|
} catch (ExecutionException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
package org.jclouds.atmosonline.saas.blobstore.functions;
|
package org.jclouds.atmosonline.saas.blobstore.functions;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
|
@ -8,8 +12,11 @@ import org.jclouds.atmosonline.saas.functions.AtmosObjectName;
|
||||||
import org.jclouds.blobstore.domain.MutableBlobMetadata;
|
import org.jclouds.blobstore.domain.MutableBlobMetadata;
|
||||||
import org.jclouds.blobstore.domain.ResourceType;
|
import org.jclouds.blobstore.domain.ResourceType;
|
||||||
import org.jclouds.blobstore.domain.internal.MutableBlobMetadataImpl;
|
import org.jclouds.blobstore.domain.internal.MutableBlobMetadataImpl;
|
||||||
|
import org.jclouds.http.HttpUtils;
|
||||||
|
|
||||||
import com.google.common.base.Function;
|
import com.google.common.base.Function;
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Adrian Cole
|
* @author Adrian Cole
|
||||||
|
@ -17,6 +24,9 @@ import com.google.common.base.Function;
|
||||||
@Singleton
|
@Singleton
|
||||||
public class ObjectToBlobMetadata implements Function<AtmosObject, MutableBlobMetadata> {
|
public class ObjectToBlobMetadata implements Function<AtmosObject, MutableBlobMetadata> {
|
||||||
private final AtmosObjectName objectName;
|
private final AtmosObjectName objectName;
|
||||||
|
private static final Set<String> systemMetadata = ImmutableSet.of("atime", "mtime", "ctime",
|
||||||
|
"itime", "type", "uid", "gid", "objectid", "objname", "size", "nlink", "policyname",
|
||||||
|
"content-md5");
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
protected ObjectToBlobMetadata(AtmosObjectName objectName) {
|
protected ObjectToBlobMetadata(AtmosObjectName objectName) {
|
||||||
|
@ -27,13 +37,21 @@ public class ObjectToBlobMetadata implements Function<AtmosObject, MutableBlobMe
|
||||||
MutableBlobMetadata to = new MutableBlobMetadataImpl();
|
MutableBlobMetadata to = new MutableBlobMetadataImpl();
|
||||||
to.setId(from.getSystemMetadata().getObjectID());
|
to.setId(from.getSystemMetadata().getObjectID());
|
||||||
to.setLastModified(from.getSystemMetadata().getLastUserDataModification());
|
to.setLastModified(from.getSystemMetadata().getLastUserDataModification());
|
||||||
to.setContentMD5(from.getContentMetadata().getContentMD5());
|
String md5hex = from.getUserMetadata().getMetadata().get("content-md5");
|
||||||
|
if (md5hex != null)
|
||||||
|
to.setContentMD5(HttpUtils.fromHexString(md5hex));
|
||||||
if (from.getContentMetadata().getContentType() != null)
|
if (from.getContentMetadata().getContentType() != null)
|
||||||
to.setContentType(from.getContentMetadata().getContentType());
|
to.setContentType(from.getContentMetadata().getContentType());
|
||||||
to.setName(objectName.apply(from));
|
to.setName(objectName.apply(from));
|
||||||
to.setSize(from.getSystemMetadata().getSize());
|
to.setSize(from.getSystemMetadata().getSize());
|
||||||
to.setType(ResourceType.BLOB);
|
to.setType(ResourceType.BLOB);
|
||||||
to.setUserMetadata(from.getUserMetadata().getMetadata());
|
Map<String, String> lowerKeyMetadata = Maps.newHashMap();
|
||||||
|
for (Entry<String, String> entry : from.getUserMetadata().getMetadata().entrySet()) {
|
||||||
|
String key = entry.getKey().toLowerCase();
|
||||||
|
if (!systemMetadata.contains(key))
|
||||||
|
lowerKeyMetadata.put(key, entry.getValue());
|
||||||
|
}
|
||||||
|
to.setUserMetadata(lowerKeyMetadata);
|
||||||
return to;
|
return to;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -27,8 +27,8 @@ public class FindMD5InUserMetadata implements ContainsValueInListStrategy {
|
||||||
private final AtmosStorageClient client;
|
private final AtmosStorageClient client;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
private FindMD5InUserMetadata(ObjectMD5 objectMD5,
|
private FindMD5InUserMetadata(ObjectMD5 objectMD5, ListBlobMetadataStrategy getAllBlobMetadata,
|
||||||
ListBlobMetadataStrategy getAllBlobMetadata, AtmosStorageClient client) {
|
AtmosStorageClient client) {
|
||||||
this.objectMD5 = objectMD5;
|
this.objectMD5 = objectMD5;
|
||||||
this.getAllBlobMetadata = getAllBlobMetadata;
|
this.getAllBlobMetadata = getAllBlobMetadata;
|
||||||
this.client = client;
|
this.client = client;
|
||||||
|
@ -39,7 +39,8 @@ public class FindMD5InUserMetadata implements ContainsValueInListStrategy {
|
||||||
byte[] toSearch = objectMD5.apply(value);
|
byte[] toSearch = objectMD5.apply(value);
|
||||||
String hex = HttpUtils.toHexString(toSearch);
|
String hex = HttpUtils.toHexString(toSearch);
|
||||||
for (BlobMetadata metadata : getAllBlobMetadata.execute(containerName, options)) {
|
for (BlobMetadata metadata : getAllBlobMetadata.execute(containerName, options)) {
|
||||||
UserMetadata properties = client.getUserMetadata(containerName+"/"+metadata.getName());
|
UserMetadata properties = client.headFile(containerName + "/" + metadata.getName())
|
||||||
|
.getUserMetadata();
|
||||||
if (hex.equals(properties.getMetadata().get("content-md5")))
|
if (hex.equals(properties.getMetadata().get("content-md5")))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -195,7 +195,7 @@ public class AtmosStorageClientLiveTest {
|
||||||
|
|
||||||
// loop to gather metrics
|
// loop to gather metrics
|
||||||
for (boolean stream : new Boolean[] { true, false }) {
|
for (boolean stream : new Boolean[] { true, false }) {
|
||||||
for (int i = 0; i < 30; i++) {
|
for (int i = 0; i < 10; i++) {
|
||||||
System.err.printf("upload/delete/create attempt %d type %s%n", i + 1, stream ? "stream"
|
System.err.printf("upload/delete/create attempt %d type %s%n", i + 1, stream ? "stream"
|
||||||
: "string");
|
: "string");
|
||||||
// try updating
|
// try updating
|
||||||
|
|
|
@ -23,8 +23,13 @@
|
||||||
*/
|
*/
|
||||||
package org.jclouds.atmosonline.saas.blobstore.integration;
|
package org.jclouds.atmosonline.saas.blobstore.integration;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
|
||||||
import org.jclouds.atmosonline.saas.AtmosStorageClient;
|
import org.jclouds.atmosonline.saas.AtmosStorageClient;
|
||||||
import org.jclouds.blobstore.integration.internal.BaseBlobIntegrationTest;
|
import org.jclouds.blobstore.integration.internal.BaseBlobIntegrationTest;
|
||||||
|
import org.testng.annotations.DataProvider;
|
||||||
import org.testng.annotations.Test;
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -34,4 +39,46 @@ import org.testng.annotations.Test;
|
||||||
@Test(groups = { "integration", "live" }, testName = "emcsaas.AtmosStorageIntegrationTest")
|
@Test(groups = { "integration", "live" }, testName = "emcsaas.AtmosStorageIntegrationTest")
|
||||||
public class AtmosStorageIntegrationTest extends BaseBlobIntegrationTest<AtmosStorageClient> {
|
public class AtmosStorageIntegrationTest extends BaseBlobIntegrationTest<AtmosStorageClient> {
|
||||||
|
|
||||||
|
@DataProvider(name = "delete")
|
||||||
|
// no unicode support
|
||||||
|
@Override
|
||||||
|
public Object[][] createData() {
|
||||||
|
return new Object[][] { { "normal" } };
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Test(enabled = false)
|
||||||
|
public void testGetIfMatch() throws InterruptedException, ExecutionException, TimeoutException,
|
||||||
|
IOException {
|
||||||
|
// no etag support
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Test(enabled = false)
|
||||||
|
public void testGetIfModifiedSince() throws InterruptedException, ExecutionException,
|
||||||
|
TimeoutException, IOException {
|
||||||
|
// not supported
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Test(enabled = false)
|
||||||
|
public void testGetIfNoneMatch() throws InterruptedException, ExecutionException,
|
||||||
|
TimeoutException, IOException {
|
||||||
|
// no etag support
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Test(enabled = false)
|
||||||
|
public void testGetIfUnmodifiedSince() throws InterruptedException, ExecutionException,
|
||||||
|
TimeoutException, IOException {
|
||||||
|
// not supported
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Test(enabled = false)
|
||||||
|
public void testGetTwoRanges() throws InterruptedException, ExecutionException,
|
||||||
|
TimeoutException, IOException {
|
||||||
|
// not supported
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
Loading…
Reference in New Issue