From 3a0c15b345b0f2eb9a019f10adfb731a7a644b64 Mon Sep 17 00:00:00 2001 From: Andrew Gaul Date: Mon, 7 May 2012 14:16:03 -0700 Subject: [PATCH] Reduce filesystem and transient differences These providers have a similar lineage but many gratuitous differences. This commit reduces the diff between them and is a prerequisite for upcoming changes to make them more similar to each other and real providers. Some future commit might unify these in some smarter way, e.g., having a TransientStrategy to match FilesystemStrategy. --- .../filesystem/FilesystemAsyncBlobStore.java | 121 +++---- .../blobstore/TransientAsyncBlobStore.java | 319 ++++++++++-------- 2 files changed, 228 insertions(+), 212 deletions(-) diff --git a/apis/filesystem/src/main/java/org/jclouds/filesystem/FilesystemAsyncBlobStore.java b/apis/filesystem/src/main/java/org/jclouds/filesystem/FilesystemAsyncBlobStore.java index 412cf2d326..c462b44661 100644 --- a/apis/filesystem/src/main/java/org/jclouds/filesystem/FilesystemAsyncBlobStore.java +++ b/apis/filesystem/src/main/java/org/jclouds/filesystem/FilesystemAsyncBlobStore.java @@ -26,8 +26,6 @@ import static com.google.common.collect.Iterables.filter; import static com.google.common.collect.Iterables.find; import static com.google.common.collect.Iterables.size; import static com.google.common.collect.Iterables.transform; -import static com.google.common.collect.Lists.newArrayList; -import static com.google.common.collect.Lists.partition; import static com.google.common.collect.Maps.newHashMap; import static com.google.common.collect.Sets.filter; import static com.google.common.collect.Sets.newTreeSet; @@ -48,10 +46,10 @@ import java.util.Collection; import java.util.Date; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; -import java.util.Map.Entry; import java.util.concurrent.ExecutorService; import javax.annotation.Resource; @@ -126,10 +124,14 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore { protected final FilesystemStorageStrategy storageStrategy; @Inject - protected FilesystemAsyncBlobStore(BlobStoreContext context, DateService dateService, Crypto crypto, - HttpGetOptionsListToGetOptions httpGetOptionsConverter, IfDirectoryReturnNameStrategy ifDirectoryReturnName, - BlobUtils blobUtils, @Named(Constants.PROPERTY_USER_THREADS) ExecutorService service, - Supplier defaultLocation, @Memoized Supplier> locations, + protected FilesystemAsyncBlobStore(BlobStoreContext context, + DateService dateService, Crypto crypto, + HttpGetOptionsListToGetOptions httpGetOptionsConverter, + IfDirectoryReturnNameStrategy ifDirectoryReturnName, + BlobUtils blobUtils, + @Named(Constants.PROPERTY_USER_THREADS) ExecutorService service, + Supplier defaultLocation, + @Memoized Supplier> locations, FilesystemStorageStrategy storageStrategy) { super(context, blobUtils, service, defaultLocation, locations); this.dateService = dateService; @@ -146,9 +148,8 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore { public ListenableFuture> list(final String container, ListContainerOptions options) { // Check if the container exists - if (!containerExistsSyncImpl(container)) { + if (!containerExistsSyncImpl(container)) return immediateFailedFuture(cnfe(container)); - } // Loading blobs from container Iterable blobBelongingToContainer = null; @@ -183,11 +184,10 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore { final String finalMarker = options.getMarker(); StorageMetadata lastMarkerMetadata = find(contents, new Predicate() { public boolean apply(StorageMetadata metadata) { - return metadata.getName().equals(finalMarker); + return metadata.getName().compareTo(finalMarker) > 0; } }); contents = contents.tailSet(lastMarkerMetadata); - contents.remove(lastMarkerMetadata); } final String prefix = options.getDir(); @@ -199,30 +199,26 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore { })); } - Integer maxResults = options.getMaxResults() != null ? options.getMaxResults() : 1000; - if (contents.size() > 0) { - SortedSet contentsSlice = firstSliceOfSize(contents, maxResults); - if (!contentsSlice.contains(contents.last())) { + int maxResults = options.getMaxResults() != null ? options.getMaxResults() : 1000; + if (!contents.isEmpty()) { + StorageMetadata lastElement = contents.last(); + contents = newTreeSet(Iterables.limit(contents, maxResults)); + if (!contents.contains(lastElement)) { // Partial listing - marker = contentsSlice.last().getName(); - } else { - marker = null; + marker = contents.last().getName(); } - contents = contentsSlice; } final String delimiter = options.isRecursive() ? null : File.separator; if (delimiter != null) { - SortedSet commonPrefixes = null; - Iterable iterable = transform(contents, new CommonPrefixes(prefix != null ? prefix : null, - delimiter)); - commonPrefixes = iterable != null ? newTreeSet(iterable) : new TreeSet(); + SortedSet commonPrefixes = newTreeSet( + transform(contents, new CommonPrefixes(prefix, delimiter))); commonPrefixes.remove(CommonPrefixes.NO_PREFIX); - contents = newTreeSet(filter(contents, new DelimiterFilter(prefix != null ? prefix : null, delimiter))); + contents = newTreeSet(filter(contents, new DelimiterFilter(prefix, delimiter))); - Iterables. addAll(contents, - transform(commonPrefixes, new Function() { + Iterables. addAll(contents, transform(commonPrefixes, + new Function() { public StorageMetadata apply(String o) { MutableStorageMetadata md = new MutableStorageMetadataImpl(); md.setType(StorageType.RELATIVE_PATH); @@ -245,7 +241,7 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore { } - private ContainerNotFoundException cnfe(String name) { + private ContainerNotFoundException cnfe(final String name) { return new ContainerNotFoundException(name, String.format("container %s not in filesystem", name)); } @@ -284,18 +280,31 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore { * {@inheritDoc} */ @Override - public ListenableFuture removeBlob(String container, String key) { + public ListenableFuture removeBlob(final String container, final String key) { storageStrategy.removeBlob(container, key); return immediateFuture(null); } + /** + * Override parent method because it uses strange futures and listenables + * that creates problem in the test if more than one test that deletes the + * container is executed + * + * @param container + * @return + */ + @Override + public ListenableFuture deleteContainer(final String container) { + deleteAndVerifyContainerGone(container); + return immediateFuture(null); + } + /** * {@inheritDoc} */ @Override - public ListenableFuture containerExists(String containerName) { - boolean exists = containerExistsSyncImpl(containerName); - return immediateFuture(exists); + public ListenableFuture containerExists(final String containerName) { + return immediateFuture(containerExistsSyncImpl(containerName)); } /** @@ -419,11 +428,6 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore { } } - public static > SortedSet firstSliceOfSize(Iterable elements, int size) { - List> slices = partition(newArrayList(elements), size); - return newTreeSet(slices.get(0)); - } - public static HttpResponseException returnResponseException(int code) { HttpResponse response = null; response = new HttpResponse(code, null, null); @@ -474,18 +478,20 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore { * {@inheritDoc} */ @Override - public ListenableFuture putBlob(String containerName, Blob object) { - String blobKey = object.getMetadata().getName(); + public ListenableFuture putBlob(String containerName, Blob blob) { + checkNotNull(containerName, "containerName must be set"); + checkNotNull(blob, "blob must be set"); + String blobKey = blob.getMetadata().getName(); - logger.debug("Put object with key [%s] to container [%s]", blobKey, containerName); - String eTag = getEtag(object); + logger.debug("Put blob with key [%s] to container [%s]", blobKey, containerName); + String eTag = getEtag(blob); try { // TODO // must override existing file? - storageStrategy.writePayloadOnFile(containerName, blobKey, object.getPayload()); + storageStrategy.writePayloadOnFile(containerName, blobKey, blob.getPayload()); } catch (IOException e) { - logger.error(e, "An error occurred storing the new object with name [%s] to container [%s].", blobKey, + logger.error(e, "An error occurred storing the new blob with name [%s] to container [%s].", blobKey, containerName); Throwables.propagate(e); } @@ -584,10 +590,10 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore { * {@inheritDoc} */ @Override - public ListenableFuture blobMetadata(String container, String key) { + public ListenableFuture blobMetadata(final String container, final String key) { try { Blob blob = getBlob(container, key).get(); - return Futures. immediateFuture(blob != null ? blob.getMetadata() : null); + return immediateFuture(blob != null ? (BlobMetadata) blob.getMetadata() : null); } catch (Exception e) { if (size(filter(getCausalChain(e), KeyNotFoundException.class)) >= 1) return immediateFuture(null); @@ -595,26 +601,6 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore { } } - @Override - protected boolean deleteAndVerifyContainerGone(String container) { - storageStrategy.deleteContainer(container); - return containerExistsSyncImpl(container); - } - - /** - * Override parent method because it uses strange futures and listenables - * that creates problem in the test if more than one test that deletes the - * container is executed - * - * @param container - * @return - */ - @Override - public ListenableFuture deleteContainer(String container) { - deleteAndVerifyContainerGone(container); - return immediateFuture(null); - } - /** * Each container is a directory, so in order to check if a container exists * the corresponding directory must exists. Synchronous implementation @@ -627,7 +613,6 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore { } /** - * * Calculates the object MD5 and returns it as eTag * * @param object @@ -645,6 +630,12 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore { return eTag; } + @Override + protected boolean deleteAndVerifyContainerGone(final String container) { + storageStrategy.deleteContainer(container); + return containerExistsSyncImpl(container); + } + @Override public ListenableFuture putBlob(String container, Blob blob, PutOptions options) { // TODO implement options diff --git a/blobstore/src/main/java/org/jclouds/blobstore/TransientAsyncBlobStore.java b/blobstore/src/main/java/org/jclouds/blobstore/TransientAsyncBlobStore.java index 629b7b4d1e..b4cce8a694 100644 --- a/blobstore/src/main/java/org/jclouds/blobstore/TransientAsyncBlobStore.java +++ b/blobstore/src/main/java/org/jclouds/blobstore/TransientAsyncBlobStore.java @@ -18,7 +18,6 @@ */ package org.jclouds.blobstore; -import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Throwables.getCausalChain; @@ -55,6 +54,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutorService; +import javax.annotation.Resource; import javax.inject.Inject; import javax.inject.Named; import javax.inject.Provider; @@ -98,6 +98,7 @@ import org.jclouds.io.Payloads; import org.jclouds.io.payloads.ByteArrayPayload; import org.jclouds.io.payloads.DelegatingPayload; import org.jclouds.javax.annotation.Nullable; +import org.jclouds.logging.Logger; import com.google.common.base.Function; import com.google.common.base.Predicate; @@ -116,6 +117,9 @@ import com.google.common.util.concurrent.ListenableFuture; */ public class TransientAsyncBlobStore extends BaseAsyncBlobStore { + @Resource + protected Logger logger = Logger.NULL; + protected final DateService dateService; protected final Crypto crypto; protected final ConcurrentMap> containerToBlobs; @@ -126,13 +130,17 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore { protected final Factory blobFactory; @Inject - protected TransientAsyncBlobStore(BlobStoreContext context, DateService dateService, Crypto crypto, - ConcurrentMap> containerToBlobs, Provider uriBuilders, - ConcurrentMap containerToLocation, - HttpGetOptionsListToGetOptions httpGetOptionsConverter, - IfDirectoryReturnNameStrategy ifDirectoryReturnName, Factory blobFactory, BlobUtils blobUtils, - @Named(Constants.PROPERTY_USER_THREADS) ExecutorService service, Supplier defaultLocation, - @Memoized Supplier> locations) { + protected TransientAsyncBlobStore(BlobStoreContext context, + DateService dateService, Crypto crypto, + HttpGetOptionsListToGetOptions httpGetOptionsConverter, + IfDirectoryReturnNameStrategy ifDirectoryReturnName, + BlobUtils blobUtils, + @Named(Constants.PROPERTY_USER_THREADS) ExecutorService service, + Supplier defaultLocation, + @Memoized Supplier> locations, + Factory blobFactory, + ConcurrentMap> containerToBlobs, Provider uriBuilders, + ConcurrentMap containerToLocation) { super(context, blobUtils, service, defaultLocation, locations); this.blobFactory = blobFactory; this.dateService = dateService; @@ -153,65 +161,67 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore { public ListenableFuture> list(final String container, ListContainerOptions options) { final Map realContents = getContainerToBlobs().get(container); + // Check if the container exists if (realContents == null) return immediateFailedFuture(cnfe(container)); SortedSet contents = newTreeSet(transform(realContents.keySet(), - new Function() { - public StorageMetadata apply(String key) { - Blob oldBlob = realContents.get(key); - checkState(oldBlob != null, "blob " + key + " is not present although it was in the list of " - + container); - checkState(oldBlob.getMetadata() != null, "blob " + container + "/" + key + " has no metadata"); - MutableBlobMetadata md = copy(oldBlob.getMetadata()); - String directoryName = ifDirectoryReturnName.execute(md); - if (directoryName != null) { - md.setName(directoryName); - md.setType(StorageType.RELATIVE_PATH); - } - return md; + new Function() { + public StorageMetadata apply(String key) { + Blob oldBlob = realContents.get(key); + checkState(oldBlob != null, "blob " + key + " is not present although it was in the list of " + + container); + checkState(oldBlob.getMetadata() != null, "blob " + container + "/" + key + " has no metadata"); + MutableBlobMetadata md = copy(oldBlob.getMetadata()); + String directoryName = ifDirectoryReturnName.execute(md); + if (directoryName != null) { + md.setName(directoryName); + md.setType(StorageType.RELATIVE_PATH); } - })); - - if (options.getMarker() != null) { - final String finalMarker = options.getMarker(); - StorageMetadata lastMarkerMetadata = find(contents, new Predicate() { - public boolean apply(StorageMetadata metadata) { - return metadata.getName().compareTo(finalMarker) > 0; - } - }); - contents = contents.tailSet(lastMarkerMetadata); - } - - final String prefix = options.getDir(); - if (prefix != null) { - contents = newTreeSet(filter(contents, new Predicate() { - public boolean apply(StorageMetadata o) { - return (o != null && o.getName().startsWith(prefix) && !o.getName().equals(prefix)); - } - })); - } + return md; + } + })); String marker = null; - int maxResults = options.getMaxResults() != null ? options.getMaxResults() : 1000; - if (!contents.isEmpty()) { - StorageMetadata lastElement = contents.last(); - contents = newTreeSet(Iterables.limit(contents, maxResults)); - if (!contents.contains(lastElement)) { - // Partial listing - marker = contents.last().getName(); + if (options != null) { + if (options.getMarker() != null) { + final String finalMarker = options.getMarker(); + StorageMetadata lastMarkerMetadata = find(contents, new Predicate() { + public boolean apply(StorageMetadata metadata) { + return metadata.getName().compareTo(finalMarker) > 0; + } + }); + contents = contents.tailSet(lastMarkerMetadata); } - } - final String delimiter = options.isRecursive() ? null : "/"; - if (delimiter != null) { - SortedSet commonPrefixes = newTreeSet( - transform(contents, new CommonPrefixes(prefix, delimiter))); - commonPrefixes.remove(CommonPrefixes.NO_PREFIX); + final String prefix = options.getDir(); + if (prefix != null) { + contents = newTreeSet(filter(contents, new Predicate() { + public boolean apply(StorageMetadata o) { + return (o != null && o.getName().startsWith(prefix) && !o.getName().equals(prefix)); + } + })); + } - contents = newTreeSet(filter(contents, new DelimiterFilter(prefix, delimiter))); + int maxResults = options.getMaxResults() != null ? options.getMaxResults() : 1000; + if (!contents.isEmpty()) { + StorageMetadata lastElement = contents.last(); + contents = newTreeSet(Iterables.limit(contents, maxResults)); + if (!contents.contains(lastElement)) { + // Partial listing + marker = contents.last().getName(); + } + } - Iterables. addAll(contents, transform(commonPrefixes, + final String delimiter = options.isRecursive() ? null : "/"; + if (delimiter != null) { + SortedSet commonPrefixes = newTreeSet( + transform(contents, new CommonPrefixes(prefix, delimiter))); + commonPrefixes.remove(CommonPrefixes.NO_PREFIX); + + contents = newTreeSet(filter(contents, new DelimiterFilter(prefix, delimiter))); + + Iterables. addAll(contents, transform(commonPrefixes, new Function() { public StorageMetadata apply(String o) { MutableStorageMetadata md = new MutableStorageMetadataImpl(); @@ -220,17 +230,18 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore { return md; } })); - } + } - // trim metadata, if the response isn't supposed to be detailed. - if (!options.isDetailed()) { - for (StorageMetadata md : contents) { - md.getUserMetadata().clear(); + // trim metadata, if the response isn't supposed to be detailed. + if (!options.isDetailed()) { + for (StorageMetadata md : contents) { + md.getUserMetadata().clear(); + } } } return Futures.> immediateFuture(new PageSetImpl(contents, - marker)); + marker)); } @@ -246,10 +257,10 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore { os = new ObjectOutputStream(bout); os.writeObject(in); ObjectInput is = new ObjectInputStream(new ByteArrayInputStream(bout.toByteArray())); - MutableBlobMetadata out = (MutableBlobMetadata) is.readObject(); - convertUserMetadataKeysToLowercase(out); - HttpUtils.copy(in.getContentMetadata(), out.getContentMetadata()); - return out; + MutableBlobMetadata metadata = (MutableBlobMetadata) is.readObject(); + convertUserMetadataKeysToLowercase(metadata); + HttpUtils.copy(in.getContentMetadata(), metadata.getContentMetadata()); + return metadata; } catch (Exception e) { throw propagate(e); } @@ -315,8 +326,8 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore { * {@inheritDoc} */ @Override - public ListenableFuture containerExists(final String container) { - return immediateFuture(getContainerToBlobs().containsKey(container)); + public ListenableFuture containerExists(final String containerName) { + return immediateFuture(getContainerToBlobs().containsKey(containerName)); } /** @@ -324,16 +335,18 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore { */ @Override public ListenableFuture> list() { + Iterable containers = getContainerToBlobs().keySet(); + return Futures.> immediateFuture(new PageSetImpl(transform( - getContainerToBlobs().keySet(), new Function() { - public StorageMetadata apply(String name) { - MutableStorageMetadata cmd = create(); - cmd.setName(name); - cmd.setType(StorageType.CONTAINER); - cmd.setLocation(getContainerToLocation().get(name)); - return cmd; - } - }), null)); + containers, new Function() { + public StorageMetadata apply(String name) { + MutableStorageMetadata cmd = create(); + cmd.setName(name); + cmd.setType(StorageType.CONTAINER); + cmd.setLocation(getContainerToLocation().get(name)); + return cmd; + } + }), null)); } protected MutableStorageMetadata create() { @@ -437,10 +450,6 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore { return 0; } - public HttpRequest getCurrentRequest() { - return new HttpRequest("GET", URI.create("http://stub")); - } - public int incrementFailureCount() { return 0; } @@ -449,6 +458,11 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore { } + @Override + public HttpRequest getCurrentRequest() { + return new HttpRequest("GET", URI.create("http://stub")); + } + @Override public void setCurrentRequest(HttpRequest request) { @@ -461,15 +475,18 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore { * {@inheritDoc} */ @Override - public ListenableFuture putBlob(String containerName, Blob in) { - checkArgument(containerName != null, "containerName must be set"); - checkArgument(in != null, "blob must be set"); + public ListenableFuture putBlob(String containerName, Blob blob) { + checkNotNull(containerName, "containerName must be set"); + checkNotNull(blob, "blob must be set"); ConcurrentMap container = getContainerToBlobs().get(containerName); + String blobKey = blob.getMetadata().getName(); + + logger.debug("Put blob with key [%s] to container [%s]", blobKey, containerName); if (container == null) { return Futures.immediateFailedFuture(new IllegalStateException("containerName not found: " + containerName)); } - Blob blob = createUpdatedCopyOfBlobInContainer(containerName, in); + blob = createUpdatedCopyOfBlobInContainer(containerName, blob); container.put(blob.getMetadata().getName(), blob); @@ -536,74 +553,83 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore { */ @Override public ListenableFuture getBlob(final String containerName, final String key, GetOptions options) { - if (!getContainerToBlobs().containsKey(containerName)) + logger.debug("Retrieving blob with key %s from container %s", key, containerName); + // If the container doesn't exist, an exception is thrown + if (!getContainerToBlobs().containsKey(containerName)) { + logger.debug("Container %s does not exist", containerName); return immediateFailedFuture(cnfe(containerName)); + } + // If the blob doesn't exist, a null object is returned Map realContents = getContainerToBlobs().get(containerName); - if (!realContents.containsKey(key)) + if (!realContents.containsKey(key)) { + logger.debug("Item %s does not exist in container %s", key, containerName); return immediateFuture(null); - - Blob object = realContents.get(key); - - if (options.getIfMatch() != null) { - if (!object.getMetadata().getETag().equals(options.getIfMatch())) - return immediateFailedFuture(returnResponseException(412)); } - if (options.getIfNoneMatch() != null) { - if (object.getMetadata().getETag().equals(options.getIfNoneMatch())) - return immediateFailedFuture(returnResponseException(304)); - } - if (options.getIfModifiedSince() != null) { - Date modifiedSince = options.getIfModifiedSince(); - if (object.getMetadata().getLastModified().before(modifiedSince)) { - HttpResponse response = new HttpResponse(304, null, null); - return immediateFailedFuture(new HttpResponseException(String.format("%1$s is before %2$s", object + + Blob blob = realContents.get(key); + + if (options != null) { + if (options.getIfMatch() != null) { + if (!blob.getMetadata().getETag().equals(options.getIfMatch())) + return immediateFailedFuture(returnResponseException(412)); + } + if (options.getIfNoneMatch() != null) { + if (blob.getMetadata().getETag().equals(options.getIfNoneMatch())) + return immediateFailedFuture(returnResponseException(304)); + } + if (options.getIfModifiedSince() != null) { + Date modifiedSince = options.getIfModifiedSince(); + if (blob.getMetadata().getLastModified().before(modifiedSince)) { + HttpResponse response = new HttpResponse(304, null, null); + return immediateFailedFuture(new HttpResponseException(String.format("%1$s is before %2$s", blob .getMetadata().getLastModified(), modifiedSince), null, response)); - } - - } - if (options.getIfUnmodifiedSince() != null) { - Date unmodifiedSince = options.getIfUnmodifiedSince(); - if (object.getMetadata().getLastModified().after(unmodifiedSince)) { - HttpResponse response = new HttpResponse(412, null, null); - return immediateFailedFuture(new HttpResponseException(String.format("%1$s is after %2$s", object - .getMetadata().getLastModified(), unmodifiedSince), null, response)); - } - } - Blob returnVal = copyBlob(object); - - if (options.getRanges() != null && options.getRanges().size() > 0) { - byte[] data; - try { - data = toByteArray(returnVal.getPayload().getInput()); - } catch (IOException e) { - return immediateFailedFuture(new RuntimeException(e)); - } - ByteArrayOutputStream out = new ByteArrayOutputStream(); - for (String s : options.getRanges()) { - if (s.startsWith("-")) { - int length = Integer.parseInt(s.substring(1)); - out.write(data, data.length - length, length); - } else if (s.endsWith("-")) { - int offset = Integer.parseInt(s.substring(0, s.length() - 1)); - out.write(data, offset, data.length - offset); - } else if (s.contains("-")) { - String[] firstLast = s.split("\\-"); - int offset = Integer.parseInt(firstLast[0]); - int last = Integer.parseInt(firstLast[1]); - int length = (last < data.length) ? last + 1 : data.length - offset; - out.write(data, offset, length); - } else { - return immediateFailedFuture(new IllegalArgumentException("first and last were null!")); } } - ContentMetadata cmd = returnVal.getPayload().getContentMetadata(); - returnVal.setPayload(out.toByteArray()); - HttpUtils.copy(cmd, returnVal.getPayload().getContentMetadata()); - returnVal.getPayload().getContentMetadata().setContentLength(new Long(out.toByteArray().length)); + if (options.getIfUnmodifiedSince() != null) { + Date unmodifiedSince = options.getIfUnmodifiedSince(); + if (blob.getMetadata().getLastModified().after(unmodifiedSince)) { + HttpResponse response = new HttpResponse(412, null, null); + return immediateFailedFuture(new HttpResponseException(String.format("%1$s is after %2$s", blob + .getMetadata().getLastModified(), unmodifiedSince), null, response)); + } + } + blob = copyBlob(blob); + + if (options.getRanges() != null && options.getRanges().size() > 0) { + byte[] data; + try { + data = toByteArray(blob.getPayload().getInput()); + } catch (IOException e) { + return immediateFailedFuture(new RuntimeException(e)); + } + ByteArrayOutputStream out = new ByteArrayOutputStream(); + for (String s : options.getRanges()) { + if (s.startsWith("-")) { + int length = Integer.parseInt(s.substring(1)); + out.write(data, data.length - length, length); + } else if (s.endsWith("-")) { + int offset = Integer.parseInt(s.substring(0, s.length() - 1)); + out.write(data, offset, data.length - offset); + } else if (s.contains("-")) { + String[] firstLast = s.split("\\-"); + int offset = Integer.parseInt(firstLast[0]); + int last = Integer.parseInt(firstLast[1]); + int length = (last < data.length) ? last + 1 : data.length - offset; + out.write(data, offset, length); + } else { + return immediateFailedFuture(new IllegalArgumentException("first and last were null!")); + } + + } + ContentMetadata cmd = blob.getPayload().getContentMetadata(); + blob.setPayload(out.toByteArray()); + HttpUtils.copy(cmd, blob.getPayload().getContentMetadata()); + blob.getPayload().getContentMetadata().setContentLength(new Long(out.toByteArray().length)); + } } - checkNotNull(returnVal.getPayload(), "payload " + returnVal); - return immediateFuture(returnVal); + checkNotNull(blob.getPayload(), "payload " + blob); + return immediateFuture(blob); } /** @@ -633,7 +659,7 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore { } @Override - protected boolean deleteAndVerifyContainerGone(String container) { + protected boolean deleteAndVerifyContainerGone(final String container) { getContainerToBlobs().remove(container); return getContainerToBlobs().containsKey(container); } @@ -650,10 +676,9 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore { @Override public ListenableFuture createContainerInLocation(Location location, String container, - CreateContainerOptions options) { + CreateContainerOptions options) { if (options.isPublicRead()) throw new UnsupportedOperationException("publicRead"); return createContainerInLocation(location, container); } - }