Skip to content

Commit

Permalink
Pending jclouds/jclouds#1212, seem to have found a workaround for JCL…
Browse files Browse the repository at this point in the history
…OUDS-1422.
  • Loading branch information
jglick committed May 23, 2018
1 parent 2f30460 commit 517bc32
Showing 1 changed file with 302 additions and 1 deletion.
Expand Up @@ -24,17 +24,31 @@

package io.jenkins.plugins.artifact_manager_s3;

import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.base.Strings;
import static com.google.common.collect.Iterables.transform;
import static com.google.common.collect.Sets.filter;
import static com.google.common.collect.Sets.newTreeSet;
import com.google.inject.AbstractModule;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URL;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.concurrent.ExecutorService;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import javax.inject.Inject;
import org.apache.commons.io.IOUtils;
import org.apache.http.ExceptionLogger;
Expand All @@ -61,12 +75,25 @@
import org.jclouds.blobstore.config.TransientBlobStoreContextModule;
import org.jclouds.blobstore.domain.Blob;
import org.jclouds.blobstore.domain.BlobAccess;
import org.jclouds.blobstore.domain.BlobBuilder;
import org.jclouds.blobstore.domain.BlobMetadata;
import org.jclouds.blobstore.domain.ContainerAccess;
import org.jclouds.blobstore.domain.MultipartPart;
import org.jclouds.blobstore.domain.MultipartUpload;
import org.jclouds.blobstore.domain.MutableStorageMetadata;
import org.jclouds.blobstore.domain.PageSet;
import org.jclouds.blobstore.domain.StorageMetadata;
import org.jclouds.blobstore.domain.StorageType;
import org.jclouds.blobstore.domain.internal.MutableStorageMetadataImpl;
import org.jclouds.blobstore.domain.internal.PageSetImpl;
import org.jclouds.blobstore.options.CopyOptions;
import org.jclouds.blobstore.options.CreateContainerOptions;
import org.jclouds.blobstore.options.GetOptions;
import org.jclouds.blobstore.options.ListContainerOptions;
import org.jclouds.blobstore.options.PutOptions;
import org.jclouds.blobstore.util.BlobUtils;
import org.jclouds.domain.Location;
import org.jclouds.io.Payload;
import org.kohsuke.MetaInfServices;

