EMMA Coverage Report (generated Mon Oct 17 05:41:20 EDT 2011)
[all classes][org.jclouds.filesystem]

COVERAGE SUMMARY FOR SOURCE FILE [FilesystemAsyncBlobStore.java]

nameclass, %method, %block, %line, %
FilesystemAsyncBlobStore.java44%  (4/9)55%  (28/51)56%  (720/1287)56%  (132.8/237)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class FilesystemAsyncBlobStore$20%   (0/1)0%   (0/2)0%   (0/15)0%   (0/2)
FilesystemAsyncBlobStore$2 (FilesystemAsyncBlobStore, String): void 0%   (0/1)0%   (0/9)0%   (0/1)
apply (StorageMetadata): boolean 0%   (0/1)0%   (0/6)0%   (0/1)
     
class FilesystemAsyncBlobStore$40%   (0/1)0%   (0/2)0%   (0/18)0%   (0/5)
FilesystemAsyncBlobStore$4 (FilesystemAsyncBlobStore): void 0%   (0/1)0%   (0/6)0%   (0/1)
apply (String): StorageMetadata 0%   (0/1)0%   (0/12)0%   (0/4)
     
class FilesystemAsyncBlobStore$60%   (0/1)0%   (0/10)0%   (0/24)0%   (0/10)
FilesystemAsyncBlobStore$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 FilesystemAsyncBlobStore$CommonPrefixes0%   (0/1)0%   (0/2)0%   (0/58)0%   (0/12)
FilesystemAsyncBlobStore$CommonPrefixes (String, String): void 0%   (0/1)0%   (0/9)0%   (0/4)
apply (StorageMetadata): String 0%   (0/1)0%   (0/49)0%   (0/8)
     
class FilesystemAsyncBlobStore$DelimiterFilter0%   (0/1)0%   (0/2)0%   (0/71)0%   (0/13)
FilesystemAsyncBlobStore$DelimiterFilter (String, String): void 0%   (0/1)0%   (0/9)0%   (0/4)
apply (StorageMetadata): boolean 0%   (0/1)0%   (0/62)0%   (0/9)
     
