EMMA Coverage Report (generated Wed Jun 22 19:47:49 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%  (34/35)87%  (781/902)87%  (160.9/184)

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

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