JCLOUDS-929: Implement generic delimiter support.

The patch adds delimiter option support in the generic blob store
interface. A live integration test is added to verify that jclouds
correctly lists objects separated by a delimiter.
This commit is contained in:
Timur Alperovich 2015-06-08 17:25:52 -07:00 committed by Andrew Gaul
parent 1fe90b03c9
commit a29d75a5d1
10 changed files with 107 additions and 7 deletions

View File

@ -30,4 +30,9 @@ public class AtmosContainerLiveTest extends BaseContainerLiveTest {
public void testContainerListWithPrefix() {
throw new SkipException("Prefix option has not been plumbed down to Atmos");
}
@Override
public void testDelimiterList() {
throw new SkipException("Delimiter support is not yet implemented");
}
}

View File

@ -21,6 +21,7 @@ import static org.jclouds.openstack.keystone.v2_0.config.KeystoneProperties.CRED
import java.util.Properties;
import org.jclouds.blobstore.integration.internal.BaseContainerLiveTest;
import org.testng.SkipException;
import org.testng.annotations.Test;
@Test(groups = "live", testName = "SwiftContainerLiveTest")
@ -36,4 +37,10 @@ public class SwiftContainerLiveTest extends BaseContainerLiveTest {
setIfTestSystemPropertyPresent(props, CREDENTIAL_TYPE);
return props;
}
@Override
@Test
public void testDelimiterList() {
throw new SkipException("Delimiter support is not yet implemented");
}
}

View File

@ -18,6 +18,7 @@ package org.jclouds.s3.blobstore.integration;
import org.jclouds.blobstore.integration.internal.BaseBlobStoreIntegrationTest;
import org.jclouds.blobstore.integration.internal.BaseContainerLiveTest;
import org.testng.SkipException;
import org.testng.annotations.Test;
@Test(groups = "live", testName = "S3ContainerLiveTest")
@ -27,4 +28,10 @@ public class S3ContainerLiveTest extends BaseContainerLiveTest {
provider = "s3";
BaseBlobStoreIntegrationTest.SANITY_CHECK_RETURNED_BUCKET_NAME = true;
}
@Override
@Test
public void testDelimiterList() {
throw new SkipException("not yet implemented");
}
}

View File

@ -33,7 +33,7 @@ public class SwiftContainerLiveTest extends BaseContainerLiveTest {
setIfTestSystemPropertyPresent(props, KeystoneProperties.CREDENTIAL_TYPE);
return props;
}
public SwiftContainerLiveTest() {
provider = System.getProperty("test.swift.provider", "swift");
}
@ -58,4 +58,9 @@ public class SwiftContainerLiveTest extends BaseContainerLiveTest {
public void testContainerListWithPrefix() {
throw new SkipException("Prefix option has not been plumbed down to Swift");
}
@Override
public void testDelimiterList() {
throw new SkipException("The test fails as the path parameter elides subdirectories");
}
}

View File

