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

COVERAGE SUMMARY FOR SOURCE FILE [FilesystemStorageStrategyImpl.java]

nameclass, %method, %block, %line, %
FilesystemStorageStrategyImpl.java100% (4/4)97%  (33/34)89%  (748/844)88%  (146.6/166)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class FilesystemStorageStrategyImpl$FileIterator100% (1/1)75%  (3/4)69%  (58/84)73%  (11/15)
remove (): void 0%   (0/1)0%   (0/26)0%   (0/4)
FilesystemStorageStrategyImpl$FileIterator (FilesystemStorageStrategyImpl, St... 100% (1/1)100% (32/32)100% (8/8)
hasNext (): boolean 100% (1/1)100% (10/10)100% (1/1)
next (): String 100% (1/1)100% (16/16)100% (2/2)
     
class FilesystemStorageStrategyImpl100% (1/1)100% (26/26)90%  (656/726)90%  (133.6/149)
deleteDirectory (String, String): void 100% (1/1)59%  (20/34)57%  (4/7)
openFolder (String): File 100% (1/1)61%  (22/36)83%  (5/6)
clearContainer (String, ListContainerOptions): void 100% (1/1)69%  (34/49)73%  (8/11)
normalize (String): String 100% (1/1)72%  (13/18)75%  (3/4)
writePayloadOnFile (String, String, Payload): void 100% (1/1)74%  (46/62)66%  (11.8/18)
removeDirectoriesTreeOfBlobKey (String, String): void 100% (1/1)93%  (53/57)98%  (11.7/12)
removeFileSeparatorFromBorders (String, boolean): String 100% (1/1)95%  (38/40)90%  (9/10)
FilesystemStorageStrategyImpl (Provider, String, FilesystemContainerNameValid... 100% (1/1)100% (30/30)100% (7/7)
blobExists (String, String): boolean 100% (1/1)100% (21/21)100% (3/3)
buildPathAndChecksIfDirectoryExists (String []): boolean 100% (1/1)100% (21/21)100% (4/4)
buildPathAndChecksIfFileExists (String []): boolean 100% (1/1)100% (21/21)100% (4/4)
buildPathStartingFromBaseDir (String []): String 100% (1/1)100% (48/48)100% (8/8)
clearContainer (String): void 100% (1/1)100% (9/9)100% (3/3)
containerExists (String): boolean 100% (1/1)100% (9/9)100% (2/2)
countBlobs (String, ListContainerOptions): long 100% (1/1)100% (5/5)100% (1/1)
createContainer (String): boolean 100% (1/1)100% (19/19)100% (3/3)
createDirectory (String, String): void 100% (1/1)100% (6/6)100% (2/2)
createDirectoryWithResult (String, String): boolean 100% (1/1)100% (58/58)100% (8/8)
deleteContainer (String): void 100% (1/1)100% (9/9)100% (3/3)
directoryExists (String, String): boolean 100% (1/1)100% (13/13)100% (1/1)
getAllContainerNames (): Iterable 100% (1/1)100% (7/7)100% (2/2)
getBlobKeysInsideContainer (String): Iterable 100% (1/1)100% (34/34)100% (8/8)
getFileForBlobKey (String, String): File 100% (1/1)100% (28/28)100% (5/5)
newBlob (String): Blob 100% (1/1)100% (12/12)100% (2/2)
populateBlobKeysInContainer (File, Set): void 100% (1/1)100% (36/36)100% (7/7)
removeBlob (String, String): void 100% (1/1)100% (44/44)100% (8/8)
     
class FilesystemStorageStrategyImpl$1100% (1/1)100% (2/2)100% (18/18)100% (2/2)
FilesystemStorageStrategyImpl$1 (FilesystemStorageStrategyImpl): void 100% (1/1)100% (6/6)100% (1/1)
iterator (): Iterator 100% (1/1)100% (12/12)100% (1/1)
     
class FilesystemStorageStrategyImpl$2100% (1/1)100% (2/2)100% (16/16)100% (2/2)
FilesystemStorageStrategyImpl$2 (FilesystemStorageStrategyImpl, int): void 100% (1/1)100% (9/9)100% (1/1)
add (String): boolean 100% (1/1)100% (7/7)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.filesystem.strategy.internal;
20 
21import static com.google.common.base.Preconditions.checkNotNull;
22 
23import java.io.File;
24import java.io.FileFilter;
25import java.io.FileOutputStream;
26import java.io.IOException;
27import java.util.HashSet;
28import java.util.Iterator;
29import java.util.Set;
30 
31import javax.annotation.Resource;
32import javax.inject.Inject;
33import javax.inject.Named;
34import javax.inject.Provider;
35 
36import org.apache.commons.io.FileUtils;
37import org.apache.commons.io.filefilter.DirectoryFileFilter;
38import org.jclouds.blobstore.domain.Blob;
39import org.jclouds.blobstore.domain.BlobBuilder;
40import org.jclouds.blobstore.options.ListContainerOptions;
41import org.jclouds.filesystem.predicates.validators.FilesystemBlobKeyValidator;
42import org.jclouds.filesystem.predicates.validators.FilesystemContainerNameValidator;
43import org.jclouds.filesystem.reference.FilesystemConstants;
44import org.jclouds.filesystem.strategy.FilesystemStorageStrategy;
45import org.jclouds.io.Payload;
46import org.jclouds.logging.Logger;
47import org.jclouds.rest.annotations.ParamValidators;
48 
49import com.google.common.base.Throwables;
50import com.google.common.io.Closeables;
51import com.google.common.io.Files;
52 
53/**
54 * 
55 * @author Alfredo "Rainbowbreeze" Morresi
56 */
57public class FilesystemStorageStrategyImpl implements FilesystemStorageStrategy {
58 
59   private static final String BACK_SLASH = "\\";
60 
61   @Resource
62   protected Logger logger = Logger.NULL;
63 
64   protected final Provider<BlobBuilder> blobBuilders;
65   protected final String baseDirectory;
66   protected final FilesystemContainerNameValidator filesystemContainerNameValidator;
67   protected final FilesystemBlobKeyValidator filesystemBlobKeyValidator;
68 
69   @Inject
70   protected FilesystemStorageStrategyImpl(Provider<BlobBuilder> blobBuilders,
71         @Named(FilesystemConstants.PROPERTY_BASEDIR) String baseDir,
72         FilesystemContainerNameValidator filesystemContainerNameValidator,
73         FilesystemBlobKeyValidator filesystemBlobKeyValidator) {
74      this.blobBuilders = checkNotNull(blobBuilders, "filesystem storage strategy blobBuilders");
75      this.baseDirectory = checkNotNull(baseDir, "filesystem storage strategy base directory");
76      this.filesystemContainerNameValidator = checkNotNull(filesystemContainerNameValidator,
77            "filesystem container name validator");
78      this.filesystemBlobKeyValidator = checkNotNull(filesystemBlobKeyValidator, "filesystem blob key validator");
79   }
80 
81   @Override
82   public boolean containerExists(String container) {
83      filesystemContainerNameValidator.validate(container);
84      return directoryExists(container, null);
85   }
86 
87   @Override
88   public boolean blobExists(String container, String key) {
89      filesystemContainerNameValidator.validate(container);
90      filesystemBlobKeyValidator.validate(key);
91      return buildPathAndChecksIfFileExists(container, key);
92   }
93 
94   @Override
95   public boolean createContainer(String container) {
96      logger.debug("Creating container %s", container);
97      filesystemContainerNameValidator.validate(container);
98      return createDirectoryWithResult(container, null);
99   }
100 
101   @Override
102   public void deleteContainer(String container) {
103      filesystemContainerNameValidator.validate(container);
104      deleteDirectory(container, null);
105   }
106 
107   /**
108    * Empty the directory of its content (files and subdirectories)
109    * 
110    * @param container
111    */
112   @Override
113   public void clearContainer(final String container) {
114      filesystemContainerNameValidator.validate(container);
115      clearContainer(container, ListContainerOptions.Builder.recursive());
116   }
117 
118   @Override
119   public void clearContainer(String container, ListContainerOptions options) {
120      filesystemContainerNameValidator.validate(container);
121      // TODO
122      // now all is deleted, check it based on options
123      try {
124         File containerFile = openFolder(container);
125         File[] children = containerFile.listFiles();
126         if (null != children) {
127            for (File child : children)
128               FileUtils.forceDelete(child);
129         }
130      } catch (IOException e) {
131         logger.error(e, "An error occurred while clearing container %s", container);
132         Throwables.propagate(e);
133      }
134   }
135 
136   @Override
137   public Blob newBlob(@ParamValidators({ FilesystemBlobKeyValidator.class }) String name) {
138      filesystemBlobKeyValidator.validate(name);
139      return blobBuilders.get().name(name).build();
140   }
141 
142   @Override
143   public void removeBlob(final String container, final String blobKey) {
144      filesystemContainerNameValidator.validate(container);
145      filesystemBlobKeyValidator.validate(blobKey);
146      String fileName = buildPathStartingFromBaseDir(container, blobKey);
147      logger.debug("Deleting blob %s", fileName);
148      File fileToBeDeleted = new File(fileName);
149      fileToBeDeleted.delete();
150 
151      // now examins if the key of the blob is a complex key (with a directory structure)
152      // and eventually remove empty directory
153      removeDirectoriesTreeOfBlobKey(container, blobKey);
154   }
155 
156   /**
157    * Return an iterator that reports all the containers under base path
158    * 
159    * @return
160    */
161   @Override
162   public Iterable<String> getAllContainerNames() {
163      Iterable<String> containers = new Iterable<String>() {
164         @Override
165         public Iterator<String> iterator() {
166            return new FileIterator(buildPathStartingFromBaseDir(), DirectoryFileFilter.INSTANCE);
167         }
168      };
169 
170      return containers;
171   }
172 
173   /**
174    * Returns a {@link File} object that links to the blob
175    * 
176    * @param container
177    * @param blobKey
178    * @return
179    */
180   @Override
181   public File getFileForBlobKey(String container, String blobKey) {
182      filesystemContainerNameValidator.validate(container);
183      filesystemBlobKeyValidator.validate(blobKey);
184      String fileName = buildPathStartingFromBaseDir(container, blobKey);
185      File blobFile = new File(fileName);
186      return blobFile;
187   }
188 
189   /**
190    * Write a {@link Blob} {@link Payload} into a file
191    * 
192    * @param container
193    * @param blobKey
194    * @param payload
195    * @throws IOException
196    */
197   @Override
198   public void writePayloadOnFile(String container, String blobKey, Payload payload) throws IOException {
199      filesystemContainerNameValidator.validate(container);
200      filesystemBlobKeyValidator.validate(blobKey);
201      File outputFile = getFileForBlobKey(container, blobKey);
202      if (payload.getRawContent().equals(outputFile)){
203         // we shouldn't re-copy the same contents
204         return;
205      }
206      FileOutputStream output = null;
207      try {
208         Files.createParentDirs(outputFile);
209         if (payload.getRawContent() instanceof File)
210            Files.copy(File.class.cast(payload.getRawContent()), outputFile);
211         else
212            payload.writeTo(new FileOutputStream(outputFile));
213      } catch (IOException ex) {
214         if (outputFile != null) {
215            outputFile.delete();
216         }
217         throw ex;
218      } finally {
219         Closeables.closeQuietly(output);
220         payload.release();
221      }
222   }
223 
224   /**
225    * Returns all the blobs key inside a container
226    * 
227    * @param container
228    * @return
229    * @throws IOException
230    */
231   @Override
232   public Iterable<String> getBlobKeysInsideContainer(String container) throws IOException {
233      filesystemContainerNameValidator.validate(container);
234      // check if container exists
235      // TODO maybe an error is more appropriate
236      if (!containerExists(container)) {
237         return new HashSet<String>();
238      }
239 
240      File containerFile = openFolder(container);
241      final int containerPathLenght = containerFile.getAbsolutePath().length() + 1;
242      Set<String> blobNames = new HashSet<String>() {
243 
244         private static final long serialVersionUID = 3152191346558570795L;
245 
246         @Override
247         public boolean add(String e) {
248            return super.add(e.substring(containerPathLenght));
249         }
250      };
251      populateBlobKeysInContainer(containerFile, blobNames);
252      return blobNames;
253   }
254 
255   @Override
256   public boolean directoryExists(String container, String directory) {
257      return buildPathAndChecksIfDirectoryExists(container, directory);
258   }
259 
260   @Override
261   public void createDirectory(String container, String directory) {
262      createDirectoryWithResult(container, directory);
263   }
264 
265   @Override
266   public void deleteDirectory(String container, String directory) {
267      // create complete dir path
268      String fullDirPath = buildPathStartingFromBaseDir(container, directory);
269      try {
270         FileUtils.forceDelete(new File(fullDirPath));
271      } catch (IOException ex) {
272         logger.error("An error occurred removing directory %s.", fullDirPath);
273         Throwables.propagate(ex);
274      }
275   }
276 
277   @Override
278   public long countBlobs(String container, ListContainerOptions options) {
279      // TODO
280      throw new UnsupportedOperationException("Not supported yet.");
281   }
282 
283   // ---------------------------------------------------------- Private methods
284 
285   private boolean buildPathAndChecksIfFileExists(String... tokens) {
286      String path = buildPathStartingFromBaseDir(tokens);
287      File file = new File(path);
288      boolean exists = file.exists() || file.isFile();
289      return exists;
290   }
291 
292   /**
293    * Check if the file system resource whose name is obtained applying buildPath on the input path
294    * tokens is a directory, otherwise a RuntimeException is thrown
295    * 
296    * @param tokens
297    *           the tokens that make up the name of the resource on the file system
298    */
299   private boolean buildPathAndChecksIfDirectoryExists(String... tokens) {
300      String path = buildPathStartingFromBaseDir(tokens);
301      File file = new File(path);
302      boolean exists = file.exists() || file.isDirectory();
303      return exists;
304   }
305 
306   /**
307    * Facility method used to concatenate path tokens normalizing separators
308    * 
309    * @param pathTokens
310    *           all the string in the proper order that must be concatenated in order to obtain the
311    *           filename
312    * @return the resulting string
313    */
314   protected String buildPathStartingFromBaseDir(String... pathTokens) {
315      String normalizedToken = removeFileSeparatorFromBorders(normalize(baseDirectory), true);
316      StringBuilder completePath = new StringBuilder(normalizedToken);
317      if (pathTokens != null && pathTokens.length > 0) {
318         for (int i = 0; i < pathTokens.length; i++) {
319            if (pathTokens[i] != null) {
320               normalizedToken = removeFileSeparatorFromBorders(normalize(pathTokens[i]), false);
321               completePath.append(File.separator).append(normalizedToken);
322            }
323         }
324      }
325      return completePath.toString();
326   }
327 
328   /**
329    * Substitutes all the file separator occurrences in the path with a file separator for the
330    * current operative system
331    * 
332    * @param pathToBeNormalized
333    * @return
334    */
335   private String normalize(String pathToBeNormalized) {
336      if (null != pathToBeNormalized && pathToBeNormalized.contains(BACK_SLASH)) {
337         if (!BACK_SLASH.equals(File.separator)) {
338            return pathToBeNormalized.replaceAll(BACK_SLASH, File.separator);
339         }
340      }
341      return pathToBeNormalized;
342   }
343 
344   /**
345    * Remove leading and trailing {@link File.separator} character from the string.
346    * 
347    * @param pathToBeCleaned
348    * @param remove
349    *           only trailing separator char from path
350    * @return
351    */
352   private String removeFileSeparatorFromBorders(String pathToBeCleaned, boolean onlyTrailing) {
353      if (null == pathToBeCleaned || pathToBeCleaned.equals(""))
354         return pathToBeCleaned;
355 
356      int beginIndex = 0;
357      int endIndex = pathToBeCleaned.length();
358 
359      // search for separator chars
360      if (!onlyTrailing) {
361         if (pathToBeCleaned.substring(0, 1).equals(File.separator))
362            beginIndex = 1;
363      }
364      if (pathToBeCleaned.substring(pathToBeCleaned.length() - 1).equals(File.separator))
365         endIndex--;
366 
367      return pathToBeCleaned.substring(beginIndex, endIndex);
368   }
369 
370   /**
371    * Removes recursively the directory structure of a complex blob key, only if the directory is
372    * empty
373    * 
374    * @param container
375    * @param normalizedKey
376    */
377   private void removeDirectoriesTreeOfBlobKey(String container, String blobKey) {
378      String normalizedBlobKey = normalize(blobKey);
379      // exists is no path is present in the blobkey
380      if (!normalizedBlobKey.contains(File.separator))
381         return;
382 
383      File file = new File(normalizedBlobKey);
384      // TODO
385      // "/media/data/works/java/amazon/jclouds/master/filesystem/aa/bb/cc/dd/eef6f0c8-0206-460b-8870-352e6019893c.txt"
386      String parentPath = file.getParent();
387      // no need to manage "/" parentPath, because "/" cannot be used as start
388      // char of blobkey
389      if (null != parentPath || "".equals(parentPath)) {
390         // remove parent directory only it's empty
391         File directory = new File(buildPathStartingFromBaseDir(container, parentPath));
392         String[] children = directory.list();
393         if (null == children || children.length == 0) {
394            directory.delete();
395            // recursively call for removing other path
396            removeDirectoriesTreeOfBlobKey(container, parentPath);
397         }
398      }
399   }
400 
401   private File openFolder(String folderName) throws IOException {
402      String baseFolderName = buildPathStartingFromBaseDir(folderName);
403      File folder = new File(baseFolderName);
404      if (folder.exists()) {
405         if (!folder.isDirectory()) {
406            throw new IOException("Resource " + baseFolderName + " isn't a folder.");
407         }
408      }
409      return folder;
410   }
411 
412   private class FileIterator implements Iterator<String> {
413      int currentFileIndex = 0;
414      File[] children = new File[0];
415      File currentFile = null;
416 
417      public FileIterator(String fileName, FileFilter filter) {
418         File file = new File(fileName);
419         if (file.exists() && file.isDirectory()) {
420            children = file.listFiles(filter);
421         }
422      }
423 
424      @Override
425      public boolean hasNext() {
426         return currentFileIndex < children.length;
427      }
428 
429      @Override
430      public String next() {
431         currentFile = children[currentFileIndex++];
432         return currentFile.getName();
433      }
434 
435      @Override
436      public void remove() {
437         if (currentFile != null && currentFile.exists()) {
438            if (!currentFile.delete()) {
439               throw new RuntimeException("An error occurred deleting " + currentFile.getName());
440            }
441         }
442      }
443   }
444 
445   private void populateBlobKeysInContainer(File directory, Set<String> blobNames) {
446      File[] children = directory.listFiles();
447      for (File child : children) {
448         if (child.isFile()) {
449            blobNames.add(child.getAbsolutePath());
450         } else if (child.isDirectory()) {
451            populateBlobKeysInContainer(child, blobNames);
452         }
453      }
454   }
455 
456   /**
457    * Creates a directory and returns the result
458    * 
459    * @param container
460    * @param directory
461    * @return true if the directory was created, otherwise false
462    */
463   protected boolean createDirectoryWithResult(String container, String directory) {
464      String directoryFullName = buildPathStartingFromBaseDir(container, directory);
465      logger.debug("Creating directory %s", directoryFullName);
466 
467      // cannot use directoryFullName, because the following method rebuild
468      // another time the path starting from base directory
469      if (buildPathAndChecksIfDirectoryExists(container, directory)) {
470         logger.debug("Directory %s already exists", directoryFullName);
471         return false;
472      }
473 
474      File directoryToCreate = new File(directoryFullName);
475      boolean result = directoryToCreate.mkdirs();
476      return result;
477   }
478 
479}

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