EMMA Coverage Report (generated Wed Jun 22 19:47:49 EDT 2011)
[all classes][org.jclouds.blobstore]

COVERAGE SUMMARY FOR SOURCE FILE [TransientAsyncBlobStore.java]

nameclass, %method, %block, %line, %
TransientAsyncBlobStore.java67%  (6/9)39%  (22/57)40%  (623/1564)43%  (119.4/278)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class TransientAsyncBlobStore$30%   (0/1)0%   (0/2)0%   (0/27)0%   (0/2)
TransientAsyncBlobStore$3 (TransientAsyncBlobStore, String): void 0%   (0/1)0%   (0/9)0%   (0/1)
apply (StorageMetadata): boolean 0%   (0/1)0%   (0/18)0%   (0/1)
     
class TransientAsyncBlobStore$50%   (0/1)0%   (0/2)0%   (0/26)0%   (0/6)
TransientAsyncBlobStore$5 (TransientAsyncBlobStore): void 0%   (0/1)0%   (0/6)0%   (0/1)
apply (String): StorageMetadata 0%   (0/1)0%   (0/20)0%   (0/5)
     
class TransientAsyncBlobStore$60%   (0/1)0%   (0/10)0%   (0/24)0%   (0/10)
TransientAsyncBlobStore$6 (): void 0%   (0/1)0%   (0/3)0%   (0/1)
getCurrentRequest (): HttpRequest 0%   (0/1)0%   (0/7)0%   (0/1)
getException (): Exception 0%   (0/1)0%   (0/2)0%   (0/1)
getFailureCount (): int 0%   (0/1)0%   (0/2)0%   (0/1)
getRedirectCount (): int 0%   (0/1)0%   (0/2)0%   (0/1)
incrementFailureCount (): int 0%   (0/1)0%   (0/2)0%   (0/1)
incrementRedirectCount (): int 0%   (0/1)0%   (0/2)0%   (0/1)
isReplayable (): boolean 0%   (0/1)0%   (0/2)0%   (0/1)
setCurrentRequest (HttpRequest): void 0%   (0/1)0%   (0/1)0%   (0/1)
setException (Exception): void 0%   (0/1)0%   (0/1)0%   (0/1)
     
class TransientAsyncBlobStore$DelimiterFilter100% (1/1)100% (2/2)31%  (22/71)45%  (5.9/13)
apply (StorageMetadata): boolean 100% (1/1)21%  (13/62)21%  (1.9/9)
TransientAsyncBlobStore$DelimiterFilter (String, String): void 100% (1/1)100% (9/9)100% (4/4)
     
class TransientAsyncBlobStore$4100% (1/1)50%  (1/2)33%  (6/18)20%  (1/5)
apply (String): StorageMetadata 0%   (0/1)0%   (0/12)0%   (0/4)
TransientAsyncBlobStore$4 (TransientAsyncBlobStore): void 100% (1/1)100% (6/6)100% (1/1)
     
class TransientAsyncBlobStore$CommonPrefixes100% (1/1)100% (2/2)38%  (22/58)67%  (8/12)
apply (StorageMetadata): String 100% (1/1)27%  (13/49)50%  (4/8)
TransientAsyncBlobStore$CommonPrefixes (String, String): void 100% (1/1)100% (9/9)100% (4/4)
     