/**
Expand Down Expand Up @@ -123,7 +150,7 @@ public static final class MockModule extends AbstractModule {
@Override
protected void configure() {
install(new BlobStoreObjectModule());
bind(BlobStore.class).to(LocalBlobStore.class);
bind(BlobStore.class).to(PatchedLocalBlobStore.class);
bind(ConsistencyModel.class).toInstance(ConsistencyModel.STRICT);
bind(LocalStorageStrategy.class).to(MockStrategy.class);
bind(BlobRequestSigner.class).to(LocalBlobRequestSigner.class);
Expand Down Expand Up @@ -287,4 +314,278 @@ public String getSeparator() {

}

/** @see <a href="https://issues.apache.org/jira/browse/JCLOUDS-1422">JCLOUDS-1422</a> */
public static final class PatchedLocalBlobStore implements BlobStore {

private final LocalBlobStore delegate;

@Inject
public PatchedLocalBlobStore(LocalBlobStore delegate) {
this.delegate = delegate;
}

@Override
public PageSet<? extends StorageMetadata> list(String containerName, ListContainerOptions options) {
PageSet<? extends StorageMetadata> base = delegate.list(containerName, options);
if (!options.isRecursive()) {
String prefix = options.getPrefix();
if (!Strings.isNullOrEmpty(prefix)) {
SortedSet<StorageMetadata> recursiveResult = new TreeSet<>(delegate.list(containerName, options.clone().recursive()));
SortedSet<StorageMetadata> processedResult = extractCommonPrefixes(recursiveResult, "/", prefix);
if (!processedResult.equals(new TreeSet<>(base))) {
LOGGER.log(Level.INFO, "JCLOUDS-1422: for list({0}, {1}) replacing {2} with {3}", new Object[] {containerName, options, summarize(base), summarize(processedResult)});
return new PageSetImpl<>(processedResult, /* TODO close enough for now */null);
}
}
}
return base;
}

private String summarize(Collection<? extends StorageMetadata> list) {
return list.stream().map(StorageMetadata::getName).collect(Collectors.joining(":"));
}

// from LocalBlobStore
private static SortedSet<StorageMetadata> extractCommonPrefixes(SortedSet<StorageMetadata> contents, String delimiter, String prefix) {
if (Strings.isNullOrEmpty(delimiter)) {
return contents;
}
SortedSet<String> commonPrefixes = newTreeSet(transform(contents, new CommonPrefixes(prefix, delimiter)));
commonPrefixes.remove(CommonPrefixes.NO_PREFIX);
contents = newTreeSet(filter(contents, new DelimiterFilter(prefix, delimiter)));
for (String o : commonPrefixes) {
MutableStorageMetadata md = new MutableStorageMetadataImpl();
md.setType(StorageType.RELATIVE_PATH);
if (prefix != null) {
o = prefix + o;
}
md.setName(o + delimiter);
contents.add(md);
}
return contents;
}
private static class CommonPrefixes implements Function<StorageMetadata, String> {
private final String prefix;
private final String delimiter;
public static final String NO_PREFIX = "NO_PREFIX";
CommonPrefixes(String prefix, String delimiter) {
this.prefix = prefix;
this.delimiter = delimiter;
}
@Override
public String apply(StorageMetadata metadata) {
String working = metadata.getName();
if (prefix != null) {
if (working.startsWith(prefix)) {
working = working.substring(prefix.length());
} else {
return NO_PREFIX;
}
}
if (working.contains(delimiter)) {
return working.substring(0, working.indexOf(delimiter));
} else {
return NO_PREFIX;
}
}
}
private static class DelimiterFilter implements Predicate<StorageMetadata> {
private final String prefix;
private final String delimiter;
DelimiterFilter(String prefix, String delimiter) {
this.prefix = prefix;
this.delimiter = delimiter;
}
@Override
public boolean apply(StorageMetadata metadata) {
String name = metadata.getName();
if (prefix == null || prefix.isEmpty()) {
return !name.contains(delimiter);
}
if (name.startsWith(prefix)) {
String unprefixedName = name.substring(prefix.length());
if (unprefixedName.isEmpty()) {
return true;
}
return !unprefixedName.contains(delimiter);
}
return false;
}
}

@Override
public BlobStoreContext getContext() {
return delegate.getContext();
}
@Override
public BlobBuilder blobBuilder(String name) {
return delegate.blobBuilder(name);
}
@Override
public PageSet<? extends StorageMetadata> list(String containerName) {
return delegate.list(containerName);
}
@Override
public long countBlobs(String containerName) {
return delegate.countBlobs(containerName);
}
@Override
public long countBlobs(String containerName, ListContainerOptions options) {
return delegate.countBlobs(containerName, options);
}
@Override
public void clearContainer(String containerName) {
delegate.clearContainer(containerName);
}
@Override
public void clearContainer(String containerName, ListContainerOptions options) {
delegate.clearContainer(containerName, options);
}
@Override
public void deleteDirectory(String containerName, String directory) {
delegate.deleteDirectory(containerName, directory);
}
@Override
public boolean directoryExists(String containerName, String directory) {
return delegate.directoryExists(containerName, directory);
}
@Override
public void createDirectory(String containerName, String directory) {
delegate.createDirectory(containerName, directory);
}
@Override
public Blob getBlob(String containerName, String key) {
return delegate.getBlob(containerName, key);
}
@Override
public void deleteContainer(String containerName) {
delegate.deleteContainer(containerName);
}
@Override
public Set<? extends Location> listAssignableLocations() {
return delegate.listAssignableLocations();
}
@Override
public void removeBlob(String containerName, String key) {
delegate.removeBlob(containerName, key);
}
@Override
public void removeBlobs(String container, Iterable<String> names) {
delegate.removeBlobs(container, names);
}
@Override
public BlobAccess getBlobAccess(String container, String name) {
return delegate.getBlobAccess(container, name);
}
@Override
public void setBlobAccess(String container, String name, BlobAccess access) {
delegate.setBlobAccess(container, name, access);
}
@Override
public boolean deleteContainerIfEmpty(String containerName) {
return delegate.deleteContainerIfEmpty(containerName);
}
@Override
public boolean containerExists(String containerName) {
return delegate.containerExists(containerName);
}
@Override
public PageSet<? extends StorageMetadata> list() {
return delegate.list();
}
@Override
public boolean createContainerInLocation(Location location, String name) {
return delegate.createContainerInLocation(location, name);
}
@Override
public ContainerAccess getContainerAccess(String container) {
return delegate.getContainerAccess(container);
}
@Override
public void setContainerAccess(String container, ContainerAccess access) {
delegate.setContainerAccess(container, access);
}
@Override
public String putBlob(String containerName, Blob blob) {
return delegate.putBlob(containerName, blob);
}
@Override
public String copyBlob(String fromContainer, String fromName, String toContainer, String toName, CopyOptions options) {
return delegate.copyBlob(fromContainer, fromName, toContainer, toName, options);
}
@Override
public boolean blobExists(String containerName, String key) {
return delegate.blobExists(containerName, key);
}
@Override
public Blob getBlob(String containerName, String key, GetOptions options) {
return delegate.getBlob(containerName, key, options);
}
@Override
public BlobMetadata blobMetadata(String containerName, String key) {
return delegate.blobMetadata(containerName, key);
}
@Override
public String putBlob(String containerName, Blob blob, PutOptions options) {
return delegate.putBlob(containerName, blob, options);
}
@Override
public boolean createContainerInLocation(Location location, String container, CreateContainerOptions options) {
return delegate.createContainerInLocation(location, container, options);
}
@Override
public MultipartUpload initiateMultipartUpload(String container, BlobMetadata blobMetadata, PutOptions options) {
return delegate.initiateMultipartUpload(container, blobMetadata, options);
}
@Override
public void abortMultipartUpload(MultipartUpload mpu) {
delegate.abortMultipartUpload(mpu);
}
@Override
public String completeMultipartUpload(MultipartUpload mpu, List<MultipartPart> parts) {
return delegate.completeMultipartUpload(mpu, parts);
}
@Override
public MultipartPart uploadMultipartPart(MultipartUpload mpu, int partNumber, Payload payload) {
return delegate.uploadMultipartPart(mpu, partNumber, payload);
}
@Override
public List<MultipartPart> listMultipartUpload(MultipartUpload mpu) {
return delegate.listMultipartUpload(mpu);
}
@Override
public List<MultipartUpload> listMultipartUploads(String container) {
return delegate.listMultipartUploads(container);
}
@Override
public long getMinimumMultipartPartSize() {
return delegate.getMinimumMultipartPartSize();
}
@Override
public long getMaximumMultipartPartSize() {
return delegate.getMaximumMultipartPartSize();
}
@Override
public int getMaximumNumberOfParts() {
return delegate.getMaximumNumberOfParts();
}
@Override
public void downloadBlob(String container, String name, File destination) {
delegate.downloadBlob(container, name, destination);
}
@Override
public void downloadBlob(String container, String name, File destination, ExecutorService executor) {
delegate.downloadBlob(container, name, destination, executor);
}
@Override
public InputStream streamBlob(String container, String name) {
return delegate.streamBlob(container, name);
}
@Override
public InputStream streamBlob(String container, String name, ExecutorService executor) {
return delegate.streamBlob(container, name, executor);
}

}

}

0 comments on commit 517bc32

Please sign in to comment.