EMMA Coverage Report (generated Mon Oct 17 05:41:20 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%  (619/1563)43%  (119.5/279)

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%  (483/1245)43%  (95.6/222)
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)
createContainerInLocation (Location, String): ListenableFuture 100% (1/1)84%  (27/32)78%  (3.9/5)
list (String, ListContainerOptions): ListenableFuture 100% (1/1)86%  (153/178)92%  (29.4/32)
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 * Licensed to jclouds, Inc. (jclouds) under one or more
3 * contributor license agreements.  See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership.  jclouds licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License.  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,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied.  See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
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 org.jclouds.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         return immediateFuture(Boolean.FALSE);
365      }
366      getContainerToBlobs().put(name, new ConcurrentHashMap<String, Blob>());
367      getContainerToLocation().put(name, location != null ? location : defaultLocation.get());
368      return immediateFuture(Boolean.TRUE);
369   }
370 
371   /**
372    * throws IllegalStateException if the container already exists
373    */
374   public ListenableFuture<Void> createContainerInLocationIfAbsent(final Location location, final String name) {
375      ConcurrentMap<String, Blob> container = getContainerToBlobs().putIfAbsent(name,
376               new ConcurrentHashMap<String, Blob>());
377      if (container == null) {
378         getContainerToLocation().put(name, location != null ? location : defaultLocation.get());
379         return immediateFuture((Void) null);
380      } else {
381         return Futures.immediateFailedFuture(new IllegalStateException("container " + name + " already exists"));
382      }
383   }
384 
385   public String getFirstQueryOrNull(String string, @Nullable HttpRequestOptions options) {
386      if (options == null)
387         return null;
388      Collection<String> values = options.buildQueryParameters().get(string);
389      return (values != null && values.size() >= 1) ? values.iterator().next() : null;
390   }
391 
392   protected static class DelimiterFilter implements Predicate<StorageMetadata> {
393      private final String prefix;
394      private final String delimiter;
395 
396      public DelimiterFilter(String prefix, String delimiter) {
397         this.prefix = prefix;
398         this.delimiter = delimiter;
399      }
400 
401      public boolean apply(StorageMetadata metadata) {
402         if (prefix == null)
403            return metadata.getName().indexOf(delimiter) == -1;
404         // ensure we don't accidentally append twice
405         String toMatch = prefix.endsWith("/") ? prefix : prefix + delimiter;
406         if (metadata.getName().startsWith(toMatch)) {
407            String unprefixedName = metadata.getName().replaceFirst(toMatch, "");
408            if (unprefixedName.equals("")) {
409               // we are the prefix in this case, return false
410               return false;
411            }
412            return unprefixedName.indexOf(delimiter) == -1;
413         }
414         return false;
415      }
416   }
417 
418   protected static class CommonPrefixes implements Function<StorageMetadata, String> {
419      private final String prefix;
420      private final String delimiter;
421      public static final String NO_PREFIX = "NO_PREFIX";
422 
423      public CommonPrefixes(String prefix, String delimiter) {
424         this.prefix = prefix;
425         this.delimiter = delimiter;
426      }
427 
428      public String apply(StorageMetadata metadata) {
429         String working = metadata.getName();
430         if (prefix != null) {
431            // ensure we don't accidentally append twice
432            String toMatch = prefix.endsWith("/") ? prefix : prefix + delimiter;
433            if (working.startsWith(toMatch)) {
434               working = working.replaceFirst(toMatch, "");
435            }
436         }
437         if (working.contains(delimiter)) {
438            return working.substring(0, working.indexOf(delimiter));
439         }
440         return NO_PREFIX;
441      }
442   }
443 
444   public static <T extends Comparable<?>> SortedSet<T> firstSliceOfSize(Iterable<T> elements, int size) {
445      List<List<T>> slices = partition(newArrayList(elements), size);
446      return newTreeSet(slices.get(0));
447   }
448 
449   public static HttpResponseException returnResponseException(int code) {
450      HttpResponse response = null;
451      response = new HttpResponse(code, null, null);
452      return new HttpResponseException(new HttpCommand() {
453 
454         public int getRedirectCount() {
455            return 0;
456         }
457 
458         public int incrementRedirectCount() {
459            return 0;
460         }
461 
462         public boolean isReplayable() {
463            return false;
464         }
465 
466         public Exception getException() {
467            return null;
468         }
469 
470         public int getFailureCount() {
471            return 0;
472         }
473 
474         public HttpRequest getCurrentRequest() {
475            return new HttpRequest("GET", URI.create("http://stub"));
476         }
477 
478         public int incrementFailureCount() {
479            return 0;
480         }
481 
482         public void setException(Exception exception) {
483 
484         }
485 
486         @Override
487         public void setCurrentRequest(HttpRequest request) {
488 
489         }
490 
491      }, response);
492   }
493 
494   /**
495    * {@inheritDoc}
496    */
497   @Override
498   public ListenableFuture<String> putBlob(String containerName, Blob in) {
499      checkArgument(containerName != null, "containerName must be set");
500      checkArgument(in != null, "blob must be set");
501      ConcurrentMap<String, Blob> container = getContainerToBlobs().get(containerName);
502      if (container == null) {
503         new IllegalStateException("containerName not found: " + containerName);
504      }
505 
506      Blob blob = createUpdatedCopyOfBlobInContainer(containerName, in);
507 
508      container.put(blob.getMetadata().getName(), blob);
509 
510      return immediateFuture(Iterables.getOnlyElement(blob.getAllHeaders().get(HttpHeaders.ETAG)));
511   }
512 
513   public ListenableFuture<Blob> putBlobAndReturnOld(String containerName, Blob in) {
514      ConcurrentMap<String, Blob> container = getContainerToBlobs().get(containerName);
515      if (container == null) {
516         new IllegalStateException("containerName not found: " + containerName);
517      }
518 
519      Blob blob = createUpdatedCopyOfBlobInContainer(containerName, in);
520 
521      Blob old = container.put(blob.getMetadata().getName(), blob);
522 
523      return immediateFuture(old);
524   }
525 
526   protected Blob createUpdatedCopyOfBlobInContainer(String containerName, Blob in) {
527      checkNotNull(in, "blob");
528      checkNotNull(in.getPayload(), "blob.payload");
529      ByteArrayPayload payload = (in.getPayload() instanceof ByteArrayPayload) ? ByteArrayPayload.class.cast(in
530               .getPayload()) : null;
531      if (payload == null)
532         payload = (in.getPayload() instanceof DelegatingPayload) ? (DelegatingPayload.class.cast(in.getPayload())
533                  .getDelegate() instanceof ByteArrayPayload) ? ByteArrayPayload.class.cast(DelegatingPayload.class
534                  .cast(in.getPayload()).getDelegate()) : null : null;
535      try {
536         if (payload == null || !(payload instanceof ByteArrayPayload)) {
537            MutableContentMetadata oldMd = in.getPayload().getContentMetadata();
538            ByteArrayOutputStream out = new ByteArrayOutputStream();
539            in.getPayload().writeTo(out);
540            payload = (ByteArrayPayload) Payloads.calculateMD5(Payloads.newPayload(out.toByteArray()));
541            HttpUtils.copy(oldMd, payload.getContentMetadata());
542         } else {
543            if (payload.getContentMetadata().getContentMD5() == null)
544               Payloads.calculateMD5(in, crypto.md5());
545         }
546      } catch (IOException e) {
547         Throwables.propagate(e);
548      }
549      Blob blob = blobFactory.create(copy(in.getMetadata()));
550      blob.setPayload(payload);
551      blob.getMetadata().setContainer(containerName);
552      blob.getMetadata().setUri(
553               uriBuilders.get().scheme("mem").host(containerName).path(in.getMetadata().getName()).build());
554      blob.getMetadata().setLastModified(new Date());
555      String eTag = CryptoStreams.hex(payload.getContentMetadata().getContentMD5());
556      blob.getMetadata().setETag(eTag);
557      // Set HTTP headers to match metadata
558      blob.getAllHeaders().replaceValues(HttpHeaders.LAST_MODIFIED,
559               Collections.singleton(dateService.rfc822DateFormat(blob.getMetadata().getLastModified())));
560      blob.getAllHeaders().replaceValues(HttpHeaders.ETAG, Collections.singleton(eTag));
561      copyPayloadHeadersToBlob(payload, blob);
562      blob.getAllHeaders().putAll(Multimaps.forMap(blob.getMetadata().getUserMetadata()));
563      return blob;
564   }
565 
566   private void copyPayloadHeadersToBlob(Payload payload, Blob blob) {
567      blob.getAllHeaders().putAll(HttpUtils.getContentHeadersFromMetadata(payload.getContentMetadata()));
568   }
569 
570   /**
571    * {@inheritDoc}
572    */
573   @Override
574   public ListenableFuture<Boolean> blobExists(final String containerName, final String key) {
575      if (!getContainerToBlobs().containsKey(containerName))
576         return immediateFailedFuture(cnfe(containerName));
577      Map<String, Blob> realContents = getContainerToBlobs().get(containerName);
578      return immediateFuture(realContents.containsKey(key));
579   }
580 
581   /**
582    * {@inheritDoc}
583    */
584   @Override
585   public ListenableFuture<Blob> getBlob(final String containerName, final String key, GetOptions options) {
586      if (!getContainerToBlobs().containsKey(containerName))
587         return immediateFailedFuture(cnfe(containerName));
588      Map<String, Blob> realContents = getContainerToBlobs().get(containerName);
589      if (!realContents.containsKey(key))
590         return immediateFuture(null);
591 
592      Blob object = realContents.get(key);
593 
594      if (options.getIfMatch() != null) {
595         if (!object.getMetadata().getETag().equals(options.getIfMatch()))
596            return immediateFailedFuture(returnResponseException(412));
597      }
598      if (options.getIfNoneMatch() != null) {
599         if (object.getMetadata().getETag().equals(options.getIfNoneMatch()))
600            return immediateFailedFuture(returnResponseException(304));
601      }
602      if (options.getIfModifiedSince() != null) {
603         Date modifiedSince = options.getIfModifiedSince();
604         if (object.getMetadata().getLastModified().before(modifiedSince)) {
605            HttpResponse response = new HttpResponse(304, null, null);
606            return immediateFailedFuture(new HttpResponseException(String.format("%1$s is before %2$s", object
607                     .getMetadata().getLastModified(), modifiedSince), null, response));
608         }
609 
610      }
611      if (options.getIfUnmodifiedSince() != null) {
612         Date unmodifiedSince = options.getIfUnmodifiedSince();
613         if (object.getMetadata().getLastModified().after(unmodifiedSince)) {
614            HttpResponse response = new HttpResponse(412, null, null);
615            return immediateFailedFuture(new HttpResponseException(String.format("%1$s is after %2$s", object
616                     .getMetadata().getLastModified(), unmodifiedSince), null, response));
617         }
618      }
619      Blob returnVal = copyBlob(object);
620 
621      if (options.getRanges() != null && options.getRanges().size() > 0) {
622         byte[] data;
623         try {
624            data = toByteArray(returnVal.getPayload().getInput());
625         } catch (IOException e) {
626            return immediateFailedFuture(new RuntimeException(e));
627         }
628         ByteArrayOutputStream out = new ByteArrayOutputStream();
629         for (String s : options.getRanges()) {
630            if (s.startsWith("-")) {
631               int length = Integer.parseInt(s.substring(1));
632               out.write(data, data.length - length, length);
633            } else if (s.endsWith("-")) {
634               int offset = Integer.parseInt(s.substring(0, s.length() - 1));
635               out.write(data, offset, data.length - offset);
636            } else if (s.contains("-")) {
637               String[] firstLast = s.split("\\-");
638               int offset = Integer.parseInt(firstLast[0]);
639               int last = Integer.parseInt(firstLast[1]);
640               int length = (last < data.length) ? last + 1 : data.length - offset;
641               out.write(data, offset, length);
642            } else {
643               return immediateFailedFuture(new IllegalArgumentException("first and last were null!"));
644            }
645 
646         }
647         ContentMetadata cmd = returnVal.getPayload().getContentMetadata();
648         returnVal.setPayload(out.toByteArray());
649         HttpUtils.copy(cmd, returnVal.getPayload().getContentMetadata());
650         returnVal.getPayload().getContentMetadata().setContentLength(new Long(out.toByteArray().length));
651      }
652      checkNotNull(returnVal.getPayload(), "payload " + returnVal);
653      return immediateFuture(returnVal);
654   }
655 
656   /**
657    * {@inheritDoc}
658    */
659   @Override
660   public ListenableFuture<BlobMetadata> blobMetadata(final String container, final String key) {
661      try {
662         Blob blob = getBlob(container, key).get();
663         return immediateFuture(blob != null ? (BlobMetadata) copy(blob.getMetadata()) : null);
664      } catch (Exception e) {
665         if (size(filter(getCausalChain(e), KeyNotFoundException.class)) >= 1)
666            return immediateFuture(null);
667         return immediateFailedFuture(e);
668      }
669   }
670 
671   private Blob copyBlob(Blob blob) {
672      Blob returnVal = blobFactory.create(copy(blob.getMetadata()));
673      returnVal.setPayload(blob.getPayload());
674      copyPayloadHeadersToBlob(blob.getPayload(), returnVal);
675      return returnVal;
676   }
677 
678   public ConcurrentMap<String, ConcurrentMap<String, Blob>> getContainerToBlobs() {
679      return containerToBlobs;
680   }
681 
682   @Override
683   protected boolean deleteAndVerifyContainerGone(String container) {
684      getContainerToBlobs().remove(container);
685      return getContainerToBlobs().containsKey(container);
686   }
687 
688   public ConcurrentMap<String, Location> getContainerToLocation() {
689      return containerToLocation;
690   }
691 
692   @Override
693   public ListenableFuture<String> putBlob(String container, Blob blob, PutOptions options) {
694      // TODO implement options
695      return putBlob(container, blob);
696   }
697 
698   @Override
699   public ListenableFuture<Boolean> createContainerInLocation(Location location, String container,
700            CreateContainerOptions options) {
701      if (options.isPublicRead())
702         throw new UnsupportedOperationException("publicRead");
703      return createContainerInLocation(location, container);
704   }
705 
706}

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