class TransientAsyncBlobStore100% (1/1)39%  (13/33)39%  (487/1246)43%  (95.6/221)
blobExists (String, String): ListenableFuture 0%   (0/1)0%   (0/22)0%   (0/4)
blobMetadata (String, String): ListenableFuture 0%   (0/1)0%   (0/30)0%   (0/6)
cnfe (String): ContainerNotFoundException 0%   (0/1)0%   (0/19)0%   (0/1)
containerExists (String): ListenableFuture 0%   (0/1)0%   (0/7)0%   (0/1)
copy (MutableBlobMetadata, String): MutableBlobMetadata 0%   (0/1)0%   (0/8)0%   (0/3)
copyBlob (Blob): Blob 0%   (0/1)0%   (0/18)0%   (0/4)
create (): MutableStorageMetadata 0%   (0/1)0%   (0/4)0%   (0/1)
createContainerInLocation (Location, String, CreateContainerOptions): Listena... 0%   (0/1)0%   (0/13)0%   (0/3)
createContainerInLocationIfAbsent (Location, String): ListenableFuture 0%   (0/1)0%   (0/43)0%   (0/5)
deleteAndVerifyContainerGone (String): boolean 0%   (0/1)0%   (0/10)0%   (0/2)
deleteContainer (String): ListenableFuture 0%   (0/1)0%   (0/13)0%   (0/3)
deleteContainerImpl (String): ListenableFuture 0%   (0/1)0%   (0/27)0%   (0/6)
getBlob (String, String, GetOptions): ListenableFuture 0%   (0/1)0%   (0/298)0%   (0/50)
getFirstQueryOrNull (String, HttpRequestOptions): String 0%   (0/1)0%   (0/22)0%   (0/4)
list (): ListenableFuture 0%   (0/1)0%   (0/14)0%   (0/1)
putBlob (String, Blob, PutOptions): ListenableFuture 0%   (0/1)0%   (0/5)0%   (0/1)
putBlobAndReturnOld (String, Blob): ListenableFuture 0%   (0/1)0%   (0/36)0%   (0/6)
removeBlob (String, String): ListenableFuture 0%   (0/1)0%   (0/16)0%   (0/3)
removeBlobAndReturnOld (String, String): ListenableFuture 0%   (0/1)0%   (0/17)0%   (0/3)
returnResponseException (int): HttpResponseException 0%   (0/1)0%   (0/17)0%   (0/3)
convertUserMetadataKeysToLowercase (MutableBlobMetadata): void 100% (1/1)50%  (14/28)68%  (3.4/5)
copy (MutableBlobMetadata): MutableBlobMetadata 100% (1/1)63%  (34/54)67%  (8/12)
putBlob (String, Blob): ListenableFuture 100% (1/1)74%  (39/53)85%  (6.8/8)
<static initializer> 100% (1/1)75%  (6/8)75%  (0.8/1)
createUpdatedCopyOfBlobInContainer (String, Blob): Blob 100% (1/1)75%  (129/172)81%  (23.4/29)
list (String, ListContainerOptions): ListenableFuture 100% (1/1)86%  (153/178)92%  (29.4/32)
createContainerInLocation (Location, String): ListenableFuture 100% (1/1)94%  (31/33)98%  (3.9/4)
TransientAsyncBlobStore (BlobStoreContext, DateService, Crypto, ConcurrentMap... 100% (1/1)100% (47/47)100% (12/12)
clearContainer (String): ListenableFuture 100% (1/1)100% (9/9)100% (2/2)
copyPayloadHeadersToBlob (Payload, Blob): void 100% (1/1)100% (8/8)100% (2/2)
firstSliceOfSize (Iterable, int): SortedSet 100% (1/1)100% (11/11)100% (2/2)
getContainerToBlobs (): ConcurrentMap 100% (1/1)100% (3/3)100% (1/1)
getContainerToLocation (): ConcurrentMap 100% (1/1)100% (3/3)100% (1/1)
     
class TransientAsyncBlobStore$1100% (1/1)100% (2/2)90%  (71/79)79%  (7.9/10)
apply (String): StorageMetadata 100% (1/1)88%  (59/67)77%  (6.9/9)
TransientAsyncBlobStore$1 (TransientAsyncBlobStore, Map, String): void 100% (1/1)100% (12/12)100% (1/1)
     
class TransientAsyncBlobStore$2100% (1/1)100% (2/2)100% (15/15)100% (2/2)
TransientAsyncBlobStore$2 (TransientAsyncBlobStore, String): void 100% (1/1)100% (9/9)100% (1/1)
apply (StorageMetadata): boolean 100% (1/1)100% (6/6)100% (1/1)

1/**
2 *
3 * Copyright (C) 2011 Cloud Conscious, LLC. <info@cloudconscious.com>
4 *
5 * ====================================================================
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 * ====================================================================
18 */
19package org.jclouds.blobstore;
20 
21import static com.google.common.base.Preconditions.checkArgument;
22import static com.google.common.base.Preconditions.checkNotNull;
23import static com.google.common.base.Preconditions.checkState;
24import static com.google.common.base.Throwables.getCausalChain;
25import static com.google.common.base.Throwables.propagate;
26import static com.google.common.collect.Iterables.filter;
27import static com.google.common.collect.Iterables.find;
28import static com.google.common.collect.Iterables.size;
29import static com.google.common.collect.Iterables.transform;
30import static com.google.common.collect.Lists.newArrayList;
31import static com.google.common.collect.Lists.partition;
32import static com.google.common.collect.Maps.newHashMap;
33import static com.google.common.collect.Sets.filter;
34import static com.google.common.collect.Sets.newTreeSet;
35import static com.google.common.io.ByteStreams.toByteArray;
36import static com.google.common.util.concurrent.Futures.immediateFailedFuture;
37import static com.google.common.util.concurrent.Futures.immediateFuture;
38 
39import java.io.ByteArrayInputStream;
40import java.io.ByteArrayOutputStream;
41import java.io.IOException;
42import java.io.ObjectInput;
43import java.io.ObjectInputStream;
44import java.io.ObjectOutput;
45import java.io.ObjectOutputStream;
46import java.net.URI;
47import java.util.Collection;
48import java.util.Collections;
49import java.util.Date;
50import java.util.List;
51import java.util.Map;
52import java.util.Set;
53import java.util.SortedSet;
54import java.util.TreeSet;
55import java.util.Map.Entry;
56import java.util.concurrent.ConcurrentHashMap;
57import java.util.concurrent.ConcurrentMap;
58import java.util.concurrent.ExecutorService;
59 
60import javax.annotation.Nullable;
61import javax.inject.Inject;
62import javax.inject.Named;
63import javax.inject.Provider;
64import javax.ws.rs.core.HttpHeaders;
65import javax.ws.rs.core.UriBuilder;
66 
67import org.jclouds.Constants;
68import org.jclouds.blobstore.domain.Blob;
69import org.jclouds.blobstore.domain.BlobMetadata;
70import org.jclouds.blobstore.domain.MutableBlobMetadata;
71import org.jclouds.blobstore.domain.MutableStorageMetadata;
72import org.jclouds.blobstore.domain.PageSet;
73import org.jclouds.blobstore.domain.StorageMetadata;
74import org.jclouds.blobstore.domain.StorageType;
75import org.jclouds.blobstore.domain.Blob.Factory;
76import org.jclouds.blobstore.domain.internal.MutableStorageMetadataImpl;
77import org.jclouds.blobstore.domain.internal.PageSetImpl;
78import org.jclouds.blobstore.functions.HttpGetOptionsListToGetOptions;
79import org.jclouds.blobstore.internal.BaseAsyncBlobStore;
80import org.jclouds.blobstore.options.CreateContainerOptions;
81import org.jclouds.blobstore.options.GetOptions;
82import org.jclouds.blobstore.options.ListContainerOptions;
83import org.jclouds.blobstore.options.PutOptions;
84import org.jclouds.blobstore.strategy.IfDirectoryReturnNameStrategy;
85import org.jclouds.blobstore.util.BlobUtils;
86import org.jclouds.collect.Memoized;
87import org.jclouds.crypto.Crypto;
88import org.jclouds.crypto.CryptoStreams;
89import org.jclouds.date.DateService;
90import org.jclouds.domain.Location;
91import org.jclouds.http.HttpCommand;
92import org.jclouds.http.HttpRequest;
93import org.jclouds.http.HttpResponse;
94import org.jclouds.http.HttpResponseException;
95import org.jclouds.http.HttpUtils;
96import org.jclouds.http.options.HttpRequestOptions;
97import org.jclouds.io.ContentMetadata;
98import org.jclouds.io.MutableContentMetadata;
99import org.jclouds.io.Payload;
100import org.jclouds.io.Payloads;
101import org.jclouds.io.payloads.ByteArrayPayload;
102import org.jclouds.io.payloads.DelegatingPayload;
103 
104import com.google.common.base.Function;
105import com.google.common.base.Predicate;
106import com.google.common.base.Supplier;
107import com.google.common.base.Throwables;
108import com.google.common.collect.Iterables;
109import com.google.common.collect.Multimaps;
110import com.google.common.util.concurrent.Futures;
111import com.google.common.util.concurrent.ListenableFuture;
112 
113/**
114 * Implementation of {@link BaseAsyncBlobStore} which keeps all data in a local Map object.
115 * 
116 * @author Adrian Cole
117 * @author James Murty
118 */
119public class TransientAsyncBlobStore extends BaseAsyncBlobStore {
120 
121   protected final DateService dateService;
122   protected final Crypto crypto;
123   protected final ConcurrentMap<String, ConcurrentMap<String, Blob>> containerToBlobs;
124   protected final Provider<UriBuilder> uriBuilders;
125   protected final ConcurrentMap<String, Location> containerToLocation;
126   protected final HttpGetOptionsListToGetOptions httpGetOptionsConverter;
127   protected final IfDirectoryReturnNameStrategy ifDirectoryReturnName;
128   protected final Factory blobFactory;
129 
130   @Inject
131   protected TransientAsyncBlobStore(BlobStoreContext context, DateService dateService, Crypto crypto,
132            ConcurrentMap<String, ConcurrentMap<String, Blob>> containerToBlobs, Provider<UriBuilder> uriBuilders,
133            ConcurrentMap<String, Location> containerToLocation,
134            HttpGetOptionsListToGetOptions httpGetOptionsConverter,
135            IfDirectoryReturnNameStrategy ifDirectoryReturnName, Factory blobFactory, BlobUtils blobUtils,
136            @Named(Constants.PROPERTY_USER_THREADS) ExecutorService service, Supplier<Location> defaultLocation,
137            @Memoized Supplier<Set<? extends Location>> locations) {
138      super(context, blobUtils, service, defaultLocation, locations);
139      this.blobFactory = blobFactory;
140      this.dateService = dateService;
141      this.crypto = crypto;
142      this.containerToBlobs = containerToBlobs;
143      this.uriBuilders = uriBuilders;
144      this.containerToLocation = containerToLocation;
145      this.httpGetOptionsConverter = httpGetOptionsConverter;
146      this.ifDirectoryReturnName = ifDirectoryReturnName;
147      getContainerToLocation().put("stub", defaultLocation.get());
148      getContainerToBlobs().put("stub", new ConcurrentHashMap<String, Blob>());
149   }
150 
151   /**
152    * default maxResults is 1000
153    */
154   @Override
155   public ListenableFuture<PageSet<? extends StorageMetadata>> list(final String container, ListContainerOptions options) {
156      final Map<String, Blob> realContents = getContainerToBlobs().get(container);
157 
158      if (realContents == null)
159         return immediateFailedFuture(cnfe(container));
160 
161      SortedSet<StorageMetadata> contents = newTreeSet(transform(realContents.keySet(),
162               new Function<String, StorageMetadata>() {
163                  public StorageMetadata apply(String key) {
164                     Blob oldBlob = realContents.get(key);
165                     checkState(oldBlob != null, "blob " + key + " is not present although it was in the list of "
166                              + container);
167                     checkState(oldBlob.getMetadata() != null, "blob " + container + "/" + key + " has no metadata");
168                     MutableBlobMetadata md = copy(oldBlob.getMetadata());
169                     String directoryName = ifDirectoryReturnName.execute(md);
170                     if (directoryName != null) {
171                        md.setName(directoryName);
172                        md.setType(StorageType.RELATIVE_PATH);
173                     }
174                     return md;
175                  }
176               }));
177 
178      if (options.getMarker() != null) {
179         final String finalMarker = options.getMarker();
180         StorageMetadata lastMarkerMetadata = find(contents, new Predicate<StorageMetadata>() {
181            public boolean apply(StorageMetadata metadata) {
182               return metadata.getName().equals(finalMarker);
183            }
184         });
185         contents = contents.tailSet(lastMarkerMetadata);
186         contents.remove(lastMarkerMetadata);
187      }
188 
189      final String prefix = options.getDir();
190      if (prefix != null) {
191         contents = newTreeSet(filter(contents, new Predicate<StorageMetadata>() {
192            public boolean apply(StorageMetadata o) {
193               return (o != null && o.getName().startsWith(prefix) && !o.getName().equals(prefix));
194            }
195         }));
196      }
197 
198      String marker = null;
199      Integer maxResults = options.getMaxResults() != null ? options.getMaxResults() : 1000;
200      if (contents.size() > 0) {
201         SortedSet<StorageMetadata> contentsSlice = firstSliceOfSize(contents, maxResults);
202         if (!contentsSlice.contains(contents.last())) {
203            // Partial listing
204            marker = contentsSlice.last().getName();
205         } else {
206            marker = null;
207         }
208         contents = contentsSlice;
209      }
210 
211      final String delimiter = options.isRecursive() ? null : "/";
212      if (delimiter != null) {
213         SortedSet<String> commonPrefixes = null;
214         Iterable<String> iterable = transform(contents, new CommonPrefixes(prefix != null ? prefix : null, delimiter));
215         commonPrefixes = iterable != null ? newTreeSet(iterable) : new TreeSet<String>();
216         commonPrefixes.remove(CommonPrefixes.NO_PREFIX);
217 
218         contents = newTreeSet(filter(contents, new DelimiterFilter(prefix != null ? prefix : null, delimiter)));
219 
220         Iterables.<StorageMetadata> addAll(contents, transform(commonPrefixes,
221                  new Function<String, StorageMetadata>() {
222                     public StorageMetadata apply(String o) {
223                        MutableStorageMetadata md = new MutableStorageMetadataImpl();
224                        md.setType(StorageType.RELATIVE_PATH);
225                        md.setName(o);
226                        return md;
227                     }
228                  }));
229      }
230 
231      // trim metadata, if the response isn't supposed to be detailed.
232      if (!options.isDetailed()) {
233         for (StorageMetadata md : contents) {
234            md.getUserMetadata().clear();
235         }
236      }
237 
238      return Futures.<PageSet<? extends StorageMetadata>> immediateFuture(new PageSetImpl<StorageMetadata>(contents,
239               marker));
240 
241   }
242 
243   private ContainerNotFoundException cnfe(final String name) {
244      return new ContainerNotFoundException(name, String.format("container %s not in %s", name, getContainerToBlobs()
245               .keySet()));
246   }
247 
248   public static MutableBlobMetadata copy(MutableBlobMetadata in) {
249      ByteArrayOutputStream bout = new ByteArrayOutputStream();
250      ObjectOutput os;
251      try {
252         os = new ObjectOutputStream(bout);
253         os.writeObject(in);
254         ObjectInput is = new ObjectInputStream(new ByteArrayInputStream(bout.toByteArray()));
255         MutableBlobMetadata out = (MutableBlobMetadata) is.readObject();
256         convertUserMetadataKeysToLowercase(out);
257         HttpUtils.copy(in.getContentMetadata(), out.getContentMetadata());
258         return out;
259      } catch (Exception e) {
260         propagate(e);
261         assert false : "exception should have propagated: " + e;
262         return null;
263      }
264   }
265 
266   private static void convertUserMetadataKeysToLowercase(MutableBlobMetadata metadata) {
267      Map<String, String> lowerCaseUserMetadata = newHashMap();
268      for (Entry<String, String> entry : metadata.getUserMetadata().entrySet()) {
269         lowerCaseUserMetadata.put(entry.getKey().toLowerCase(), entry.getValue());
270      }
271      metadata.setUserMetadata(lowerCaseUserMetadata);
272   }
273 
274   public static MutableBlobMetadata copy(MutableBlobMetadata in, String newKey) {
275      MutableBlobMetadata newMd = copy(in);
276      newMd.setName(newKey);
277      return newMd;
278   }
279 
280   /**
281    * {@inheritDoc}
282    */
283   @Override
284   public ListenableFuture<Void> removeBlob(final String container, final String key) {
285      if (getContainerToBlobs().containsKey(container)) {
286         getContainerToBlobs().get(container).remove(key);
287      }
288      return immediateFuture(null);
289   }
290 
291   public ListenableFuture<Blob> removeBlobAndReturnOld(String container, String key) {
292      if (getContainerToBlobs().containsKey(container)) {
293         return immediateFuture(getContainerToBlobs().get(container).remove(key));
294      }
295      return immediateFuture(null);
296   }
297 
298   /**
299    * {@inheritDoc}
300    */
301   @Override
302   public ListenableFuture<Void> clearContainer(final String container) {
303      getContainerToBlobs().get(container).clear();
304      return immediateFuture(null);
305   }
306 
307   /**
308    * {@inheritDoc}
309    */
310   @Override
311   public ListenableFuture<Void> deleteContainer(final String container) {
312      if (getContainerToBlobs().containsKey(container)) {
313         getContainerToBlobs().remove(container);
314      }
315      return immediateFuture(null);
316   }
317 
318   public ListenableFuture<Boolean> deleteContainerImpl(final String container) {
319      Boolean returnVal = true;
320      if (getContainerToBlobs().containsKey(container)) {
321         if (getContainerToBlobs().get(container).size() == 0)
322            getContainerToBlobs().remove(container);
323         else
324            returnVal = false;
325      }
326      return immediateFuture(returnVal);
327   }
328 
329   /**
330    * {@inheritDoc}
331    */
332   @Override
333   public ListenableFuture<Boolean> containerExists(final String container) {
334      return immediateFuture(getContainerToBlobs().containsKey(container));
335   }
336 
337   /**
338    * {@inheritDoc}
339    */
340   @Override
341   public ListenableFuture<PageSet<? extends StorageMetadata>> list() {
342      return Futures.<PageSet<? extends StorageMetadata>> immediateFuture(new PageSetImpl<StorageMetadata>(transform(
343               getContainerToBlobs().keySet(), new Function<String, StorageMetadata>() {
344                  public StorageMetadata apply(String name) {
345                     MutableStorageMetadata cmd = create();
346                     cmd.setName(name);
347                     cmd.setType(StorageType.CONTAINER);
348                     cmd.setLocation(getContainerToLocation().get(name));
349                     return cmd;
350                  }
351               }), null));
352   }
353 
354   protected MutableStorageMetadata create() {
355      return new MutableStorageMetadataImpl();
356   }
357 
358   /**
359    * {@inheritDoc}
360    */
361   @Override
362   public ListenableFuture<Boolean> createContainerInLocation(final Location location, final String name) {
363      if (!getContainerToBlobs().containsKey(name)) {
364         getContainerToBlobs().put(name, new ConcurrentHashMap<String, Blob>());
365         getContainerToLocation().put(name, location != null ? location : defaultLocation.get());
366      }
367      return immediateFuture(getContainerToBlobs().containsKey(name));
368   }
369 
370   /**
371    * throws IllegalStateException if the container already exists
372    */
373   public ListenableFuture<Void> createContainerInLocationIfAbsent(final Location location, final String name) {
374      ConcurrentMap<String, Blob> container = getContainerToBlobs().putIfAbsent(name,
375               new ConcurrentHashMap<String, Blob>());
376      if (container == null) {
377         getContainerToLocation().put(name, location != null ? location : defaultLocation.get());
378         return immediateFuture((Void) null);
379      } else {
380         return Futures.immediateFailedFuture(new IllegalStateException("container " + name + " already exists"));
381      }
382   }
383 
384   public String getFirstQueryOrNull(String string, @Nullable HttpRequestOptions options) {
385      if (options == null)
386         return null;
387      Collection<String> values = options.buildQueryParameters().get(string);
388      return (values != null && values.size() >= 1) ? values.iterator().next() : null;
389   }
390 
391   protected static class DelimiterFilter implements Predicate<StorageMetadata> {
392      private final String prefix;
393      private final String delimiter;
394 
395      public DelimiterFilter(String prefix, String delimiter) {
396         this.prefix = prefix;
397         this.delimiter = delimiter;
398      }
399 
400      public boolean apply(StorageMetadata metadata) {
401         if (prefix == null)
402            return metadata.getName().indexOf(delimiter) == -1;
403         // ensure we don't accidentally append twice
404         String toMatch = prefix.endsWith("/") ? prefix : prefix + delimiter;
405         if (metadata.getName().startsWith(toMatch)) {
406            String unprefixedName = metadata.getName().replaceFirst(toMatch, "");
407            if (unprefixedName.equals("")) {
408               // we are the prefix in this case, return false
409               return false;
410            }
411            return unprefixedName.indexOf(delimiter) == -1;
412         }
413         return false;
414      }
415   }
416 
417   protected static class CommonPrefixes implements Function<StorageMetadata, String> {
418      private final String prefix;
419      private final String delimiter;
420      public static final String NO_PREFIX = "NO_PREFIX";
421 
422      public CommonPrefixes(String prefix, String delimiter) {
423         this.prefix = prefix;
424         this.delimiter = delimiter;
425      }
426 
427      public String apply(StorageMetadata metadata) {
428         String working = metadata.getName();
429         if (prefix != null) {
430            // ensure we don't accidentally append twice
431            String toMatch = prefix.endsWith("/") ? prefix : prefix + delimiter;
432            if (working.startsWith(toMatch)) {
433               working = working.replaceFirst(toMatch, "");
434            }
435         }
436         if (working.contains(delimiter)) {
437            return working.substring(0, working.indexOf(delimiter));
438         }
439         return NO_PREFIX;
440      }
441   }
442 
443   public static <T extends Comparable<?>> SortedSet<T> firstSliceOfSize(Iterable<T> elements, int size) {
444      List<List<T>> slices = partition(newArrayList(elements), size);
445      return newTreeSet(slices.get(0));
446   }
447 
448   public static HttpResponseException returnResponseException(int code) {
449      HttpResponse response = null;
450      response = new HttpResponse(code, null, null);
451      return new HttpResponseException(new HttpCommand() {
452 
453         public int getRedirectCount() {
454            return 0;
455         }
456 
457         public int incrementRedirectCount() {
458            return 0;
459         }
460 
461         public boolean isReplayable() {
462            return false;
463         }
464 
465         public Exception getException() {
466            return null;
467         }
468 
469         public int getFailureCount() {
470            return 0;
471         }
472 
473         public HttpRequest getCurrentRequest() {
474            return new HttpRequest("GET", URI.create("http://stub"));
475         }
476 
477         public int incrementFailureCount() {
478            return 0;
479         }
480 
481         public void setException(Exception exception) {
482 
483         }
484 
485         @Override
486         public void setCurrentRequest(HttpRequest request) {
487 
488         }
489 
490      }, response);
491   }
492 
493   /**
494    * {@inheritDoc}
495    */
496   @Override
497   public ListenableFuture<String> putBlob(String containerName, Blob in) {
498      checkArgument(containerName != null, "containerName must be set");
499      checkArgument(in != null, "blob must be set");
500      ConcurrentMap<String, Blob> container = getContainerToBlobs().get(containerName);
501      if (container == null) {
502         new IllegalStateException("containerName not found: " + containerName);
503      }
504 
505      Blob blob = createUpdatedCopyOfBlobInContainer(containerName, in);
506 
507      container.put(blob.getMetadata().getName(), blob);
508 
509      return immediateFuture(Iterables.getOnlyElement(blob.getAllHeaders().get(HttpHeaders.ETAG)));
510   }
511 
512   public ListenableFuture<Blob> putBlobAndReturnOld(String containerName, Blob in) {
513      ConcurrentMap<String, Blob> container = getContainerToBlobs().get(containerName);
514      if (container == null) {
515         new IllegalStateException("containerName not found: " + containerName);
516      }
517 
518      Blob blob = createUpdatedCopyOfBlobInContainer(containerName, in);
519 
520      Blob old = container.put(blob.getMetadata().getName(), blob);
521 
522      return immediateFuture(old);
523   }
524 
525   protected Blob createUpdatedCopyOfBlobInContainer(String containerName, Blob in) {
526      checkNotNull(in, "blob");
527      checkNotNull(in.getPayload(), "blob.payload");
528      ByteArrayPayload payload = (in.getPayload() instanceof ByteArrayPayload) ? ByteArrayPayload.class.cast(in
529               .getPayload()) : null;
530      if (payload == null)
531         payload = (in.getPayload() instanceof DelegatingPayload) ? (DelegatingPayload.class.cast(in.getPayload())
532                  .getDelegate() instanceof ByteArrayPayload) ? ByteArrayPayload.class.cast(DelegatingPayload.class
533                  .cast(in.getPayload()).getDelegate()) : null : null;
534      try {
535         if (payload == null || !(payload instanceof ByteArrayPayload)) {
536            MutableContentMetadata oldMd = in.getPayload().getContentMetadata();
537            ByteArrayOutputStream out = new ByteArrayOutputStream();
538            in.getPayload().writeTo(out);
539            payload = (ByteArrayPayload) Payloads.calculateMD5(Payloads.newPayload(out.toByteArray()));
540            HttpUtils.copy(oldMd, payload.getContentMetadata());
541         } else {
542            if (payload.getContentMetadata().getContentMD5() == null)
543               Payloads.calculateMD5(in, crypto.md5());
544         }
545      } catch (IOException e) {
546         Throwables.propagate(e);
547      }
548      Blob blob = blobFactory.create(copy(in.getMetadata()));
549      blob.setPayload(payload);
550      blob.getMetadata().setContainer(containerName);
551      blob.getMetadata().setUri(
552               uriBuilders.get().scheme("mem").host(containerName).path(in.getMetadata().getName()).build());
553      blob.getMetadata().setLastModified(new Date());
554      String eTag = CryptoStreams.hex(payload.getContentMetadata().getContentMD5());
555      blob.getMetadata().setETag(eTag);
556      // Set HTTP headers to match metadata
557      blob.getAllHeaders().replaceValues(HttpHeaders.LAST_MODIFIED,
558               Collections.singleton(dateService.rfc822DateFormat(blob.getMetadata().getLastModified())));
559      blob.getAllHeaders().replaceValues(HttpHeaders.ETAG, Collections.singleton(eTag));
560      copyPayloadHeadersToBlob(payload, blob);
561      blob.getAllHeaders().putAll(Multimaps.forMap(blob.getMetadata().getUserMetadata()));
562      return blob;
563   }
564 
565   private void copyPayloadHeadersToBlob(Payload payload, Blob blob) {
566      blob.getAllHeaders().putAll(HttpUtils.getContentHeadersFromMetadata(payload.getContentMetadata()));
567   }
568 
569   /**
570    * {@inheritDoc}
571    */
572   @Override
573   public ListenableFuture<Boolean> blobExists(final String containerName, final String key) {
574      if (!getContainerToBlobs().containsKey(containerName))
575         return immediateFailedFuture(cnfe(containerName));
576      Map<String, Blob> realContents = getContainerToBlobs().get(containerName);
577      return immediateFuture(realContents.containsKey(key));
578   }
579 
580   /**
581    * {@inheritDoc}
582    */
583   @Override
584   public ListenableFuture<Blob> getBlob(final String containerName, final String key, GetOptions options) {
585      if (!getContainerToBlobs().containsKey(containerName))
586         return immediateFailedFuture(cnfe(containerName));
587      Map<String, Blob> realContents = getContainerToBlobs().get(containerName);
588      if (!realContents.containsKey(key))
589         return immediateFuture(null);
590 
591      Blob object = realContents.get(key);
592 
593      if (options.getIfMatch() != null) {
594         if (!object.getMetadata().getETag().equals(options.getIfMatch()))
595            return immediateFailedFuture(returnResponseException(412));
596      }
597      if (options.getIfNoneMatch() != null) {
598         if (object.getMetadata().getETag().equals(options.getIfNoneMatch()))
599            return immediateFailedFuture(returnResponseException(304));
600      }
601      if (options.getIfModifiedSince() != null) {
602         Date modifiedSince = options.getIfModifiedSince();
603         if (object.getMetadata().getLastModified().before(modifiedSince)) {
604            HttpResponse response = new HttpResponse(304, null, null);
605            return immediateFailedFuture(new HttpResponseException(String.format("%1$s is before %2$s", object
606                     .getMetadata().getLastModified(), modifiedSince), null, response));
607         }
608 
609      }
610      if (options.getIfUnmodifiedSince() != null) {
611         Date unmodifiedSince = options.getIfUnmodifiedSince();
612         if (object.getMetadata().getLastModified().after(unmodifiedSince)) {
613            HttpResponse response = new HttpResponse(412, null, null);
614            return immediateFailedFuture(new HttpResponseException(String.format("%1$s is after %2$s", object
615                     .getMetadata().getLastModified(), unmodifiedSince), null, response));
616         }
617      }
618      Blob returnVal = copyBlob(object);
619 
620      if (options.getRanges() != null && options.getRanges().size() > 0) {
621         byte[] data;
622         try {
623            data = toByteArray(returnVal.getPayload().getInput());
624         } catch (IOException e) {
625            return immediateFailedFuture(new RuntimeException(e));
626         }
627         ByteArrayOutputStream out = new ByteArrayOutputStream();
628         for (String s : options.getRanges()) {
629            if (s.startsWith("-")) {
630               int length = Integer.parseInt(s.substring(1));
631               out.write(data, data.length - length, length);
632            } else if (s.endsWith("-")) {
633               int offset = Integer.parseInt(s.substring(0, s.length() - 1));
634               out.write(data, offset, data.length - offset);
635            } else if (s.contains("-")) {
636               String[] firstLast = s.split("\\-");
637               int offset = Integer.parseInt(firstLast[0]);
638               int last = Integer.parseInt(firstLast[1]);
639               int length = (last < data.length) ? last + 1 : data.length - offset;
640               out.write(data, offset, length);
641            } else {
642               return immediateFailedFuture(new IllegalArgumentException("first and last were null!"));
643            }
644 
645         }
646         ContentMetadata cmd = returnVal.getPayload().getContentMetadata();
647         returnVal.setPayload(out.toByteArray());
648         HttpUtils.copy(cmd, returnVal.getPayload().getContentMetadata());
649         returnVal.getPayload().getContentMetadata().setContentLength(new Long(out.toByteArray().length));
650      }
651      checkNotNull(returnVal.getPayload(), "payload " + returnVal);
652      return immediateFuture(returnVal);
653   }
654 
655   /**
656    * {@inheritDoc}
657    */
658   @Override
659   public ListenableFuture<BlobMetadata> blobMetadata(final String container, final String key) {
660      try {
661         Blob blob = getBlob(container, key).get();
662         return immediateFuture(blob != null ? (BlobMetadata) copy(blob.getMetadata()) : null);
663      } catch (Exception e) {
664         if (size(filter(getCausalChain(e), KeyNotFoundException.class)) >= 1)
665            return immediateFuture(null);
666         return immediateFailedFuture(e);
667      }
668   }
669 
670   private Blob copyBlob(Blob blob) {
671      Blob returnVal = blobFactory.create(copy(blob.getMetadata()));
672      returnVal.setPayload(blob.getPayload());
673      copyPayloadHeadersToBlob(blob.getPayload(), returnVal);
674      return returnVal;
675   }
676 
677   public ConcurrentMap<String, ConcurrentMap<String, Blob>> getContainerToBlobs() {
678      return containerToBlobs;
679   }
680 
681   @Override
682   protected boolean deleteAndVerifyContainerGone(String container) {
683      getContainerToBlobs().remove(container);
684      return getContainerToBlobs().containsKey(container);
685   }
686 
687   public ConcurrentMap<String, Location> getContainerToLocation() {
688      return containerToLocation;
689   }
690 
691   @Override
692   public ListenableFuture<String> putBlob(String container, Blob blob, PutOptions options) {
693      // TODO implement options
694      return putBlob(container, blob);
695   }
696 
697   @Override
698   public ListenableFuture<Boolean> createContainerInLocation(Location location, String container,
699            CreateContainerOptions options) {
700      if (options.isPublicRead())
701         throw new UnsupportedOperationException("publicRead");
702      return createContainerInLocation(location, container);
703   }
704 
705}

[all classes][org.jclouds.blobstore]
EMMA 2.0.5312 (C) Vladimir Roubtsov