class FilesystemAsyncBlobStore100% (1/1)81%  (22/27)62%  (606/979)66%  (118.9/181)
copy (MutableBlobMetadata, String): MutableBlobMetadata 0%   (0/1)0%   (0/8)0%   (0/3)
createContainerInLocation (Location, String, CreateContainerOptions): Listena... 0%   (0/1)0%   (0/13)0%   (0/3)
getFirstQueryOrNull (String, HttpRequestOptions): String 0%   (0/1)0%   (0/22)0%   (0/4)
putBlob (String, Blob, PutOptions): ListenableFuture 0%   (0/1)0%   (0/5)0%   (0/1)
returnResponseException (int): HttpResponseException 0%   (0/1)0%   (0/17)0%   (0/3)
getEtag (Blob): String 100% (1/1)47%  (15/32)57%  (4/7)
blobMetadata (String, String): ListenableFuture 100% (1/1)48%  (14/29)32%  (1.9/6)
convertUserMetadataKeysToLowercase (MutableBlobMetadata): void 100% (1/1)50%  (14/28)68%  (3.4/5)
list (String, ListContainerOptions): ListenableFuture 100% (1/1)52%  (102/197)62%  (23.6/38)
putBlob (String, Blob): ListenableFuture 100% (1/1)63%  (33/52)67%  (6/9)
copy (MutableBlobMetadata): MutableBlobMetadata 100% (1/1)64%  (36/56)67%  (8/12)
getBlob (String, String, GetOptions): ListenableFuture 100% (1/1)65%  (203/312)70%  (35.1/50)
<static initializer> 100% (1/1)75%  (6/8)75%  (0.8/1)
loadFileBlob (String, String): Blob 100% (1/1)75%  (52/69)77%  (10/13)
FilesystemAsyncBlobStore (BlobStoreContext, DateService, Crypto, HttpGetOptio... 100% (1/1)100% (29/29)100% (8/8)
access$000 (FilesystemAsyncBlobStore, String, String): Blob 100% (1/1)100% (5/5)100% (1/1)
blobExists (String, String): ListenableFuture 100% (1/1)100% (8/8)100% (1/1)
cnfe (String): ContainerNotFoundException 100% (1/1)100% (13/13)100% (1/1)
containerExists (String): ListenableFuture 100% (1/1)100% (8/8)100% (2/2)
containerExistsSyncImpl (String): boolean 100% (1/1)100% (5/5)100% (1/1)
create (): MutableStorageMetadata 100% (1/1)100% (4/4)100% (1/1)
createContainerInLocation (Location, String): ListenableFuture 100% (1/1)100% (9/9)100% (2/2)
deleteAndVerifyContainerGone (String): boolean 100% (1/1)100% (8/8)100% (2/2)
deleteContainer (String): ListenableFuture 100% (1/1)100% (7/7)100% (2/2)
firstSliceOfSize (Iterable, int): SortedSet 100% (1/1)100% (11/11)100% (2/2)
list (): ListenableFuture 100% (1/1)100% (16/16)100% (2/2)
removeBlob (String, String): ListenableFuture 100% (1/1)100% (8/8)100% (2/2)
     
class FilesystemAsyncBlobStore$1100% (1/1)100% (2/2)90%  (69/77)79%  (7.9/10)
apply (String): StorageMetadata 100% (1/1)88%  (60/68)77%  (6.9/9)
FilesystemAsyncBlobStore$1 (FilesystemAsyncBlobStore, String): void 100% (1/1)100% (9/9)100% (1/1)
     
class FilesystemAsyncBlobStore$3100% (1/1)100% (2/2)100% (27/27)100% (2/2)
FilesystemAsyncBlobStore$3 (FilesystemAsyncBlobStore, String): void 100% (1/1)100% (9/9)100% (1/1)
apply (StorageMetadata): boolean 100% (1/1)100% (18/18)100% (1/1)
     
class FilesystemAsyncBlobStore$5100% (1/1)100% (2/2)100% (18/18)100% (5/5)
FilesystemAsyncBlobStore$5 (FilesystemAsyncBlobStore): void 100% (1/1)100% (6/6)100% (1/1)
apply (String): StorageMetadata 100% (1/1)100% (12/12)100% (4/4)

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.filesystem;
20 
21import static com.google.common.base.Preconditions.checkNotNull;
22import static com.google.common.base.Preconditions.checkState;
23import static com.google.common.base.Throwables.getCausalChain;
24import static com.google.common.base.Throwables.propagate;
25import static com.google.common.collect.Iterables.filter;
26import static com.google.common.collect.Iterables.find;
27import static com.google.common.collect.Iterables.size;
28import static com.google.common.collect.Iterables.transform;
29import static com.google.common.collect.Lists.newArrayList;
30import static com.google.common.collect.Lists.partition;
31import static com.google.common.collect.Maps.newHashMap;
32import static com.google.common.collect.Sets.filter;
33import static com.google.common.collect.Sets.newTreeSet;
34import static com.google.common.io.ByteStreams.toByteArray;
35import static com.google.common.util.concurrent.Futures.immediateFailedFuture;
36import static com.google.common.util.concurrent.Futures.immediateFuture;
37 
38import java.io.ByteArrayInputStream;
39import java.io.ByteArrayOutputStream;
40import java.io.File;
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.Date;
49import java.util.List;
50import java.util.Map;
51import java.util.Map.Entry;
52import java.util.Set;
53import java.util.SortedSet;
54import java.util.TreeSet;
55import java.util.concurrent.ExecutorService;
56 
57import org.jclouds.javax.annotation.Nullable;
58import javax.annotation.Resource;
59import javax.inject.Inject;
60import javax.inject.Named;
61import javax.ws.rs.Path;
62import javax.ws.rs.PathParam;
63 
64import org.jclouds.Constants;
65import org.jclouds.blobstore.BlobStoreContext;
66import org.jclouds.blobstore.ContainerNotFoundException;
67import org.jclouds.blobstore.KeyNotFoundException;
68import org.jclouds.blobstore.domain.Blob;
69import org.jclouds.blobstore.domain.BlobBuilder;
70import org.jclouds.blobstore.domain.BlobMetadata;
71import org.jclouds.blobstore.domain.MutableBlobMetadata;
72import org.jclouds.blobstore.domain.MutableStorageMetadata;
73import org.jclouds.blobstore.domain.PageSet;
74import org.jclouds.blobstore.domain.StorageMetadata;
75import org.jclouds.blobstore.domain.StorageType;
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.filesystem.predicates.validators.FilesystemContainerNameValidator;
92import org.jclouds.filesystem.strategy.FilesystemStorageStrategy;
93import org.jclouds.http.HttpCommand;
94import org.jclouds.http.HttpRequest;
95import org.jclouds.http.HttpResponse;
96import org.jclouds.http.HttpResponseException;
97import org.jclouds.http.options.HttpRequestOptions;
98import org.jclouds.io.Payloads;
99import org.jclouds.io.payloads.BaseMutableContentMetadata;
100import org.jclouds.logging.Logger;
101import org.jclouds.rest.annotations.ParamValidators;
102 
103import com.google.common.base.Function;
104import com.google.common.base.Predicate;
105import com.google.common.base.Supplier;
106import com.google.common.base.Throwables;
107import com.google.common.collect.Iterables;
108import com.google.common.util.concurrent.Futures;
109import com.google.common.util.concurrent.ListenableFuture;
110 
111/**
112 * 
113 * Preconditions: Blob name cannot start with / char (or \ under windows)
114 * 
115 * @author Alfredo "Rainbowbreeze" Morresi
116 */
117public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore {
118 
119   @Resource
120   protected Logger logger = Logger.NULL;
121 
122   protected final DateService dateService;
123   protected final Crypto crypto;
124   protected final HttpGetOptionsListToGetOptions httpGetOptionsConverter;
125   protected final IfDirectoryReturnNameStrategy ifDirectoryReturnName;
126   protected final FilesystemStorageStrategy storageStrategy;
127 
128   @Inject
129   protected FilesystemAsyncBlobStore(BlobStoreContext context, DateService dateService, Crypto crypto,
130         HttpGetOptionsListToGetOptions httpGetOptionsConverter, IfDirectoryReturnNameStrategy ifDirectoryReturnName,
131         BlobUtils blobUtils, @Named(Constants.PROPERTY_USER_THREADS) ExecutorService service,
132         Supplier<Location> defaultLocation, @Memoized Supplier<Set<? extends Location>> locations,
133         FilesystemStorageStrategy storageStrategy) {
134      super(context, blobUtils, service, defaultLocation, locations);
135      this.dateService = dateService;
136      this.crypto = crypto;
137      this.httpGetOptionsConverter = httpGetOptionsConverter;
138      this.ifDirectoryReturnName = ifDirectoryReturnName;
139      this.storageStrategy = checkNotNull(storageStrategy, "Storage strategy");
140   }
141 
142   /**
143    * default maxResults is 1000
144    */
145   @Override
146   public ListenableFuture<PageSet<? extends StorageMetadata>> list(final String container, ListContainerOptions options) {
147 
148      // Check if the container exists
149      if (!containerExistsSyncImpl(container)) {
150         return immediateFailedFuture(cnfe(container));
151      }
152 
153      // Loading blobs from container
154      Iterable<String> blobBelongingToContainer = null;
155      try {
156         blobBelongingToContainer = storageStrategy.getBlobKeysInsideContainer(container);
157      } catch (IOException e) {
158         logger.error(e, "An error occurred loading blobs contained into container %s", container);
159         Throwables.propagate(e);
160      }
161 
162      SortedSet<StorageMetadata> contents = newTreeSet(transform(blobBelongingToContainer,
163            new Function<String, StorageMetadata>() {
164               public StorageMetadata apply(String key) {
165                  Blob oldBlob = loadFileBlob(container, key);
166 
167                  checkState(oldBlob != null, "blob " + key + " is not present although it was in the list of "
168                        + container);
169                  checkState(oldBlob.getMetadata() != null, "blob " + container + "/" + key + " has no metadata");
170                  MutableBlobMetadata md = copy(oldBlob.getMetadata());
171                  String directoryName = ifDirectoryReturnName.execute(md);
172                  if (directoryName != null) {
173                     md.setName(directoryName);
174                     md.setType(StorageType.RELATIVE_PATH);
175                  }
176                  return md;
177               }
178            }));
179 
180      String marker = null;
181      if (options != null) {
182         if (options.getMarker() != null) {
183            final String finalMarker = options.getMarker();
184            StorageMetadata lastMarkerMetadata = find(contents, new Predicate<StorageMetadata>() {
185               public boolean apply(StorageMetadata metadata) {
186                  return metadata.getName().equals(finalMarker);
187               }
188            });
189            contents = contents.tailSet(lastMarkerMetadata);
190            contents.remove(lastMarkerMetadata);
191         }
192 
193         final String prefix = options.getDir();
194         if (prefix != null) {
195            contents = newTreeSet(filter(contents, new Predicate<StorageMetadata>() {
196               public boolean apply(StorageMetadata o) {
197                  return (o != null && o.getName().startsWith(prefix) && !o.getName().equals(prefix));
198               }
199            }));
200         }
201 
202         Integer maxResults = options.getMaxResults() != null ? options.getMaxResults() : 1000;
203         if (contents.size() > 0) {
204            SortedSet<StorageMetadata> contentsSlice = firstSliceOfSize(contents, maxResults);
205            if (!contentsSlice.contains(contents.last())) {
206               // Partial listing
207               marker = contentsSlice.last().getName();
208            } else {
209               marker = null;
210            }
211            contents = contentsSlice;
212         }
213 
214         final String delimiter = options.isRecursive() ? null : File.separator;
215         if (delimiter != null) {
216            SortedSet<String> commonPrefixes = null;
217            Iterable<String> iterable = transform(contents, new CommonPrefixes(prefix != null ? prefix : null,
218                  delimiter));
219            commonPrefixes = iterable != null ? newTreeSet(iterable) : new TreeSet<String>();
220            commonPrefixes.remove(CommonPrefixes.NO_PREFIX);
221 
222            contents = newTreeSet(filter(contents, new DelimiterFilter(prefix != null ? prefix : null, delimiter)));
223 
224            Iterables.<StorageMetadata> addAll(contents,
225                  transform(commonPrefixes, new Function<String, StorageMetadata>() {
226                     public StorageMetadata apply(String o) {
227                        MutableStorageMetadata md = new MutableStorageMetadataImpl();
228                        md.setType(StorageType.RELATIVE_PATH);
229                        md.setName(o);
230                        return md;
231                     }
232                  }));
233         }
234 
235         // trim metadata, if the response isn't supposed to be detailed.
236         if (!options.isDetailed()) {
237            for (StorageMetadata md : contents) {
238               md.getUserMetadata().clear();
239            }
240         }
241      }
242 
243      return Futures.<PageSet<? extends StorageMetadata>> immediateFuture(new PageSetImpl<StorageMetadata>(contents,
244            marker));
245 
246   }
247 
248   private ContainerNotFoundException cnfe(String name) {
249      return new ContainerNotFoundException(name, String.format("container %s not in filesystem", name));
250   }
251 
252   public static MutableBlobMetadata copy(MutableBlobMetadata in) {
253      ByteArrayOutputStream bout = new ByteArrayOutputStream();
254      ObjectOutput os;
255      try {
256         os = new ObjectOutputStream(bout);
257         os.writeObject(in);
258         ObjectInput is = new ObjectInputStream(new ByteArrayInputStream(bout.toByteArray()));
259         MutableBlobMetadata metadata = (MutableBlobMetadata) is.readObject();
260         convertUserMetadataKeysToLowercase(metadata);
261         metadata.setContentMetadata(BaseMutableContentMetadata.fromContentMetadata(in.getContentMetadata().toBuilder()
262               .build()));
263         return metadata;
264      } catch (Exception e) {
265         propagate(e);
266         assert false : "exception should have propagated: " + e;
267         return null;
268      }
269   }
270 
271   private static void convertUserMetadataKeysToLowercase(MutableBlobMetadata metadata) {
272      Map<String, String> lowerCaseUserMetadata = newHashMap();
273      for (Entry<String, String> entry : metadata.getUserMetadata().entrySet()) {
274         lowerCaseUserMetadata.put(entry.getKey().toLowerCase(), entry.getValue());
275      }
276      metadata.setUserMetadata(lowerCaseUserMetadata);
277   }
278 
279   public static MutableBlobMetadata copy(MutableBlobMetadata in, String newKey) {
280      MutableBlobMetadata newMd = copy(in);
281      newMd.setName(newKey);
282      return newMd;
283   }
284 
285   /**
286    * {@inheritDoc}
287    */
288   @Override
289   public ListenableFuture<Void> removeBlob(String container, String key) {
290      storageStrategy.removeBlob(container, key);
291      return immediateFuture(null);
292   }
293 
294   /**
295    * {@inheritDoc}
296    */
297   @Override
298   public ListenableFuture<Boolean> containerExists(String containerName) {
299      boolean exists = containerExistsSyncImpl(containerName);
300      return immediateFuture(exists);
301   }
302 
303   /**
304    * {@inheritDoc}
305    */
306   @Override
307   public ListenableFuture<PageSet<? extends StorageMetadata>> list() {
308      Iterable<String> containers = storageStrategy.getAllContainerNames();
309 
310      return Futures.<PageSet<? extends StorageMetadata>> immediateFuture(new PageSetImpl<StorageMetadata>(transform(
311            containers, new Function<String, StorageMetadata>() {
312               public StorageMetadata apply(String name) {
313                  MutableStorageMetadata cmd = create();
314                  cmd.setName(name);
315                  cmd.setType(StorageType.CONTAINER);
316                  return cmd;
317               }
318            }), null));
319   }
320 
321   protected MutableStorageMetadata create() {
322      return new MutableStorageMetadataImpl();
323   }
324 
325   /**
326    * {@inheritDoc}
327    */
328   @Path("{container}")
329   @Override
330   public ListenableFuture<Boolean> createContainerInLocation(final Location location,
331         @PathParam("container") @ParamValidators({ FilesystemContainerNameValidator.class }) String name) {
332      boolean result = storageStrategy.createContainer(name);
333      return immediateFuture(result);
334   }
335 
336   public String getFirstQueryOrNull(String string, @Nullable HttpRequestOptions options) {
337      if (options == null)
338         return null;
339      Collection<String> values = options.buildQueryParameters().get(string);
340      return (values != null && values.size() >= 1) ? values.iterator().next() : null;
341   }
342 
343   /**
344    * Load the blob with the given key belonging to the container with the given
345    * name. There must exist a resource on the file system whose complete name
346    * is given concatenating the container name and the key
347    * 
348    * @param container
349    *           it's the name of the container the blob belongs to
350    * @param key
351    *           it's the key of the blob
352    * 
353    * @return the blob belonging to the given container with the given key
354    */
355   private Blob loadFileBlob(String container, String key) {
356      logger.debug("Opening blob in container: %s - %s", container, key);
357      BlobBuilder builder = blobUtils.blobBuilder();
358      builder.name(key);
359      File file = storageStrategy.getFileForBlobKey(container, key);
360      try {
361         builder.payload(file).calculateMD5();
362      } catch (IOException e) {
363         logger.error("An error occurred calculating MD5 for blob %s from container ", key, container);
364         Throwables.propagateIfPossible(e);
365      }
366      Blob blob = builder.build();
367      if (blob.getPayload().getContentMetadata().getContentMD5() != null)
368         blob.getMetadata().setETag(CryptoStreams.hex(blob.getPayload().getContentMetadata().getContentMD5()));
369      return blob;
370   }
371 
372   protected static class DelimiterFilter implements Predicate<StorageMetadata> {
373      private final String prefix;
374      private final String delimiter;
375 
376      public DelimiterFilter(String prefix, String delimiter) {
377         this.prefix = prefix;
378         this.delimiter = delimiter;
379      }
380 
381      public boolean apply(StorageMetadata metadata) {
382         if (prefix == null)
383            return metadata.getName().indexOf(delimiter) == -1;
384         // ensure we don't accidentally append twice
385         String toMatch = prefix.endsWith("/") ? prefix : prefix + delimiter;
386         if (metadata.getName().startsWith(toMatch)) {
387            String unprefixedName = metadata.getName().replaceFirst(toMatch, "");
388            if (unprefixedName.equals("")) {
389               // we are the prefix in this case, return false
390               return false;
391            }
392            return unprefixedName.indexOf(delimiter) == -1;
393         }
394         return false;
395      }
396   }
397 
398   protected static class CommonPrefixes implements Function<StorageMetadata, String> {
399      private final String prefix;
400      private final String delimiter;
401      public static final String NO_PREFIX = "NO_PREFIX";
402 
403      public CommonPrefixes(String prefix, String delimiter) {
404         this.prefix = prefix;
405         this.delimiter = delimiter;
406      }
407 
408      public String apply(StorageMetadata metadata) {
409         String working = metadata.getName();
410         if (prefix != null) {
411            // ensure we don't accidentally append twice
412            String toMatch = prefix.endsWith("/") ? prefix : prefix + delimiter;
413            if (working.startsWith(toMatch)) {
414               working = working.replaceFirst(toMatch, "");
415            }
416         }
417         if (working.contains(delimiter)) {
418            return working.substring(0, working.indexOf(delimiter));
419         }
420         return NO_PREFIX;
421      }
422   }
423 
424   public static <T extends Comparable<?>> SortedSet<T> firstSliceOfSize(Iterable<T> elements, int size) {
425      List<List<T>> slices = partition(newArrayList(elements), size);
426      return newTreeSet(slices.get(0));
427   }
428 
429   public static HttpResponseException returnResponseException(int code) {
430      HttpResponse response = null;
431      response = new HttpResponse(code, null, null);
432      return new HttpResponseException(new HttpCommand() {
433 
434         public int getRedirectCount() {
435            return 0;
436         }
437 
438         public int incrementRedirectCount() {
439            return 0;
440         }
441 
442         public boolean isReplayable() {
443            return false;
444         }
445 
446         public Exception getException() {
447            return null;
448         }
449 
450         public int getFailureCount() {
451            return 0;
452         }
453 
454         public int incrementFailureCount() {
455            return 0;
456         }
457 
458         public void setException(Exception exception) {
459 
460         }
461 
462         @Override
463         public HttpRequest getCurrentRequest() {
464            return new HttpRequest("GET", URI.create("http://stub"));
465         }
466 
467         @Override
468         public void setCurrentRequest(HttpRequest request) {
469 
470         }
471 
472      }, response);
473   }
474 
475   /**
476    * {@inheritDoc}
477    */
478   @Override
479   public ListenableFuture<String> putBlob(String containerName, Blob object) {
480      String blobKey = object.getMetadata().getName();
481 
482      logger.debug("Put object with key [%s] to container [%s]", blobKey, containerName);
483      String eTag = getEtag(object);
484      try {
485         // TODO
486         // must override existing file?
487 
488         storageStrategy.writePayloadOnFile(containerName, blobKey, object.getPayload());
489      } catch (IOException e) {
490         logger.error(e, "An error occurred storing the new object with name [%s] to container [%s].", blobKey,
491               containerName);
492         Throwables.propagate(e);
493      }
494      return immediateFuture(eTag);
495   }
496 
497   /**
498    * {@inheritDoc}
499    */
500   @Override
501   public ListenableFuture<Boolean> blobExists(final String containerName, final String key) {
502      return immediateFuture(storageStrategy.blobExists(containerName, key));
503   }
504 
505   /**
506    * {@inheritDoc}
507    */
508   @Override
509   public ListenableFuture<Blob> getBlob(final String containerName, final String key, GetOptions options) {
510      logger.debug("Retrieving blob with key %s from container %s", key, containerName);
511      // If the container doesn't exist, an exception is thrown
512      if (!containerExistsSyncImpl(containerName)) {
513         logger.debug("Container %s does not exist", containerName);
514         return immediateFailedFuture(cnfe(containerName));
515      }
516      // If the blob doesn't exist, a null object is returned
517      if (!storageStrategy.blobExists(containerName, key)) {
518         logger.debug("Item %s does not exist in container %s", key, containerName);
519         return immediateFuture(null);
520      }
521 
522      Blob blob = loadFileBlob(containerName, key);
523 
524      if (options != null) {
525         if (options.getIfMatch() != null) {
526            if (!blob.getMetadata().getETag().equals(options.getIfMatch()))
527               return immediateFailedFuture(returnResponseException(412));
528         }
529         if (options.getIfNoneMatch() != null) {
530            if (blob.getMetadata().getETag().equals(options.getIfNoneMatch()))
531               return immediateFailedFuture(returnResponseException(304));
532         }
533         if (options.getIfModifiedSince() != null) {
534            Date modifiedSince = options.getIfModifiedSince();
535            if (blob.getMetadata().getLastModified().before(modifiedSince)) {
536               HttpResponse response = new HttpResponse(304, null, null);
537               return immediateFailedFuture(new HttpResponseException(String.format("%1$s is before %2$s", blob
538                     .getMetadata().getLastModified(), modifiedSince), null, response));
539            }
540 
541         }
542         if (options.getIfUnmodifiedSince() != null) {
543            Date unmodifiedSince = options.getIfUnmodifiedSince();
544            if (blob.getMetadata().getLastModified().after(unmodifiedSince)) {
545               HttpResponse response = new HttpResponse(412, null, null);
546               return immediateFailedFuture(new HttpResponseException(String.format("%1$s is after %2$s", blob
547                     .getMetadata().getLastModified(), unmodifiedSince), null, response));
548            }
549         }
550 
551         if (options.getRanges() != null && options.getRanges().size() > 0) {
552            byte[] data;
553            try {
554               data = toByteArray(blob.getPayload().getInput());
555            } catch (IOException e) {
556               return immediateFailedFuture(new RuntimeException(e));
557            }
558            ByteArrayOutputStream out = new ByteArrayOutputStream();
559            for (String s : options.getRanges()) {
560               if (s.startsWith("-")) {
561                  int length = Integer.parseInt(s.substring(1));
562                  out.write(data, data.length - length, length);
563               } else if (s.endsWith("-")) {
564                  int offset = Integer.parseInt(s.substring(0, s.length() - 1));
565                  out.write(data, offset, data.length - offset);
566               } else if (s.contains("-")) {
567                  String[] firstLast = s.split("\\-");
568                  int offset = Integer.parseInt(firstLast[0]);
569                  int last = Integer.parseInt(firstLast[1]);
570                  int length = last - offset + 1; // the range end is included
571                  out.write(data, offset, length);
572               } else {
573                  return immediateFailedFuture(new IllegalArgumentException("first and last were null!"));
574               }
575 
576            }
577            blob.setPayload(out.toByteArray());
578            blob.getMetadata().getContentMetadata().setContentLength(new Long(data.length));
579         }
580      }
581      checkNotNull(blob.getPayload(), "payload " + blob);
582      return immediateFuture(blob);
583   }
584 
585   /**
586    * {@inheritDoc}
587    */
588   @Override
589   public ListenableFuture<BlobMetadata> blobMetadata(String container, String key) {
590      try {
591         Blob blob = getBlob(container, key).get();
592         return Futures.<BlobMetadata> immediateFuture(blob != null ? blob.getMetadata() : null);
593      } catch (Exception e) {
594         if (size(filter(getCausalChain(e), KeyNotFoundException.class)) >= 1)
595            return immediateFuture(null);
596         return immediateFailedFuture(e);
597      }
598   }
599 
600   @Override
601   protected boolean deleteAndVerifyContainerGone(String container) {
602      storageStrategy.deleteContainer(container);
603      return containerExistsSyncImpl(container);
604   }
605 
606   /**
607    * Override parent method because it uses strange futures and listenables
608    * that creates problem in the test if more than one test that deletes the
609    * container is executed
610    * 
611    * @param container
612    * @return
613    */
614   @Override
615   public ListenableFuture<Void> deleteContainer(String container) {
616      deleteAndVerifyContainerGone(container);
617      return immediateFuture(null);
618   }
619 
620   /**
621    * Each container is a directory, so in order to check if a container exists
622    * the corresponding directory must exists. Synchronous implementation
623    * 
624    * @param containerName
625    * @return
626    */
627   private boolean containerExistsSyncImpl(String containerName) {
628      return storageStrategy.containerExists(containerName);
629   }
630 
631   /**
632    * 
633    * Calculates the object MD5 and returns it as eTag
634    * 
635    * @param object
636    * @return
637    */
638   private String getEtag(Blob object) {
639      try {
640         Payloads.calculateMD5(object, crypto.md5());
641      } catch (IOException ex) {
642         logger.error(ex, "An error occurred calculating MD5 for object with name %s.", object.getMetadata().getName());
643         Throwables.propagate(ex);
644      }
645 
646      String eTag = CryptoStreams.hex(object.getPayload().getContentMetadata().getContentMD5());
647      return eTag;
648   }
649 
650   @Override
651   public ListenableFuture<String> putBlob(String container, Blob blob, PutOptions options) {
652      // TODO implement options
653      return putBlob(container, blob);
654   }
655 
656   @Override
657   public ListenableFuture<Boolean> createContainerInLocation(Location location, String container,
658         CreateContainerOptions options) {
659      if (options.isPublicRead())
660         throw new UnsupportedOperationException("publicRead");
661      return createContainerInLocation(location, container);
662   }
663}

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