@ -39,6 +39,7 @@ public class ListContainerOptions extends ListOptions implements Cloneable {
public static final ImmutableListContainerOptions NONE = new ImmutableListContainerOptions(
new ListContainerOptions());
private String delimiter;
private String dir;
private String prefix;
private boolean recursive;
@ -48,12 +49,13 @@ public class ListContainerOptions extends ListOptions implements Cloneable {
}
ListContainerOptions(Integer maxKeys, String marker, String dir, boolean recursive,
boolean detailed, String prefix) {
boolean detailed, String prefix, String delimiter) {
super(maxKeys, marker);
this.dir = dir;
this.recursive = recursive;
this.detailed = detailed;
this.prefix = prefix;
this.delimiter = delimiter;
}
public static class ImmutableListContainerOptions extends ListContainerOptions {
@ -124,6 +126,16 @@ public class ListContainerOptions extends ListOptions implements Cloneable {
return delegate.clone();
}
@Override
public ListContainerOptions delimiter(String delimiterString) {
throw new UnsupportedOperationException();
}
@Override
public String getDelimiter() {
return delegate.getDelimiter();
}
@Override
public String toString() {
return delegate.toString();
@ -135,6 +147,10 @@ public class ListContainerOptions extends ListOptions implements Cloneable {
return dir;
}
public String getDelimiter() {
return delimiter;
}
public boolean isRecursive() {
return recursive;
}
@ -149,7 +165,7 @@ public class ListContainerOptions extends ListOptions implements Cloneable {
/**
* This will list the contents of a virtual or real directory path.
*
*
*/
public ListContainerOptions inDirectory(String dir) {
checkNotNull(dir, "dir");
@ -200,6 +216,15 @@ public class ListContainerOptions extends ListOptions implements Cloneable {
return this;
}
/**
* specify the delimiter to be used when listing
*
*/
public ListContainerOptions delimiter(String delimiterString) {
this.delimiter = delimiterString;
return this;
}
public static class Builder {
/**
@ -249,11 +274,18 @@ public class ListContainerOptions extends ListOptions implements Cloneable {
ListContainerOptions options = new ListContainerOptions();
return options.prefix(prefix);
}
/**
* @see ListContainerOptions#delimiter(String)
*/
public static ListContainerOptions delimiter(String delimiterString) {
ListContainerOptions options = new ListContainerOptions();
return options.delimiter(delimiterString);
}
}
@Override
public ListContainerOptions clone() {
return new ListContainerOptions(getMaxResults(), getMarker(), dir, recursive, detailed, prefix);
return new ListContainerOptions(getMaxResults(), getMarker(), dir, recursive, detailed, prefix, delimiter);
}
@Override
@ -282,6 +314,6 @@ public class ListContainerOptions extends ListOptions implements Cloneable {
Objects.equal(getMarker(), other.getMarker()) &&
Objects.equal(getMaxResults(), other.getMaxResults());
}
}

View File

@ -382,6 +382,9 @@ public class DeleteAllKeysInList implements ClearListStrategy, ClearContainerStr
public void execute(final String containerName,
ListContainerOptions listOptions) {
if (listOptions.getDelimiter() != null || listOptions.getPrefix() != null) {
throw new IllegalArgumentException("Prefix and delimiter support has not yet been added");
}
final AtomicBoolean deleteFailure = new AtomicBoolean();
int retries = maxErrors;

View File

@ -133,6 +133,29 @@ public class BaseContainerLiveTest extends BaseBlobStoreIntegrationTest {
blobStore.putBlob(containerName, blobStore.blobBuilder("foo").payload("").build());
checkEqualNames(ImmutableSet.of(prefix, prefix + "foo", prefix + "bar"),
blobStore.list(containerName, ListContainerOptions.Builder.prefix(prefix)));
}
finally {
returnContainer(containerName);
}
}
@Test(groups = "live")
public void testDelimiterList() throws InterruptedException {
final String containerName = getContainerName();
BlobStore blobStore = view.getBlobStore();
String payload = "foo";
try {
blobStore.putBlob(containerName, blobStore.blobBuilder("test/foo/foo").payload(payload).build());
blobStore.putBlob(containerName, blobStore.blobBuilder("test/bar/foo").payload(payload).build());
blobStore.putBlob(containerName, blobStore.blobBuilder("foo").payload(payload).build());
blobStore.putBlob(containerName, blobStore.blobBuilder("test-foo").payload(payload).build());
blobStore.putBlob(containerName, blobStore.blobBuilder("test-bar").payload(payload).build());
checkEqualNames(ImmutableSet.of("foo", "test/", "test-foo", "test-bar"), blobStore.list(
containerName, ListContainerOptions.Builder.delimiter("/")));
checkEqualNames(ImmutableSet.of("test/foo/foo", "test/bar/foo", "foo", "test-foo", "test-bar"),
blobStore.list(containerName, ListContainerOptions.Builder.delimiter("\\")));
checkEqualNames(ImmutableSet.of("test-", "test/foo/foo", "test/bar/foo", "foo"), blobStore.list(
containerName, ListContainerOptions.Builder.delimiter("-")));
} finally {
returnContainer(containerName);
}
@ -173,5 +196,4 @@ public class BaseContainerLiveTest extends BaseBlobStoreIntegrationTest {
recycleContainerAndAddToPool(containerName);
}
}
}

View File

@ -49,6 +49,13 @@ public class ListOptionsTest {
assertEquals(options.getDir(), "test");
}
@Test
public void testDelimiter() {
ListContainerOptions options = new ListContainerOptions();
options.delimiter("-");
assertEquals(options.getDelimiter(), "-");
}
@Test
public void testPathStatic() {
ListContainerOptions options = inDirectory("test");

View File

@ -17,6 +17,7 @@
package org.jclouds.azureblob.blobstore.integration;
import org.jclouds.blobstore.integration.internal.BaseContainerLiveTest;
import org.testng.SkipException;
import org.testng.annotations.Test;
@Test(groups = { "live" })
@ -24,4 +25,10 @@ public class AzureBlobContainerLiveTest extends BaseContainerLiveTest {
public AzureBlobContainerLiveTest() {
provider = "azureblob";
}
@Override
@Test
public void testDelimiterList() {
throw new SkipException("The delimiter support has not been plumbed through to Azure blob");
}
}

View File

@ -34,4 +34,9 @@ public class HPCloudObjectStorageContainerLiveTest extends BaseContainerLiveTest
@Test
public void testPublicAccessInNonDefaultLocationWithBigBlob() { throw new SkipException("Locations are ignored"); }
@Override
@Test
public void testDelimiterList() {
throw new SkipException("\"path\" parameter elides subdirectories");
}
}