/**
  * Returns a {@link File} object that links to the blob
  *
  * @param container
  * @param blobKey
  * @return
  */
 public File getFileForBlobKey(String container, String blobKey) {
   filesystemContainerNameValidator.validate(container);
   filesystemBlobKeyValidator.validate(blobKey);
   String fileName = buildPathStartingFromBaseDir(container, blobKey);
   File blobFile = new File(fileName);
   return blobFile;
 }
  @Override
  public void removeBlob(final String container, final String blobKey) {
    filesystemContainerNameValidator.validate(container);
    filesystemBlobKeyValidator.validate(blobKey);
    String fileName = buildPathStartingFromBaseDir(container, blobKey);
    logger.debug("Deleting blob %s", fileName);
    File fileToBeDeleted = new File(fileName);

    if (fileToBeDeleted.isDirectory()) {
      try {
        UserDefinedFileAttributeView view =
            getUserDefinedFileAttributeView(fileToBeDeleted.toPath());
        if (view != null) {
          for (String s : view.list()) {
            view.delete(s);
          }
        }
      } catch (IOException e) {
        logger.debug("Could not delete attributes from %s: %s", fileToBeDeleted, e);
      }
    }

    try {
      delete(fileToBeDeleted);
    } catch (IOException e) {
      logger.debug("Could not delete %s: %s", fileToBeDeleted, e);
    }

    // now examine if the key of the blob is a complex key (with a directory structure)
    // and eventually remove empty directory
    removeDirectoriesTreeOfBlobKey(container, blobKey);
  }
 @Override
 public void deleteContainer(String container) {
   filesystemContainerNameValidator.validate(container);
   if (!containerExists(container)) {
     return;
   }
   deleteDirectory(container, null);
 }
 @Override
 public boolean blobExists(String container, String key) {
   filesystemContainerNameValidator.validate(container);
   filesystemBlobKeyValidator.validate(key);
   try {
     return buildPathAndChecksIfBlobExists(container, key);
   } catch (IOException e) {
     logger.error(e, "An error occurred while checking key %s in container %s", container, key);
     throw Throwables.propagate(e);
   }
 }
 @Override
 public boolean createContainerInLocation(
     String container, Location location, CreateContainerOptions options) {
   // TODO: implement location
   logger.debug("Creating container %s", container);
   filesystemContainerNameValidator.validate(container);
   boolean created = createDirectoryWithResult(container, null);
   if (created) {
     setContainerAccess(
         container,
         options.isPublicRead() ? ContainerAccess.PUBLIC_READ : ContainerAccess.PRIVATE);
   }
   return created;
 }
 @Override
 public void clearContainer(String container, ListContainerOptions options) {
   filesystemContainerNameValidator.validate(container);
   if (options.getDir() != null) {
     container += denormalize("/" + options.getDir());
   }
   try {
     File containerFile = openFolder(container);
     File[] children = containerFile.listFiles();
     if (null != children) {
       for (File child : children)
         if (options.isRecursive() || child.isFile()) {
           Utils.deleteRecursively(child);
         }
     }
   } catch (IOException e) {
     logger.error(e, "An error occurred while clearing container %s", container);
     Throwables.propagate(e);
   }
 }
  /**
   * Returns all the blobs key inside a container
   *
   * @param container
   * @return
   * @throws IOException
   */
  @Override
  public Iterable<String> getBlobKeysInsideContainer(String container) throws IOException {
    filesystemContainerNameValidator.validate(container);
    // check if container exists
    // TODO maybe an error is more appropriate
    Set<String> blobNames = Sets.newHashSet();
    if (!containerExists(container)) {
      return blobNames;
    }

    File containerFile = openFolder(container);
    final int containerPathLength = containerFile.getAbsolutePath().length() + 1;
    populateBlobKeysInContainer(
        containerFile,
        blobNames,
        new Function<String, String>() {
          @Override
          public String apply(String string) {
            return string.substring(containerPathLength);
          }
        });
    return blobNames;
  }
 public boolean createContainer(String container) {
   filesystemContainerNameValidator.validate(container);
   return createContainerInLocation(container, null, CreateContainerOptions.NONE);
 }
  @Override
  public String putBlob(final String containerName, final Blob blob) throws IOException {
    String blobKey = blob.getMetadata().getName();
    Payload payload = blob.getPayload();
    filesystemContainerNameValidator.validate(containerName);
    filesystemBlobKeyValidator.validate(blobKey);
    if (getDirectoryBlobSuffix(blobKey) != null) {
      return putDirectoryBlob(containerName, blob);
    }
    File outputFile = getFileForBlobKey(containerName, blobKey);
    // TODO: should we use a known suffix to filter these out during list?
    String tmpBlobName = blobKey + "-" + UUID.randomUUID();
    File tmpFile = getFileForBlobKey(containerName, tmpBlobName);
    Path tmpPath = tmpFile.toPath();
    HashingInputStream his = null;
    try {
      Files.createParentDirs(tmpFile);
      his = new HashingInputStream(Hashing.md5(), payload.openStream());
      long actualSize = Files.asByteSink(tmpFile).writeFrom(his);
      Long expectedSize = blob.getMetadata().getContentMetadata().getContentLength();
      if (expectedSize != null && actualSize != expectedSize) {
        throw new IOException(
            "Content-Length mismatch, actual: " + actualSize + " expected: " + expectedSize);
      }
      HashCode actualHashCode = his.hash();
      HashCode expectedHashCode = payload.getContentMetadata().getContentMD5AsHashCode();
      if (expectedHashCode != null && !actualHashCode.equals(expectedHashCode)) {
        throw new IOException(
            "MD5 hash code mismatch, actual: " + actualHashCode + " expected: " + expectedHashCode);
      }
      payload.getContentMetadata().setContentMD5(actualHashCode);

      if (outputFile.exists()) {
        delete(outputFile);
      }

      UserDefinedFileAttributeView view = getUserDefinedFileAttributeView(tmpPath);
      if (view != null) {
        try {
          view.write(XATTR_CONTENT_MD5, ByteBuffer.wrap(actualHashCode.asBytes()));
          writeCommonMetadataAttr(view, blob);
        } catch (IOException e) {
          logger.debug("xattrs not supported on %s", tmpPath);
        }
      }

      setBlobAccess(containerName, tmpBlobName, BlobAccess.PRIVATE);

      if (!tmpFile.renameTo(outputFile)) {
        throw new RuntimeException("Could not rename file " + tmpFile + " to " + outputFile);
      }

      return base16().lowerCase().encode(actualHashCode.asBytes());
    } catch (IOException ex) {
      if (tmpFile != null) {
        try {
          delete(tmpFile);
        } catch (IOException e) {
          logger.debug("Could not delete %s: %s", tmpFile, e);
        }
      }
      throw ex;
    } finally {
      closeQuietly(his);
      if (payload != null) {
        payload.release();
      }
    }
  }
 @Override
 public boolean containerExists(String container) {
   filesystemContainerNameValidator.validate(container);
   return directoryExists(container, null);
 }