Issue 462: corrected semantics of BlobMap when using inDirectory and added BlobBuilder

This commit is contained in:
Adrian Cole 2011-02-13 22:32:31 +01:00
parent 5df062ba47
commit 20c23e7962
39 changed files with 2498 additions and 2182 deletions

View File

@ -36,7 +36,7 @@ import com.google.common.base.Function;
*/
@Singleton
public class ObjectToBlob implements Function<AtmosObject, Blob> {
private final Blob.Factory blobFactory;
private final Factory blobFactory;
private final ObjectToBlobMetadata object2BlobMd;
@Inject

View File

@ -129,10 +129,10 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore {
@Inject
protected FilesystemAsyncBlobStore(BlobStoreContext context, DateService dateService, Crypto crypto,
HttpGetOptionsListToGetOptions httpGetOptionsConverter,
IfDirectoryReturnNameStrategy ifDirectoryReturnName, Blob.Factory blobFactory, BlobUtils blobUtils,
@Named(Constants.PROPERTY_USER_THREADS) ExecutorService service, Supplier<Location> defaultLocation,
@Memoized Supplier<Set<? extends Location>> locations, FilesystemStorageStrategy storageStrategy) {
HttpGetOptionsListToGetOptions httpGetOptionsConverter, IfDirectoryReturnNameStrategy ifDirectoryReturnName,
Factory blobFactory, BlobUtils blobUtils, @Named(Constants.PROPERTY_USER_THREADS) ExecutorService service,
Supplier<Location> defaultLocation, @Memoized Supplier<Set<? extends Location>> locations,
FilesystemStorageStrategy storageStrategy) {
super(context, blobUtils, service, defaultLocation, locations);
// super(context, blobUtils, service, null, null);
this.blobFactory = blobFactory;
@ -164,22 +164,22 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore {
}
SortedSet<StorageMetadata> contents = newTreeSet(transform(blobBelongingToContainer,
new Function<String, StorageMetadata>() {
public StorageMetadata apply(String key) {
Blob oldBlob = loadFileBlob(container, key);
new Function<String, StorageMetadata>() {
public StorageMetadata apply(String key) {
Blob oldBlob = loadFileBlob(container, key);
checkState(oldBlob != null, "blob " + key + " is not present although it was in the list of "
+ container);
checkState(oldBlob.getMetadata() != null, "blob " + container + "/" + key + " has no metadata");
MutableBlobMetadata md = copy(oldBlob.getMetadata());
String directoryName = ifDirectoryReturnName.execute(md);
if (directoryName != null) {
md.setName(directoryName);
md.setType(StorageType.RELATIVE_PATH);
}
return md;
checkState(oldBlob != null, "blob " + key + " is not present although it was in the list of "
+ container);
checkState(oldBlob.getMetadata() != null, "blob " + container + "/" + key + " has no metadata");
MutableBlobMetadata md = copy(oldBlob.getMetadata());
String directoryName = ifDirectoryReturnName.execute(md);
if (directoryName != null) {
md.setName(directoryName);
md.setType(StorageType.RELATIVE_PATH);
}
}));
return md;
}
}));
String marker = null;
if (options != null) {
@ -219,21 +219,21 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore {
if (delimiter != null) {
SortedSet<String> commonPrefixes = null;
Iterable<String> iterable = transform(contents, new CommonPrefixes(prefix != null ? prefix : null,
delimiter));
delimiter));
commonPrefixes = iterable != null ? newTreeSet(iterable) : new TreeSet<String>();
commonPrefixes.remove(CommonPrefixes.NO_PREFIX);
contents = newTreeSet(filter(contents, new DelimiterFilter(prefix != null ? prefix : null, delimiter)));
Iterables.<StorageMetadata> addAll(contents, transform(commonPrefixes,
new Function<String, StorageMetadata>() {
public StorageMetadata apply(String o) {
MutableStorageMetadata md = new MutableStorageMetadataImpl();
md.setType(StorageType.RELATIVE_PATH);
md.setName(o);
return md;
}
}));
Iterables.<StorageMetadata> addAll(contents,
transform(commonPrefixes, new Function<String, StorageMetadata>() {
public StorageMetadata apply(String o) {
MutableStorageMetadata md = new MutableStorageMetadataImpl();
md.setType(StorageType.RELATIVE_PATH);
md.setName(o);
return md;
}
}));
}
// trim metadata, if the response isn't supposed to be detailed.
@ -245,7 +245,7 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore {
}
return Futures.<PageSet<? extends StorageMetadata>> immediateFuture(new PageSetImpl<StorageMetadata>(contents,
marker));
marker));
}
@ -310,14 +310,14 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore {
Iterable<String> containers = storageStrategy.getAllContainerNames();
return Futures.<PageSet<? extends StorageMetadata>> immediateFuture(new PageSetImpl<StorageMetadata>(transform(
containers, new Function<String, StorageMetadata>() {
public StorageMetadata apply(String name) {
MutableStorageMetadata cmd = create();
cmd.setName(name);
cmd.setType(StorageType.CONTAINER);
return cmd;
}
}), null));
containers, new Function<String, StorageMetadata>() {
public StorageMetadata apply(String name) {
MutableStorageMetadata cmd = create();
cmd.setName(name);
cmd.setType(StorageType.CONTAINER);
return cmd;
}
}), null));
}
protected MutableStorageMetadata create() {
@ -330,7 +330,7 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore {
@Path("{container}")
@Override
public ListenableFuture<Boolean> createContainerInLocation(final Location location,
@PathParam("container") @ParamValidators( { FilesystemContainerNameValidator.class }) String name) {
@PathParam("container") @ParamValidators({ FilesystemContainerNameValidator.class }) String name) {
boolean result = storageStrategy.createContainer(name);
return immediateFuture(result);
}
@ -500,7 +500,7 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore {
storageStrategy.writePayloadOnFile(containerName, blobKey, object.getPayload());
} catch (IOException e) {
logger.error(e, "An error occurred storing the new object with name [%s] to container [%s].", blobKey,
containerName);
containerName);
Throwables.propagate(e);
}
return immediateFuture(eTag);
@ -547,7 +547,7 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore {
if (blob.getMetadata().getLastModified().before(modifiedSince)) {
HttpResponse response = new HttpResponse(304, null, null);
return immediateFailedFuture(new HttpResponseException(String.format("%1$s is before %2$s", blob
.getMetadata().getLastModified(), modifiedSince), null, response));
.getMetadata().getLastModified(), modifiedSince), null, response));
}
}
@ -556,7 +556,7 @@ public class FilesystemAsyncBlobStore extends BaseAsyncBlobStore {
if (blob.getMetadata().getLastModified().after(unmodifiedSince)) {
HttpResponse response = new HttpResponse(412, null, null);
return immediateFailedFuture(new HttpResponseException(String.format("%1$s is after %2$s", blob
.getMetadata().getLastModified(), unmodifiedSince), null, response));
.getMetadata().getLastModified(), unmodifiedSince), null, response));
}
}

View File

@ -19,33 +19,37 @@
package org.jclouds.filesystem.strategy.internal;
import org.jclouds.rest.annotations.ParamValidators;
import static com.google.common.base.Preconditions.checkNotNull;
import java.io.File;
import java.io.FileFilter;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import javax.annotation.Resource;
import javax.inject.Provider;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.DirectoryFileFilter;
import org.jclouds.blobstore.domain.Blob;
import org.jclouds.blobstore.domain.BlobBuilder;
import org.jclouds.blobstore.options.ListContainerOptions;
import org.jclouds.filesystem.predicates.validators.FilesystemBlobKeyValidator;
import org.jclouds.filesystem.predicates.validators.FilesystemContainerNameValidator;
import java.io.OutputStream;
import java.util.Set;
import java.util.HashSet;
import org.jclouds.blobstore.domain.Blob;
import org.jclouds.blobstore.options.ListContainerOptions;
import org.jclouds.io.Payload;
import java.io.InputStream;
import java.io.FileOutputStream;
import java.io.FileFilter;
import org.apache.commons.io.filefilter.DirectoryFileFilter;
import java.util.Iterator;
import com.google.common.base.Throwables;
import java.io.IOException;
import org.apache.commons.io.FileUtils;
import java.io.File;
import javax.annotation.Resource;
import com.google.inject.Inject;
import com.google.inject.name.Named;
import org.jclouds.filesystem.reference.FilesystemConstants;
import org.jclouds.filesystem.strategy.FilesystemStorageStrategy;
import org.jclouds.io.Payload;
import org.jclouds.logging.Logger;
import org.jclouds.rest.annotations.ParamValidators;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.base.Throwables;
import com.google.inject.Inject;
import com.google.inject.name.Named;
/**
*
@ -53,472 +57,469 @@ import static com.google.common.base.Preconditions.checkNotNull;
*/
public class FilesystemStorageStrategyImpl implements FilesystemStorageStrategy {
private static final String BACK_SLASH = "\\";
/** The buffer size used to copy an InputStream to an OutputStream */
private static final int COPY_BUFFER_SIZE = 1024;
private static final String BACK_SLASH = "\\";
/** The buffer size used to copy an InputStream to an OutputStream */
private static final int COPY_BUFFER_SIZE = 1024;
@Resource
protected Logger logger = Logger.NULL;
@Resource
protected Logger logger = Logger.NULL;
protected final Blob.Factory blobFactory;
protected final String baseDirectory;
protected final FilesystemContainerNameValidator filesystemContainerNameValidator;
protected final FilesystemBlobKeyValidator filesystemBlobKeyValidator;
protected final Provider<BlobBuilder> blobBuilders;
protected final String baseDirectory;
protected final FilesystemContainerNameValidator filesystemContainerNameValidator;
protected final FilesystemBlobKeyValidator filesystemBlobKeyValidator;
@Inject
protected FilesystemStorageStrategyImpl(Provider<BlobBuilder> blobBuilders,
@Named(FilesystemConstants.PROPERTY_BASEDIR) String baseDir,
FilesystemContainerNameValidator filesystemContainerNameValidator,
FilesystemBlobKeyValidator filesystemBlobKeyValidator) {
this.blobBuilders = checkNotNull(blobBuilders, "filesystem storage strategy blobBuilders");
this.baseDirectory = checkNotNull(baseDir, "filesystem storage strategy base directory");
this.filesystemContainerNameValidator = checkNotNull(filesystemContainerNameValidator,
"filesystem container name validator");
this.filesystemBlobKeyValidator = checkNotNull(filesystemBlobKeyValidator, "filesystem blob key validator");
}
@Inject
protected FilesystemStorageStrategyImpl(
Blob.Factory blobFactory,
@Named(FilesystemConstants.PROPERTY_BASEDIR) String baseDir,
FilesystemContainerNameValidator filesystemContainerNameValidator,
FilesystemBlobKeyValidator filesystemBlobKeyValidator) {
this.blobFactory = checkNotNull(blobFactory, "filesystem storage strategy blobfactory");
this.baseDirectory = checkNotNull(baseDir, "filesystem storage strategy base directory");
this.filesystemContainerNameValidator = checkNotNull(filesystemContainerNameValidator, "filesystem container name validator");
this.filesystemBlobKeyValidator = checkNotNull(filesystemBlobKeyValidator, "filesystem blob key validator");
}
@Override
public boolean containerExists(String container) {
filesystemContainerNameValidator.validate(container);
return directoryExists(container, null);
}
@Override
public boolean containerExists(String container) {
filesystemContainerNameValidator.validate(container);
return directoryExists(container, null);
}
@Override
public boolean blobExists(String container, String key) {
filesystemContainerNameValidator.validate(container);
filesystemBlobKeyValidator.validate(key);
return buildPathAndChecksIfFileExists(container, key);
}
@Override
public boolean blobExists(String container, String key) {
filesystemContainerNameValidator.validate(container);
filesystemBlobKeyValidator.validate(key);
return buildPathAndChecksIfFileExists(container, key);
}
@Override
public boolean createContainer(String container) {
logger.debug("Creating container %s", container);
filesystemContainerNameValidator.validate(container);
return createDirectoryWithResult(container, null);
}
@Override
public boolean createContainer(String container) {
logger.debug("Creating container %s", container);
filesystemContainerNameValidator.validate(container);
return createDirectoryWithResult(container, null);
}
@Override
public void deleteContainer(String container) {
filesystemContainerNameValidator.validate(container);
deleteDirectory(container, null);
}
@Override
public void deleteContainer(String container) {
filesystemContainerNameValidator.validate(container);
deleteDirectory(container, null);
}
/**
* Empty the directory of its content (files and subdirectories)
* @param container
*/
@Override
public void clearContainer(final String container) {
filesystemContainerNameValidator.validate(container);
clearContainer(container, ListContainerOptions.Builder.recursive());
}
@Override
public void clearContainer(String container, ListContainerOptions options) {
filesystemContainerNameValidator.validate(container);
//TODO
//now all is deleted, check it based on options
try {
File containerFile = openFolder(container);
File[] children = containerFile.listFiles();
if (null != children) {
for(File child:children) {
FileUtils.forceDelete(child);
}
}
} catch(IOException e) {
logger.error(e,"An error occurred while clearing container %s", container);
Throwables.propagate(e);
}
}
@Override
public Blob newBlob(@ParamValidators( { FilesystemBlobKeyValidator.class }) String name) {
filesystemBlobKeyValidator.validate(name);
Blob blob = blobFactory.create(null);
blob.getMetadata().setName(name);
return blob;
}
@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);
fileToBeDeleted.delete();
//now examins if the key of the blob is a complex key (with a directory structure)
//and eventually remove empty directory
removeDirectoriesTreeOfBlobKey(container, blobKey);
}
/**
* Return an iterator that reports all the containers under base path
* @return
*/
@Override
public Iterable<String> getAllContainerNames() {
Iterable<String> containers = new Iterable<String>() {
@Override
public Iterator<String> iterator() {
return new FileIterator(
buildPathStartingFromBaseDir(), DirectoryFileFilter.INSTANCE);
}
};
return containers;
}
/**
* Returns a {@link File} object that links to the blob
* @param container
* @param blobKey
* @return
*/
@Override
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;
}
/**
* Write a {@link Blob} {@link Payload} into a file
* @param container
* @param blobKey
* @param payload
* @throws IOException
*/
@Override
public void writePayloadOnFile(String container, String blobKey, Payload payload) throws IOException {
filesystemContainerNameValidator.validate(container);
filesystemBlobKeyValidator.validate(blobKey);
File outputFile = null;
OutputStream output = null;
InputStream input = null;
try {
outputFile = getFileForBlobKey(container, blobKey);
File parentDirectory = outputFile.getParentFile();
if (!parentDirectory.exists()) {
if (!parentDirectory.mkdirs()) {
throw new IOException("An error occurred creating directory [" + parentDirectory.getName() + "].");
}
}
output = new FileOutputStream(outputFile);
input = payload.getInput();
copy(input, output);
} catch (IOException ex) {
if (outputFile != null) {
outputFile.delete();
}
throw ex;
} finally {
if (input != null) {
try {
input.close();
} catch (IOException ex) {
// Does nothing
}
}
if (output != null) {
try {
output.close();
} catch (IOException ex) {
// Does nothing
}
}
}
}
/**
* 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
if (!containerExists(container)) {
return new HashSet<String>();
}
File containerFile = openFolder(container);
final int containerPathLenght = containerFile.getAbsolutePath().length() + 1;
Set<String> blobNames = new HashSet<String>() {
private static final long serialVersionUID = 3152191346558570795L;
@Override
public boolean add(String e) {
return super.add(e.substring(containerPathLenght));
}
};
populateBlobKeysInContainer(containerFile, blobNames);
return blobNames;
}
@Override
public boolean directoryExists(String container, String directory) {
return buildPathAndChecksIfDirectoryExists(container, directory);
}
@Override
public void createDirectory(String container, String directory) {
createDirectoryWithResult(container, directory);
}
@Override
public void deleteDirectory(String container, String directory) {
//create complete dir path
String fullDirPath = buildPathStartingFromBaseDir(container, directory);
try {
FileUtils.forceDelete(new File(fullDirPath));
} catch (IOException ex) {
logger.error("An error occurred removing directory %s.", fullDirPath);
Throwables.propagate(ex);
}
}
@Override
public long countBlobs(String container, ListContainerOptions options) {
//TODO
throw new UnsupportedOperationException("Not supported yet.");
}
//---------------------------------------------------------- Private methods
private boolean buildPathAndChecksIfFileExists(String...tokens) {
String path = buildPathStartingFromBaseDir(tokens);
File file = new File(path);
boolean exists = file.exists() || file.isFile();
return exists;
}
/**
* Check if the file system resource whose name is obtained applying buildPath
* on the input path tokens is a directory, otherwise a RuntimeException is thrown
/**
* Empty the directory of its content (files and subdirectories)
*
* @param tokens the tokens that make up the name of the resource on the
* file system
* @param container
*/
private boolean buildPathAndChecksIfDirectoryExists(String...tokens) {
String path = buildPathStartingFromBaseDir(tokens);
File file = new File(path);
boolean exists = file.exists() || file.isDirectory();
return exists;
}
@Override
public void clearContainer(final String container) {
filesystemContainerNameValidator.validate(container);
clearContainer(container, ListContainerOptions.Builder.recursive());
}
/**
* Facility method used to concatenate path tokens normalizing separators
* @param pathTokens all the string in the proper order that must be concatenated
* in order to obtain the filename
* @return the resulting string
*/
protected String buildPathStartingFromBaseDir(String...pathTokens) {
String normalizedToken = removeFileSeparatorFromBorders(normalize(baseDirectory), true);
StringBuilder completePath = new StringBuilder(normalizedToken);
if(pathTokens!=null && pathTokens.length>0) {
for(int i=0; i<pathTokens.length; i++) {
if(pathTokens[i]!=null) {
normalizedToken = removeFileSeparatorFromBorders(normalize(pathTokens[i]), false);
completePath.append(File.separator).append(normalizedToken);
}
@Override
public void clearContainer(String container, ListContainerOptions options) {
filesystemContainerNameValidator.validate(container);
// TODO
// now all is deleted, check it based on options
try {
File containerFile = openFolder(container);
File[] children = containerFile.listFiles();
if (null != children) {
for (File child : children) {
FileUtils.forceDelete(child);
}
}
return completePath.toString();
}
}
} catch (IOException e) {
logger.error(e, "An error occurred while clearing container %s", container);
Throwables.propagate(e);
}
}
/**
* Substitutes all the file separator occurrences in the path with a file
* separator for the current operative system
* @param pathToBeNormalized
* @return
*/
private String normalize(String pathToBeNormalized) {
if(null != pathToBeNormalized && pathToBeNormalized.contains(BACK_SLASH)) {
if(!BACK_SLASH.equals(File.separator)) {
return pathToBeNormalized.replaceAll(BACK_SLASH, File.separator);
@Override
public Blob newBlob(@ParamValidators({ FilesystemBlobKeyValidator.class }) String name) {
filesystemBlobKeyValidator.validate(name);
return blobBuilders.get().name(name).build();
}
@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);
fileToBeDeleted.delete();
// now examins if the key of the blob is a complex key (with a directory structure)
// and eventually remove empty directory
removeDirectoriesTreeOfBlobKey(container, blobKey);
}
/**
* Return an iterator that reports all the containers under base path
*
* @return
*/
@Override
public Iterable<String> getAllContainerNames() {
Iterable<String> containers = new Iterable<String>() {
@Override
public Iterator<String> iterator() {
return new FileIterator(buildPathStartingFromBaseDir(), DirectoryFileFilter.INSTANCE);
}
};
return containers;
}
/**
* Returns a {@link File} object that links to the blob
*
* @param container
* @param blobKey
* @return
*/
@Override
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;
}
/**
* Write a {@link Blob} {@link Payload} into a file
*
* @param container
* @param blobKey
* @param payload
* @throws IOException
*/
@Override
public void writePayloadOnFile(String container, String blobKey, Payload payload) throws IOException {
filesystemContainerNameValidator.validate(container);
filesystemBlobKeyValidator.validate(blobKey);
File outputFile = null;
OutputStream output = null;
InputStream input = null;
try {
outputFile = getFileForBlobKey(container, blobKey);
File parentDirectory = outputFile.getParentFile();
if (!parentDirectory.exists()) {
if (!parentDirectory.mkdirs()) {
throw new IOException("An error occurred creating directory [" + parentDirectory.getName() + "].");
}
}
return pathToBeNormalized;
}
}
output = new FileOutputStream(outputFile);
input = payload.getInput();
copy(input, output);
/**
* Remove leading and trailing {@link File.separator} character from the
* string.
* @param pathToBeCleaned
* @param remove only trailing separator char from path
* @return
*/
private String removeFileSeparatorFromBorders(String pathToBeCleaned, boolean onlyTrailing) {
if (null == pathToBeCleaned || pathToBeCleaned.equals("")) return pathToBeCleaned;
int beginIndex = 0;
int endIndex = pathToBeCleaned.length();
//search for separator chars
if (!onlyTrailing) {
if (pathToBeCleaned.substring(0, 1).equals(File.separator)) beginIndex = 1;
}
if (pathToBeCleaned.substring(pathToBeCleaned.length() - 1).equals(File.separator)) endIndex--;
return pathToBeCleaned.substring(beginIndex, endIndex);
}
/**
* Removes recursively the directory structure of a complex blob key, only
* if the directory is empty
* @param container
* @param normalizedKey
*/
private void removeDirectoriesTreeOfBlobKey(String container, String blobKey) {
String normalizedBlobKey = normalize(blobKey);
//exists is no path is present in the blobkey
if (!normalizedBlobKey.contains(File.separator)) return;
File file = new File(normalizedBlobKey);
//TODO
//"/media/data/works/java/amazon/jclouds/master/filesystem/aa/bb/cc/dd/eef6f0c8-0206-460b-8870-352e6019893c.txt"
String parentPath = file.getParent();
//no need to manage "/" parentPath, because "/" cannot be used as start
//char of blobkey
if (null != parentPath || "".equals(parentPath)) {
//remove parent directory only it's empty
File directory = new File(buildPathStartingFromBaseDir(container, parentPath));
String[] children = directory.list();
if (null == children || children.length == 0) {
directory.delete();
//recursively call for removing other path
removeDirectoriesTreeOfBlobKey(container, parentPath);
} catch (IOException ex) {
if (outputFile != null) {
outputFile.delete();
}
throw ex;
} finally {
if (input != null) {
try {
input.close();
} catch (IOException ex) {
// Does nothing
}
}
}
private File openFolder(String folderName) throws IOException {
String baseFolderName = buildPathStartingFromBaseDir(folderName);
File folder = new File(baseFolderName);
if(folder.exists()) {
if(!folder.isDirectory()) {
throw new IOException("Resource " + baseFolderName + " isn't a folder.");
}
if (output != null) {
try {
output.close();
} catch (IOException ex) {
// Does nothing
}
}
return folder;
}
}
}
}
/**
* 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
if (!containerExists(container)) {
return new HashSet<String>();
}
private class FileIterator implements Iterator<String>{
int currentFileIndex = 0;
File[] children = new File[0];
File currentFile = null;
File containerFile = openFolder(container);
final int containerPathLenght = containerFile.getAbsolutePath().length() + 1;
Set<String> blobNames = new HashSet<String>() {
public FileIterator(String fileName, FileFilter filter) {
File file = new File(fileName);
if(file.exists() && file.isDirectory()) {
children = file.listFiles(filter);
private static final long serialVersionUID = 3152191346558570795L;
@Override
public boolean add(String e) {
return super.add(e.substring(containerPathLenght));
}
};
populateBlobKeysInContainer(containerFile, blobNames);
return blobNames;
}
@Override
public boolean directoryExists(String container, String directory) {
return buildPathAndChecksIfDirectoryExists(container, directory);
}
@Override
public void createDirectory(String container, String directory) {
createDirectoryWithResult(container, directory);
}
@Override
public void deleteDirectory(String container, String directory) {
// create complete dir path
String fullDirPath = buildPathStartingFromBaseDir(container, directory);
try {
FileUtils.forceDelete(new File(fullDirPath));
} catch (IOException ex) {
logger.error("An error occurred removing directory %s.", fullDirPath);
Throwables.propagate(ex);
}
}
@Override
public long countBlobs(String container, ListContainerOptions options) {
// TODO
throw new UnsupportedOperationException("Not supported yet.");
}
// ---------------------------------------------------------- Private methods
private boolean buildPathAndChecksIfFileExists(String... tokens) {
String path = buildPathStartingFromBaseDir(tokens);
File file = new File(path);
boolean exists = file.exists() || file.isFile();
return exists;
}
/**
* Check if the file system resource whose name is obtained applying buildPath on the input path
* tokens is a directory, otherwise a RuntimeException is thrown
*
* @param tokens
* the tokens that make up the name of the resource on the file system
*/
private boolean buildPathAndChecksIfDirectoryExists(String... tokens) {
String path = buildPathStartingFromBaseDir(tokens);
File file = new File(path);
boolean exists = file.exists() || file.isDirectory();
return exists;
}
/**
* Facility method used to concatenate path tokens normalizing separators
*
* @param pathTokens
* all the string in the proper order that must be concatenated in order to obtain the
* filename
* @return the resulting string
*/
protected String buildPathStartingFromBaseDir(String... pathTokens) {
String normalizedToken = removeFileSeparatorFromBorders(normalize(baseDirectory), true);
StringBuilder completePath = new StringBuilder(normalizedToken);
if (pathTokens != null && pathTokens.length > 0) {
for (int i = 0; i < pathTokens.length; i++) {
if (pathTokens[i] != null) {
normalizedToken = removeFileSeparatorFromBorders(normalize(pathTokens[i]), false);
completePath.append(File.separator).append(normalizedToken);
}
}
}
}
return completePath.toString();
}
@Override
public boolean hasNext() {
return currentFileIndex<children.length;
}
/**
* Substitutes all the file separator occurrences in the path with a file separator for the
* current operative system
*
* @param pathToBeNormalized
* @return
*/
private String normalize(String pathToBeNormalized) {
if (null != pathToBeNormalized && pathToBeNormalized.contains(BACK_SLASH)) {
if (!BACK_SLASH.equals(File.separator)) {
return pathToBeNormalized.replaceAll(BACK_SLASH, File.separator);
}
}
return pathToBeNormalized;
}
@Override
public String next() {
currentFile = children[currentFileIndex++];
return currentFile.getName();
}
/**
* Remove leading and trailing {@link File.separator} character from the string.
*
* @param pathToBeCleaned
* @param remove
* only trailing separator char from path
* @return
*/
private String removeFileSeparatorFromBorders(String pathToBeCleaned, boolean onlyTrailing) {
if (null == pathToBeCleaned || pathToBeCleaned.equals(""))
return pathToBeCleaned;
@Override
public void remove() {
if(currentFile!=null && currentFile.exists()) {
if(!currentFile.delete()) {
throw new RuntimeException("An error occurred deleting "+currentFile.getName());
}
int beginIndex = 0;
int endIndex = pathToBeCleaned.length();
// search for separator chars
if (!onlyTrailing) {
if (pathToBeCleaned.substring(0, 1).equals(File.separator))
beginIndex = 1;
}
if (pathToBeCleaned.substring(pathToBeCleaned.length() - 1).equals(File.separator))
endIndex--;
return pathToBeCleaned.substring(beginIndex, endIndex);
}
/**
* Removes recursively the directory structure of a complex blob key, only if the directory is
* empty
*
* @param container
* @param normalizedKey
*/
private void removeDirectoriesTreeOfBlobKey(String container, String blobKey) {
String normalizedBlobKey = normalize(blobKey);
// exists is no path is present in the blobkey
if (!normalizedBlobKey.contains(File.separator))
return;
File file = new File(normalizedBlobKey);
// TODO
// "/media/data/works/java/amazon/jclouds/master/filesystem/aa/bb/cc/dd/eef6f0c8-0206-460b-8870-352e6019893c.txt"
String parentPath = file.getParent();
// no need to manage "/" parentPath, because "/" cannot be used as start
// char of blobkey
if (null != parentPath || "".equals(parentPath)) {
// remove parent directory only it's empty
File directory = new File(buildPathStartingFromBaseDir(container, parentPath));
String[] children = directory.list();
if (null == children || children.length == 0) {
directory.delete();
// recursively call for removing other path
removeDirectoriesTreeOfBlobKey(container, parentPath);
}
}
}
private File openFolder(String folderName) throws IOException {
String baseFolderName = buildPathStartingFromBaseDir(folderName);
File folder = new File(baseFolderName);
if (folder.exists()) {
if (!folder.isDirectory()) {
throw new IOException("Resource " + baseFolderName + " isn't a folder.");
}
}
return folder;
}
private class FileIterator implements Iterator<String> {
int currentFileIndex = 0;
File[] children = new File[0];
File currentFile = null;
public FileIterator(String fileName, FileFilter filter) {
File file = new File(fileName);
if (file.exists() && file.isDirectory()) {
children = file.listFiles(filter);
}
}
@Override
public boolean hasNext() {
return currentFileIndex < children.length;
}
@Override
public String next() {
currentFile = children[currentFileIndex++];
return currentFile.getName();
}
@Override
public void remove() {
if (currentFile != null && currentFile.exists()) {
if (!currentFile.delete()) {
throw new RuntimeException("An error occurred deleting " + currentFile.getName());
}
}
}
}
}
}
private void populateBlobKeysInContainer(File directory, Set<String> blobNames) {
File[] children = directory.listFiles();
for (File child : children) {
if (child.isFile()) {
blobNames.add(child.getAbsolutePath());
} else if (child.isDirectory()) {
populateBlobKeysInContainer(child, blobNames);
}
}
}
private void populateBlobKeysInContainer(File directory, Set<String> blobNames) {
File[] children = directory.listFiles();
for(File child:children) {
if(child.isFile()) {
blobNames.add(child.getAbsolutePath());
} else if(child.isDirectory()) {
populateBlobKeysInContainer(child, blobNames);
}
}
}
/**
* Creates a directory and returns the result
*
* @param container
* @param directory
* @return true if the directory was created, otherwise false
*/
protected boolean createDirectoryWithResult(String container, String directory) {
String directoryFullName = buildPathStartingFromBaseDir(container, directory);
logger.debug("Creating directory %s", directoryFullName);
// cannot use directoryFullName, because the following method rebuild
// another time the path starting from base directory
if (buildPathAndChecksIfDirectoryExists(container, directory)) {
logger.debug("Directory %s already exists", directoryFullName);
return false;
}
/**
* Creates a directory and returns the result
* @param container
* @param directory
* @return true if the directory was created, otherwise false
*/
protected boolean createDirectoryWithResult(String container, String directory) {
String directoryFullName = buildPathStartingFromBaseDir(container, directory);
logger.debug("Creating directory %s", directoryFullName);
File directoryToCreate = new File(directoryFullName);
boolean result = directoryToCreate.mkdirs();
return result;
}
//cannot use directoryFullName, because the following method rebuild
//another time the path starting from base directory
if (buildPathAndChecksIfDirectoryExists(container, directory)) {
logger.debug("Directory %s already exists", directoryFullName);
return false;
}
File directoryToCreate = new File(directoryFullName);
boolean result = directoryToCreate.mkdirs();
return result;
}
/**
* Copy from an InputStream to an OutputStream.
*
* @param input The InputStream
* @param output The OutputStream
* @return the number of bytes copied
* @throws IOException if an error occurs
*/
private long copy(InputStream input, OutputStream output)
throws IOException {
byte[] buffer = new byte[COPY_BUFFER_SIZE];
long count = 0;
while (true) {
int read = input.read(buffer);
if (read < 0) {
break;
}
count += read;
output.write(buffer, 0, read);
}
output.flush();
return count;
}
/**
* Copy from an InputStream to an OutputStream.
*
* @param input
* The InputStream
* @param output
* The OutputStream
* @return the number of bytes copied
* @throws IOException
* if an error occurs
*/
private long copy(InputStream input, OutputStream output) throws IOException {
byte[] buffer = new byte[COPY_BUFFER_SIZE];
long count = 0;
while (true) {
int read = input.read(buffer);
if (read < 0) {
break;
}
count += read;
output.write(buffer, 0, read);
}
output.flush();
return count;
}
}

View File

@ -19,14 +19,18 @@
package org.jclouds.filesystem.util.internal;
import com.google.inject.Inject;
import static com.google.common.base.Preconditions.checkNotNull;
import javax.inject.Provider;
import org.jclouds.blobstore.AsyncBlobStore;
import org.jclouds.blobstore.domain.Blob;
import org.jclouds.blobstore.domain.BlobBuilder;
import org.jclouds.blobstore.options.ListContainerOptions;
import org.jclouds.blobstore.util.BlobUtils;
import org.jclouds.filesystem.strategy.FilesystemStorageStrategy;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.inject.Inject;
/**
* Implements the {@link BlobUtils} interfaced and act as a bridge to
@ -36,44 +40,48 @@ import static com.google.common.base.Preconditions.checkNotNull;
*/
public class FileSystemBlobUtilsImpl implements BlobUtils {
protected final FilesystemStorageStrategy storageStrategy;
protected final FilesystemStorageStrategy storageStrategy;
protected final Provider<BlobBuilder> blobBuilders;
@Inject
public FileSystemBlobUtilsImpl(
FilesystemStorageStrategy storageStrategy) {
this.storageStrategy = checkNotNull(storageStrategy, "Filesystem Storage Strategy");
}
@Inject
public FileSystemBlobUtilsImpl(FilesystemStorageStrategy storageStrategy, Provider<BlobBuilder> blobBuilders) {
this.storageStrategy = checkNotNull(storageStrategy, "Filesystem Storage Strategy");
this.blobBuilders = checkNotNull(blobBuilders, "Filesystem blobBuilders");
}
@Override
public Blob newBlob(String name) {
return blobBuilder().name(name).build();
}
@Override
public BlobBuilder blobBuilder() {
return blobBuilders.get();
}
@Override
public Blob newBlob(String name) {
return storageStrategy.newBlob(name);
}
@Override
public boolean directoryExists(String containerName, String directory) {
return storageStrategy.directoryExists(containerName, directory);
}
@Override
public boolean directoryExists(String containerName, String directory) {
return storageStrategy.directoryExists(containerName, directory);
}
@Override
public void createDirectory(String containerName, String directory) {
storageStrategy.createDirectory(containerName, directory);
}
@Override
public void createDirectory(String containerName, String directory) {
storageStrategy.createDirectory(containerName, directory);
}
@Override
public long countBlobs(String container, ListContainerOptions options) {
return storageStrategy.countBlobs(container, options);
}
@Override
public long countBlobs(String container, ListContainerOptions options) {
return storageStrategy.countBlobs(container, options);
}
@Override
public void clearContainer(String container, ListContainerOptions options) {
storageStrategy.clearContainer(container, options);
}
@Override
public void clearContainer(String container, ListContainerOptions options) {
storageStrategy.clearContainer(container, options);
}
@Override
public void deleteDirectory(String container, String directory) {
storageStrategy.deleteDirectory(container, directory);
}
@Override
public void deleteDirectory(String container, String directory) {
storageStrategy.deleteDirectory(container, directory);
}
}

View File

@ -31,6 +31,7 @@ import org.jclouds.blobstore.domain.StorageMetadata;
import org.jclouds.blobstore.integration.internal.BaseContainerIntegrationTest;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
/**
@ -45,19 +46,16 @@ public class FilesystemContainerIntegrationTest extends BaseContainerIntegration
String key = "hello";
Blob object = context.getBlobStore().newBlob(key);
object.setPayload(TEST_STRING);
object.getMetadata().getContentMetadata().setContentType(MediaType.TEXT_PLAIN);
// NOTE all metadata in jclouds comes out as lowercase, in an effort to normalize the
// providers.
object.getMetadata().getUserMetadata().put("Adrian", "powderpuff");
Blob object = context.getBlobStore().blobBuilder(key).userMetadata(ImmutableMap.of("Adrian", "powderpuff"))
.payload(TEST_STRING).contentType(MediaType.TEXT_PLAIN).build();
String containerName = getContainerName();
try {
addBlobToContainer(containerName, object);
validateContent(containerName, key);
PageSet<? extends StorageMetadata> container = context.getBlobStore().list(containerName,
maxResults(1));
PageSet<? extends StorageMetadata> container = context.getBlobStore().list(containerName, maxResults(1));
BlobMetadata metadata = (BlobMetadata) Iterables.getOnlyElement(container);
// transient container should be lenient and not return metadata on undetailed listing.

View File

@ -35,7 +35,7 @@ import com.google.common.base.Function;
*/
@Singleton
public class ObjectToBlob implements Function<S3Object, Blob> {
private final Blob.Factory blobFactory;
private final Factory blobFactory;
private final ObjectToBlobMetadata object2BlobMd;
@Inject

View File

@ -35,7 +35,7 @@ import com.google.common.base.Function;
*/
@Singleton
public class ObjectToBlob implements Function<SwiftObject, Blob> {
private final Blob.Factory blobFactory;
private final Factory blobFactory;
private final ObjectToBlobMetadata object2BlobMd;
@Inject

View File

@ -24,6 +24,7 @@ import java.util.Set;
import javax.annotation.Nullable;
import org.jclouds.blobstore.domain.Blob;
import org.jclouds.blobstore.domain.BlobBuilder;
import org.jclouds.blobstore.domain.BlobMetadata;
import org.jclouds.blobstore.domain.PageSet;
import org.jclouds.blobstore.domain.StorageMetadata;
@ -47,8 +48,14 @@ public interface AsyncBlobStore {
/**
* @see BlobStore#newBlob
*/
@Deprecated
Blob newBlob(String name);
/**
* @see BlobStore#blobBuilder
*/
BlobBuilder blobBuilder(String name);
/**
* @see BlobStore#listAssignableLocations
*/

View File

@ -20,6 +20,7 @@
package org.jclouds.blobstore;
import org.jclouds.blobstore.domain.Blob;
import org.jclouds.blobstore.domain.BlobBuilder;
import org.jclouds.blobstore.internal.BlobMapImpl;
import org.jclouds.blobstore.options.ListContainerOptions;
@ -34,9 +35,19 @@ import com.google.inject.ImplementedBy;
*/
@ImplementedBy(BlobMapImpl.class)
public interface BlobMap extends ListableMap<String, Blob> {
/**
* @see #blobBuilder
* @param name
*/
@Deprecated
Blob newBlob(String name);
/**
*
* @return builder for creating new {@link Blob}s
*/
BlobBuilder blobBuilder();
public static interface Factory {
BlobMap create(String containerName, ListContainerOptions options);
}

View File

@ -24,6 +24,7 @@ import java.util.Set;
import javax.annotation.Nullable;
import org.jclouds.blobstore.domain.Blob;
import org.jclouds.blobstore.domain.BlobBuilder;
import org.jclouds.blobstore.domain.BlobMetadata;
import org.jclouds.blobstore.domain.PageSet;
import org.jclouds.blobstore.domain.StorageMetadata;
@ -47,9 +48,17 @@ public interface BlobStore {
/**
* creates a new blob with the specified name.
* @see #blobBuilder
*/
@Deprecated
Blob newBlob(String name);
/**
*
* @return builder for creating new {@link Blob}s
*/
BlobBuilder blobBuilder(String name);
/**
* The get locations command returns all the valid locations for containers. A location has a
* scope, which is typically region or zone. A region is a general area, like eu-west, where a

View File

@ -19,6 +19,7 @@
package org.jclouds.blobstore;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.base.Throwables.getCausalChain;
@ -125,7 +126,7 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore {
protected TransientAsyncBlobStore(BlobStoreContext context, DateService dateService, Crypto crypto,
ConcurrentMap<String, ConcurrentMap<String, Blob>> containerToBlobs,
ConcurrentMap<String, Location> containerToLocation, HttpGetOptionsListToGetOptions httpGetOptionsConverter,
IfDirectoryReturnNameStrategy ifDirectoryReturnName, Blob.Factory blobFactory, BlobUtils blobUtils,
IfDirectoryReturnNameStrategy ifDirectoryReturnName, Factory blobFactory, BlobUtils blobUtils,
@Named(Constants.PROPERTY_USER_THREADS) ExecutorService service, Supplier<Location> defaultLocation,
@Memoized Supplier<Set<? extends Location>> locations) {
super(context, blobUtils, service, defaultLocation, locations);
@ -487,6 +488,8 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore {
*/
@Override
public ListenableFuture<String> putBlob(String containerName, Blob in) {
checkArgument(containerName != null, "containerName must be set");
checkArgument(in != null, "blob must be set");
ConcurrentMap<String, Blob> container = getContainerToBlobs().get(containerName);
if (container == null) {
new IllegalStateException("containerName not found: " + containerName);
@ -513,6 +516,8 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore {
}
protected Blob createUpdatedCopyOfBlob(Blob in) {
checkNotNull(in, "blob");
checkNotNull(in.getPayload(), "blob.payload");
ByteArrayPayload payload = (in.getPayload() instanceof ByteArrayPayload) ? ByteArrayPayload.class.cast(in
.getPayload()) : null;
if (payload == null)

View File

@ -20,11 +20,12 @@
package org.jclouds.blobstore.config;
import javax.inject.Inject;
import javax.inject.Provider;
import org.jclouds.blobstore.BlobMap;
import org.jclouds.blobstore.BlobStore;
import org.jclouds.blobstore.InputStreamMap;
import org.jclouds.blobstore.domain.Blob;
import org.jclouds.blobstore.domain.BlobBuilder;
import org.jclouds.blobstore.internal.BlobMapImpl;
import org.jclouds.blobstore.internal.InputStreamMapImpl;
import org.jclouds.blobstore.options.ListContainerOptions;
@ -65,10 +66,12 @@ public class BlobStoreMapModule extends AbstractModule {
PutBlobsStrategy putBlobsStrategy;
@Inject
ListContainerAndRecurseThroughFolders listStrategy;
@Inject
Provider<BlobBuilder> blobBuilders;
public BlobMap create(String containerName, ListContainerOptions options) {
return new BlobMapImpl(connection, getAllBlobs, containsValueStrategy, putBlobsStrategy,
listStrategy, containerName, options);
return new BlobMapImpl(connection, getAllBlobs, containsValueStrategy, putBlobsStrategy, listStrategy,
containerName, options, blobBuilders);
}
}
@ -77,7 +80,7 @@ public class BlobStoreMapModule extends AbstractModule {
@Inject
BlobStore connection;
@Inject
Blob.Factory blobFactory;
Provider<BlobBuilder> blobBuilders;
@Inject
GetBlobsInListStrategy getAllBlobs;
@Inject
@ -90,9 +93,8 @@ public class BlobStoreMapModule extends AbstractModule {
ListContainerAndRecurseThroughFolders listStrategy;
public InputStreamMap create(String containerName, ListContainerOptions options) {
return new InputStreamMapImpl(connection, blobFactory, getAllBlobs, listStrategy,
containsValueStrategy, putBlobsStrategy, containerName, options,
crypto);
return new InputStreamMapImpl(connection, blobBuilders, getAllBlobs, listStrategy, containsValueStrategy,
putBlobsStrategy, containerName, options, crypto);
}
}

View File

@ -0,0 +1,123 @@
/**
*
* Copyright (C) 2010 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ====================================================================
*/
package org.jclouds.blobstore.domain;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import org.jclouds.blobstore.domain.internal.BlobBuilderImpl;
import org.jclouds.io.Payload;
import org.jclouds.io.Payloads;
import com.google.inject.ImplementedBy;
/**
*
* In case the name was confusing, this indeed builds a Blob.
*
* @author Adrian Cole
*/
@ImplementedBy(BlobBuilderImpl.class)
public interface BlobBuilder {
/**
* @param name
* The name of the {@link Blob}. Typically refers to an http path.
*/
BlobBuilder name(String name);
/**
* @param type
* overrides default type of {@link StorageType#BLOB}
*/
BlobBuilder type(StorageType type);
/**
* @param userMetadata
* User defined metadata associated with this {@link Blob}.
*
*/
BlobBuilder userMetadata(Map<String, String> userMetadata);
/**
*
* @param payload
* payload you wish to construct the {@link Blob} with.
*/
PayloadBlobBuilder payload(Payload payload);
/**
*
* @param payload
* payload you wish to construct the {@link Blob} with.
*/
PayloadBlobBuilder payload(InputStream payload);
/**
*
* @param payload
* payload you wish to construct the {@link Blob} with.
*/
PayloadBlobBuilder payload(byte[] payload);
/**
*
* @param payload
* payload you wish to construct the {@link Blob} with.
*/
PayloadBlobBuilder payload(String payload);
/**
*
* @param payload
* payload you wish to construct the {@link Blob} with.
*/
PayloadBlobBuilder payload(File payload);
/**
* This makes a blob from the currently configured parameters.
*
* @return a new blob from the current parameters
*/
Blob build();
public interface PayloadBlobBuilder extends BlobBuilder {
PayloadBlobBuilder contentLength(long contentLength);
PayloadBlobBuilder contentMD5(byte[] md5);
PayloadBlobBuilder contentType(String contentType);
PayloadBlobBuilder contentDisposition(String contentDisposition);
PayloadBlobBuilder contentLanguage(String contentLanguage);
PayloadBlobBuilder contentEncoding(String contentEncoding);
/**
*
* @see Payloads#calculateMD5
*/
PayloadBlobBuilder calculateMD5() throws IOException;
}
}

View File

@ -0,0 +1,229 @@
/**
*
* Copyright (C) 2010 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ====================================================================
*/
package org.jclouds.blobstore.domain.internal;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.jclouds.io.Payloads.newPayload;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Map;
import javax.annotation.Nullable;
import javax.inject.Inject;
import org.jclouds.blobstore.domain.Blob;
import org.jclouds.blobstore.domain.BlobBuilder;
import org.jclouds.blobstore.domain.StorageType;
import org.jclouds.crypto.Crypto;
import org.jclouds.io.Payload;
import org.jclouds.io.Payloads;
import com.google.common.base.Throwables;
import com.google.common.collect.Maps;
/**
* @author Adrian Cole
*/
public class BlobBuilderImpl implements BlobBuilder {
private Payload payload;
private String name;
private Map<String, String> userMetadata = Maps.newLinkedHashMap();
private StorageType type = StorageType.BLOB;
@Inject
private Crypto crypto;
@Override
public BlobBuilder name(String name) {
this.name = name;
return this;
}
@Override
public BlobBuilder type(StorageType type) {
this.type = type;
return this;
}
@Override
public BlobBuilder userMetadata(Map<String, String> userMetadata) {
this.userMetadata = Maps.newLinkedHashMap(checkNotNull(userMetadata, "userMetadata"));
return this;
}
@Override
public PayloadBlobBuilder payload(Payload payload) {
this.payload = payload;
return new PayloadBlobBuilderImpl(this, payload, crypto);
}
/**
* {@inheritDoc}
*/
@Override
public PayloadBlobBuilder payload(InputStream data) {
return payload(newPayload(checkNotNull(data, "data")));
}
/**
* {@inheritDoc}
*/
@Override
public PayloadBlobBuilder payload(byte[] data) {
return payload(newPayload(checkNotNull(data, "data")));
}
/**
* {@inheritDoc}
*/
@Override
public PayloadBlobBuilder payload(String data) {
return payload(newPayload(checkNotNull(data, "data")));
}
/**
* {@inheritDoc}
*/
@Override
public PayloadBlobBuilder payload(File data) {
return payload(newPayload(checkNotNull(data, "data")));
}
@Override
public Blob build() {
Blob blob = new BlobImpl(new MutableBlobMetadataImpl());
if (name != null)
blob.getMetadata().setName(name);
if (payload != null)
blob.setPayload(payload);
blob.getMetadata().setUserMetadata(userMetadata);
blob.getMetadata().setType(type);
return blob;
}
public class PayloadBlobBuilderImpl implements PayloadBlobBuilder {
private final BlobBuilder builder;
private final Payload payload;
private MessageDigest digest;
public PayloadBlobBuilderImpl(BlobBuilder builder, Payload payload, @Nullable Crypto crypto) {
this.builder = checkNotNull(builder, "builder");
this.payload = checkNotNull(payload, "payload");
try {
this.digest = crypto != null ? crypto.md5() : MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
Throwables.propagate(e);
this.digest = null;
}
}
@Override
public BlobBuilder name(String name) {
return builder.name(name);
}
@Override
public BlobBuilder type(StorageType type) {
return builder.type(type);
}
@Override
public BlobBuilder userMetadata(Map<String, String> userMetadata) {
return builder.userMetadata(userMetadata);
}
@Override
public PayloadBlobBuilder payload(Payload payload) {
return builder.payload(payload);
}
@Override
public PayloadBlobBuilder calculateMD5() throws IOException {
return builder.payload(Payloads.calculateMD5(payload, digest));
}
@Override
public PayloadBlobBuilder payload(InputStream payload) {
return builder.payload(payload);
}
@Override
public PayloadBlobBuilder payload(byte[] payload) {
return builder.payload(payload);
}
@Override
public PayloadBlobBuilder payload(String payload) {
return builder.payload(payload);
}
@Override
public PayloadBlobBuilder payload(File payload) {
return builder.payload(payload);
}
@Override
public Blob build() {
return builder.build();
}
@Override
public PayloadBlobBuilder contentLength(long contentLength) {
payload.getContentMetadata().setContentLength(contentLength);
return this;
}
@Override
public PayloadBlobBuilder contentMD5(byte[] md5) {
payload.getContentMetadata().setContentMD5(md5);
return this;
}
@Override
public PayloadBlobBuilder contentType(String contentType) {
payload.getContentMetadata().setContentType(contentType);
return this;
}
@Override
public PayloadBlobBuilder contentDisposition(String contentDisposition) {
payload.getContentMetadata().setContentDisposition(contentDisposition);
return this;
}
@Override
public PayloadBlobBuilder contentLanguage(String contentLanguage) {
payload.getContentMetadata().setContentLanguage(contentLanguage);
return this;
}
@Override
public PayloadBlobBuilder contentEncoding(String contentEncoding) {
payload.getContentMetadata().setContentEncoding(contentEncoding);
return this;
}
}
}

View File

@ -34,6 +34,7 @@ import org.jclouds.blobstore.AsyncBlobStore;
import org.jclouds.blobstore.BlobStoreContext;
import org.jclouds.blobstore.ContainerNotFoundException;
import org.jclouds.blobstore.domain.Blob;
import org.jclouds.blobstore.domain.BlobBuilder;
import org.jclouds.blobstore.domain.PageSet;
import org.jclouds.blobstore.domain.StorageMetadata;
import org.jclouds.blobstore.options.ListContainerOptions;
@ -61,8 +62,8 @@ public abstract class BaseAsyncBlobStore implements AsyncBlobStore {
@Inject
protected BaseAsyncBlobStore(BlobStoreContext context, BlobUtils blobUtils,
@Named(Constants.PROPERTY_USER_THREADS) ExecutorService service, Supplier<Location> defaultLocation,
@Memoized Supplier<Set<? extends Location>> locations) {
@Named(Constants.PROPERTY_USER_THREADS) ExecutorService service, Supplier<Location> defaultLocation,
@Memoized Supplier<Set<? extends Location>> locations) {
this.context = checkNotNull(context, "context");
this.blobUtils = checkNotNull(blobUtils, "blobUtils");
this.service = checkNotNull(service, "service");
@ -83,6 +84,14 @@ public abstract class BaseAsyncBlobStore implements AsyncBlobStore {
return blobUtils.newBlob(name);
}
/**
* invokes {@link BlobUtilsImpl#blobBuilder }
*/
@Override
public BlobBuilder blobBuilder(String name) {
return blobUtils.blobBuilder().name(name);
}
/**
* This implementation invokes
* {@link #list(String,org.jclouds.blobstore.options.ListContainerOptions)}
@ -201,12 +210,12 @@ public abstract class BaseAsyncBlobStore implements AsyncBlobStore {
public ListenableFuture<Void> createDirectory(final String containerName, final String directory) {
return blobUtils.directoryExists(containerName, directory) ? Futures.immediateFuture((Void) null)
: org.jclouds.concurrent.Futures.makeListenable(service.submit(new Callable<Void>() {
public Void call() throws Exception {
blobUtils.createDirectory(containerName, directory);
return null;
}
}), service);
: org.jclouds.concurrent.Futures.makeListenable(service.submit(new Callable<Void>() {
public Void call() throws Exception {
blobUtils.createDirectory(containerName, directory);
return null;
}
}), service);
}
/**

View File

@ -19,12 +19,16 @@
package org.jclouds.blobstore.internal;
import static com.google.common.base.Functions.identity;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.Iterables.transform;
import java.util.Map;
import java.util.Set;
import javax.inject.Inject;
import org.jclouds.blobstore.BlobStore;
import org.jclouds.blobstore.ListableMap;
import org.jclouds.blobstore.domain.Blob;
@ -39,9 +43,7 @@ import org.jclouds.blobstore.strategy.PutBlobsStrategy;
import org.jclouds.blobstore.strategy.internal.ListContainerAndRecurseThroughFolders;
import com.google.common.base.Function;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import com.google.inject.Inject;
import com.google.common.collect.ImmutableSet;
/**
* Implements core Map functionality with a {@link BlobStore}
@ -88,26 +90,19 @@ public abstract class BaseBlobMap<V> implements ListableMap<String, V> {
}
}
static class PassThrough<T> implements Function<T, T> {
public T apply(T from) {
return from;
}
}
@Inject
public BaseBlobMap(BlobStore blobstore, GetBlobsInListStrategy getAllBlobs,
ContainsValueInListStrategy containsValueStrategy, PutBlobsStrategy putBlobsStrategy,
ListContainerAndRecurseThroughFolders listStrategy, String containerName,
ListContainerOptions options) {
ContainsValueInListStrategy containsValueStrategy, PutBlobsStrategy putBlobsStrategy,
ListContainerAndRecurseThroughFolders listStrategy, String containerName, ListContainerOptions options) {
this.blobstore = checkNotNull(blobstore, "blobstore");
this.containerName = checkNotNull(containerName, "container");
checkArgument(containerName.indexOf('/') == -1,
"please specify directory path using the option: inDirectory, not encoded in the container name");
"please specify directory path using the option: inDirectory, not encoded in the container name");
this.options = checkNotNull(options, "options") instanceof ImmutableListContainerOptions ? options
: new ImmutableListContainerOptions(options);
: new ImmutableListContainerOptions(options);
String dir = options.getDir();
if (dir == null) {
prefixer = new PassThrough<String>();
prefixer = identity();
pathStripper = prefixer;
} else {
prefixer = new PrefixKey(dir, "/");
@ -122,13 +117,12 @@ public abstract class BaseBlobMap<V> implements ListableMap<String, V> {
@Override
public Set<java.util.Map.Entry<String, V>> entrySet() {
return Sets.newHashSet(Iterables.transform(list(),
new Function<BlobMetadata, Map.Entry<String, V>>() {
@Override
public java.util.Map.Entry<String, V> apply(BlobMetadata from) {
return new Entry(pathStripper.apply(from.getName()));
}
}));
return ImmutableSet.copyOf(transform(list(), new Function<BlobMetadata, Map.Entry<String, V>>() {
@Override
public java.util.Map.Entry<String, V> apply(BlobMetadata from) {
return new Entry(pathStripper.apply(from.getName()));
}
}));
}
public class Entry implements java.util.Map.Entry<String, V> {
@ -187,7 +181,7 @@ public abstract class BaseBlobMap<V> implements ListableMap<String, V> {
@Override
public Set<String> keySet() {
return Sets.newHashSet(Iterables.transform(list(), new Function<BlobMetadata, String>() {
return ImmutableSet.copyOf(transform(list(), new Function<BlobMetadata, String>() {
@Override
public String apply(BlobMetadata from) {
return from.getName();
@ -197,7 +191,7 @@ public abstract class BaseBlobMap<V> implements ListableMap<String, V> {
@Override
public boolean containsKey(Object key) {
String realKey = prefixer.apply(key.toString());
String realKey = prefixer.apply(checkNotNull(key, "key").toString());
return blobstore.blobExists(containerName, realKey);
}
@ -207,21 +201,20 @@ public abstract class BaseBlobMap<V> implements ListableMap<String, V> {
}
public Iterable<? extends BlobMetadata> list() {
return Iterables.transform(listStrategy.execute(containerName, options),
new Function<BlobMetadata, BlobMetadata>() {
public BlobMetadata apply(BlobMetadata from) {
MutableBlobMetadata md = new MutableBlobMetadataImpl(from);
if (options.getDir() != null)
md.setName(pathStripper.apply(from.getName()));
return md;
}
return transform(listStrategy.execute(containerName, options), new Function<BlobMetadata, BlobMetadata>() {
public BlobMetadata apply(BlobMetadata from) {
MutableBlobMetadata md = new MutableBlobMetadataImpl(from);
if (options.getDir() != null)
md.setName(pathStripper.apply(from.getName()));
return md;
}
});
});
}
@Override
public String toString() {
return "BaseBlobMap [containerName=" + containerName + ", options=" + options + "]";
return "[containerName=" + containerName + ", options=" + options + "]";
}
}

View File

@ -30,6 +30,7 @@ import org.jclouds.blobstore.BlobStore;
import org.jclouds.blobstore.BlobStoreContext;
import org.jclouds.blobstore.ContainerNotFoundException;
import org.jclouds.blobstore.domain.Blob;
import org.jclouds.blobstore.domain.BlobBuilder;
import org.jclouds.blobstore.domain.PageSet;
import org.jclouds.blobstore.domain.StorageMetadata;
import org.jclouds.blobstore.options.ListContainerOptions;
@ -54,7 +55,7 @@ public abstract class BaseBlobStore implements BlobStore {
@Inject
protected BaseBlobStore(BlobStoreContext context, BlobUtils blobUtils, Supplier<Location> defaultLocation,
@Memoized Supplier<Set<? extends Location>> locations) {
@Memoized Supplier<Set<? extends Location>> locations) {
this.context = checkNotNull(context, "context");
this.blobUtils = checkNotNull(blobUtils, "blobUtils");
this.defaultLocation = checkNotNull(defaultLocation, "defaultLocation");
@ -74,6 +75,14 @@ public abstract class BaseBlobStore implements BlobStore {
return blobUtils.newBlob(name);
}
/**
* invokes {@link BlobUtilsImpl#blobBuilder }
*/
@Override
public BlobBuilder blobBuilder(String name) {
return blobUtils.blobBuilder().name(name);
}
/**
* This implementation invokes
* {@link #list(String,org.jclouds.blobstore.options.ListContainerOptions)}

View File

@ -19,22 +19,28 @@
package org.jclouds.blobstore.internal;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.Iterables.transform;
import java.util.Collection;
import java.util.Map;
import javax.inject.Inject;
import javax.inject.Provider;
import org.jclouds.blobstore.BlobMap;
import org.jclouds.blobstore.BlobStore;
import org.jclouds.blobstore.KeyNotFoundException;
import org.jclouds.blobstore.domain.Blob;
import org.jclouds.blobstore.domain.BlobBuilder;
import org.jclouds.blobstore.options.ListContainerOptions;
import org.jclouds.blobstore.strategy.ContainsValueInListStrategy;
import org.jclouds.blobstore.strategy.GetBlobsInListStrategy;
import org.jclouds.blobstore.strategy.PutBlobsStrategy;
import org.jclouds.blobstore.strategy.internal.ListContainerAndRecurseThroughFolders;
import com.google.common.collect.Sets;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableSet;
/**
* Map representation of a live connection to a Blob Service.
@ -45,36 +51,59 @@ import com.google.common.collect.Sets;
* @author Adrian Cole
*/
public class BlobMapImpl extends BaseBlobMap<Blob> implements BlobMap {
public static class CorrectBlobName implements Function<java.util.Map.Entry<? extends String, ? extends Blob>, Blob> {
private final Function<String, String> prefixer;
public CorrectBlobName(Function<String, String> prefixer) {
this.prefixer = checkNotNull(prefixer, "prefixer");
}
@Override
public Blob apply(java.util.Map.Entry<? extends String, ? extends Blob> arg0) {
return apply(arg0.getKey(), arg0.getValue());
}
public Blob apply(String key, Blob blob) {
blob.getMetadata().setName(prefixer.apply(key));
return blob;
}
}
private final CorrectBlobName correctBlobName;
private final Provider<BlobBuilder> blobBuilders;
@Inject
public BlobMapImpl(BlobStore blobstore, GetBlobsInListStrategy getAllBlobs,
ContainsValueInListStrategy containsValueStrategy, PutBlobsStrategy putBlobsStrategy,
ListContainerAndRecurseThroughFolders listStrategy, String containerName, ListContainerOptions options) {
ContainsValueInListStrategy containsValueStrategy, PutBlobsStrategy putBlobsStrategy,
ListContainerAndRecurseThroughFolders listStrategy, String containerName, ListContainerOptions options,
Provider<BlobBuilder> blobBuilders) {
super(blobstore, getAllBlobs, containsValueStrategy, putBlobsStrategy, listStrategy, containerName, options);
this.correctBlobName = new CorrectBlobName(prefixer);
this.blobBuilders = checkNotNull(blobBuilders, "blobBuilders");
}
@Override
public Blob get(Object key) {
String realKey = prefixer.apply(key.toString());
String realKey = prefixer.apply(checkNotNull(key, "key").toString());
Blob blob = blobstore.getBlob(containerName, realKey);
return blob != null ? stripPrefix(blob) : null;
}
@Override
public Blob put(String key, Blob value) {
Blob returnVal = getLastValue(key);
blobstore.putBlob(containerName, value);
Blob returnVal = getLastValue(checkNotNull(key, "key"));
blobstore.putBlob(containerName, correctBlobName.apply(key, value));
return returnVal;
}
@Override
public void putAll(Map<? extends String, ? extends Blob> map) {
putBlobsStrategy.execute(containerName, map.values());
putBlobsStrategy.execute(containerName, transform(checkNotNull(map, "map").entrySet(), correctBlobName));
}
@Override
public Blob remove(Object key) {
Blob old = getLastValue(key);
Blob old = getLastValue(checkNotNull(key, "key"));
String realKey = prefixer.apply(key.toString());
blobstore.removeBlob(containerName, realKey);
return old;
@ -83,7 +112,7 @@ public class BlobMapImpl extends BaseBlobMap<Blob> implements BlobMap {
private Blob getLastValue(Object key) {
Blob old;
try {
old = get(key);
old = get(checkNotNull(key, "key"));
} catch (KeyNotFoundException e) {
old = null;
}
@ -92,12 +121,16 @@ public class BlobMapImpl extends BaseBlobMap<Blob> implements BlobMap {
@Override
public Collection<Blob> values() {
return Sets.newLinkedHashSet(getAllBlobs.execute(containerName, options));
return ImmutableSet.copyOf(getAllBlobs.execute(containerName, options));
}
@Override
public Blob newBlob(String name) {
return blobstore.newBlob(name);
return blobBuilder().name(name).build();
}
@Override
public BlobBuilder blobBuilder() {
return blobBuilders.get();
}
}

View File

@ -30,11 +30,13 @@ import java.util.Collection;
import java.util.Map;
import javax.inject.Inject;
import javax.inject.Provider;
import org.jclouds.blobstore.BlobMap;
import org.jclouds.blobstore.BlobStore;
import org.jclouds.blobstore.InputStreamMap;
import org.jclouds.blobstore.domain.Blob;
import org.jclouds.blobstore.domain.BlobBuilder;
import org.jclouds.blobstore.options.ListContainerOptions;
import org.jclouds.blobstore.strategy.ContainsValueInListStrategy;
import org.jclouds.blobstore.strategy.GetBlobsInListStrategy;
@ -66,12 +68,11 @@ public class InputStreamMapImpl extends BaseBlobMap<InputStream> implements Inpu
protected final Crypto crypto;
@Inject
public InputStreamMapImpl(BlobStore connection, Blob.Factory blobFactory,
GetBlobsInListStrategy getAllBlobs, ListContainerAndRecurseThroughFolders listStrategy,
ContainsValueInListStrategy containsValueStrategy, PutBlobsStrategy putBlobsStrategy,
String containerName, ListContainerOptions options, Crypto crypto) {
super(connection, getAllBlobs, containsValueStrategy, putBlobsStrategy, listStrategy,
containerName, options);
public InputStreamMapImpl(BlobStore connection, Provider<BlobBuilder> blobBuilders,
GetBlobsInListStrategy getAllBlobs, ListContainerAndRecurseThroughFolders listStrategy,
ContainsValueInListStrategy containsValueStrategy, PutBlobsStrategy putBlobsStrategy, String containerName,
ListContainerOptions options, Crypto crypto) {
super(connection, getAllBlobs, containsValueStrategy, putBlobsStrategy, listStrategy, containerName, options);
this.crypto = crypto;
}
@ -96,12 +97,11 @@ public class InputStreamMapImpl extends BaseBlobMap<InputStream> implements Inpu
@Override
public Collection<InputStream> values() {
return newArrayList(transform(getAllBlobs.execute(containerName, options),
new Function<Blob, InputStream>() {
public InputStream apply(Blob from) {
return getInputStreamOrNull(from);
}
}));
return newArrayList(transform(getAllBlobs.execute(containerName, options), new Function<Blob, InputStream>() {
public InputStream apply(Blob from) {
return getInputStreamOrNull(from);
}
}));
}
@Override
@ -132,22 +132,21 @@ public class InputStreamMapImpl extends BaseBlobMap<InputStream> implements Inpu
*/
@VisibleForTesting
void putAllInternal(Map<? extends String, ? extends Object> map) {
putBlobsStrategy.execute(containerName, transform(map.entrySet(),
new Function<Map.Entry<? extends String, ? extends Object>, Blob>() {
@Override
public Blob apply(Map.Entry<? extends String, ? extends Object> from) {
String name = from.getKey();
Object value = from.getValue();
return newBlobWithMD5(name, value);
}
putBlobsStrategy.execute(containerName,
transform(map.entrySet(), new Function<Map.Entry<? extends String, ? extends Object>, Blob>() {
@Override
public Blob apply(Map.Entry<? extends String, ? extends Object> from) {
String name = from.getKey();
Object value = from.getValue();
return newBlobWithMD5(name, value);
}
}));
}));
}
@VisibleForTesting
Blob newBlobWithMD5(String name, Object value) {
Blob blob = blobstore.newBlob(prefixer.apply(name));
blob.setPayload(newPayload(value));
Blob blob = blobstore.blobBuilder(prefixer.apply(name)).payload(newPayload(value)).build();
try {
Payloads.calculateMD5(blob, crypto.md5());
} catch (IOException e) {

View File

@ -19,15 +19,15 @@
package org.jclouds.blobstore.strategy.internal;
import static org.jclouds.io.Payloads.newByteArrayPayload;
import javax.inject.Named;
import javax.inject.Singleton;
import org.jclouds.blobstore.BlobStore;
import org.jclouds.blobstore.domain.Blob;
import org.jclouds.blobstore.domain.StorageType;
import org.jclouds.blobstore.reference.BlobStoreConstants;
import org.jclouds.blobstore.strategy.MkdirStrategy;
import org.jclouds.io.Payloads;
import com.google.inject.Inject;
@ -44,18 +44,17 @@ public class MarkerFileMkdirStrategy implements MkdirStrategy {
@Inject(optional = true)
@Named(BlobStoreConstants.PROPERTY_BLOBSTORE_DIRECTORY_SUFFIX)
protected String directorySuffix = "";
private final BlobStore connection;
private final BlobStore blobStore;
@Inject
MarkerFileMkdirStrategy(BlobStore connection) {
this.connection = connection;
MarkerFileMkdirStrategy(BlobStore blobStore) {
this.blobStore = blobStore;
}
public void execute(String containerName, String directory) {
Blob blob = connection.newBlob(directory + directorySuffix);
blob.setPayload(Payloads.newByteArrayPayload(new byte[] {}));
blob.getPayload().getContentMetadata().setContentType("application/directory");
blob.getMetadata().setType(StorageType.RELATIVE_PATH);
connection.putBlob(containerName, blob);
blobStore.putBlob(
containerName,
blobStore.blobBuilder(directory + directorySuffix).type(StorageType.RELATIVE_PATH)
.payload(newByteArrayPayload(new byte[] {})).contentType("application/directory").build());
}
}

View File

@ -68,7 +68,7 @@ public class PutBlobsStrategyImpl implements PutBlobsStrategy {
@Override
public void execute(String containerName, Iterable<? extends Blob> blobs) {
Map<Blob, Future<?>> responses = Maps.newHashMap();
Map<Blob, Future<?>> responses = Maps.newLinkedHashMap();
for (Blob blob : blobs) {
responses.put(blob, ablobstore.putBlob(containerName, blob));
}

View File

@ -80,7 +80,8 @@ public class BlobStoreUtils {
}
public static Blob newBlob(BlobStore blobStore, StorageMetadata blobMeta) {
Blob blob = checkNotNull(blobStore, "blobStore").newBlob(checkNotNull(blobMeta, "blobMeta").getName());
Blob blob = checkNotNull(blobStore, "blobStore").blobBuilder(checkNotNull(blobMeta, "blobMeta").getName())
.userMetadata(blobMeta.getUserMetadata()).build();
if (blobMeta instanceof BlobMetadata) {
HttpUtils.copy(((BlobMetadata) blobMeta).getContentMetadata(), blob.getMetadata().getContentMetadata());
}
@ -89,7 +90,6 @@ public class BlobStoreUtils {
blob.getMetadata().setLastModified(blobMeta.getLastModified());
blob.getMetadata().setLocation(blobMeta.getLocation());
blob.getMetadata().setUri(blobMeta.getUri());
blob.getMetadata().setUserMetadata(blobMeta.getUserMetadata());
return blob;
}

View File

@ -20,6 +20,7 @@
package org.jclouds.blobstore.util;
import org.jclouds.blobstore.domain.Blob;
import org.jclouds.blobstore.domain.BlobBuilder;
import org.jclouds.blobstore.options.ListContainerOptions;
import org.jclouds.blobstore.util.internal.BlobUtilsImpl;
@ -31,6 +32,7 @@ import com.google.inject.ImplementedBy;
*/
@ImplementedBy(BlobUtilsImpl.class)
public interface BlobUtils {
BlobBuilder blobBuilder();
Blob newBlob(String name);

View File

@ -22,9 +22,11 @@ package org.jclouds.blobstore.util.internal;
import static com.google.common.base.Preconditions.checkNotNull;
import javax.inject.Inject;
import javax.inject.Provider;
import javax.inject.Singleton;
import org.jclouds.blobstore.domain.Blob;
import org.jclouds.blobstore.domain.BlobBuilder;
import org.jclouds.blobstore.options.ListContainerOptions;
import org.jclouds.blobstore.strategy.ClearListStrategy;
import org.jclouds.blobstore.strategy.CountListStrategy;
@ -41,7 +43,7 @@ import org.jclouds.blobstore.util.BlobUtils;
@Singleton
public class BlobUtilsImpl implements BlobUtils {
protected final Blob.Factory blobFactory;
protected final Provider<BlobBuilder> blobBuilders;
protected final ClearListStrategy clearContainerStrategy;
protected final GetDirectoryStrategy getDirectoryStrategy;
protected final MkdirStrategy mkdirStrategy;
@ -49,10 +51,10 @@ public class BlobUtilsImpl implements BlobUtils {
protected final CountListStrategy countBlobsStrategy;
@Inject
protected BlobUtilsImpl(Blob.Factory blobFactory, ClearListStrategy clearContainerStrategy,
GetDirectoryStrategy getDirectoryStrategy, MkdirStrategy mkdirStrategy,
CountListStrategy countBlobsStrategy, DeleteDirectoryStrategy rmDirStrategy) {
this.blobFactory = checkNotNull(blobFactory, "blobFactory");
protected BlobUtilsImpl(Provider<BlobBuilder> blobBuilders, ClearListStrategy clearContainerStrategy,
GetDirectoryStrategy getDirectoryStrategy, MkdirStrategy mkdirStrategy, CountListStrategy countBlobsStrategy,
DeleteDirectoryStrategy rmDirStrategy) {
this.blobBuilders = checkNotNull(blobBuilders, "blobBuilders");
this.clearContainerStrategy = checkNotNull(clearContainerStrategy, "clearContainerStrategy");
this.getDirectoryStrategy = checkNotNull(getDirectoryStrategy, "getDirectoryStrategy");
this.mkdirStrategy = checkNotNull(mkdirStrategy, "mkdirStrategy");
@ -60,10 +62,14 @@ public class BlobUtilsImpl implements BlobUtils {
this.countBlobsStrategy = checkNotNull(countBlobsStrategy, "countBlobsStrategy");
}
@Override
public Blob newBlob(String name) {
Blob blob = blobFactory.create(null);
blob.getMetadata().setName(name);
return blob;
return blobBuilder().name(name).build();
}
@Override
public BlobBuilder blobBuilder() {
return blobBuilders.get();
}
public boolean directoryExists(String containerName, String directory) {

View File

@ -19,6 +19,7 @@
package org.jclouds.blobstore.integration;
import static com.google.common.collect.Iterables.getOnlyElement;
import static org.jclouds.blobstore.options.ListContainerOptions.Builder.maxResults;
import static org.testng.Assert.assertEquals;
@ -31,7 +32,7 @@ import org.jclouds.blobstore.domain.StorageMetadata;
import org.jclouds.blobstore.integration.internal.BaseContainerIntegrationTest;
import org.testng.annotations.Test;
import com.google.common.collect.Iterables;
import com.google.common.collect.ImmutableMap;
/**
* @author James Murty
@ -44,22 +45,19 @@ public class TransientContainerIntegrationTest extends BaseContainerIntegrationT
public void testNotWithDetails() throws InterruptedException {
String key = "hello";
Blob object = context.getBlobStore().newBlob(key);
object.setPayload(TEST_STRING);
object.getMetadata().getContentMetadata().setContentType(MediaType.TEXT_PLAIN);
// NOTE all metadata in jclouds comes out as lowercase, in an effort to normalize the
// providers.
object.getMetadata().getUserMetadata().put("Adrian", "powderpuff");
Blob blob = context.getBlobStore().blobBuilder("hello").userMetadata(ImmutableMap.of("Adrian", "powderpuff"))
.payload(TEST_STRING).contentType(MediaType.TEXT_PLAIN).build();
String containerName = getContainerName();
try {
addBlobToContainer(containerName, object);
addBlobToContainer(containerName, blob);
validateContent(containerName, key);
PageSet<? extends StorageMetadata> container = context.getBlobStore().list(containerName,
maxResults(1));
PageSet<? extends StorageMetadata> container = context.getBlobStore().list(containerName, maxResults(1));
BlobMetadata metadata = (BlobMetadata) Iterables.getOnlyElement(container);
BlobMetadata metadata = (BlobMetadata) getOnlyElement(container);
// transient container should be lenient and not return metadata on undetailed listing.
assertEquals(metadata.getUserMetadata().size(), 0);

View File

@ -49,6 +49,7 @@ import javax.ws.rs.core.MediaType;
import org.jclouds.blobstore.ContainerNotFoundException;
import org.jclouds.blobstore.domain.Blob;
import org.jclouds.blobstore.domain.BlobBuilder.PayloadBlobBuilder;
import org.jclouds.blobstore.domain.BlobMetadata;
import org.jclouds.blobstore.domain.PageSet;
import org.jclouds.blobstore.domain.StorageMetadata;
@ -73,6 +74,7 @@ import org.testng.annotations.Test;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import com.google.common.io.ByteStreams;
@ -97,7 +99,7 @@ public class BaseBlobIntegrationTest extends BaseBlobStoreIntegrationTest {
@SuppressWarnings("unchecked")
public static InputSupplier<InputStream> getTestDataSupplier() throws IOException {
byte[] oneConstitution = ByteStreams.toByteArray(new GZIPInputStream(BaseJettyTest.class
.getResourceAsStream("/const.txt.gz")));
.getResourceAsStream("/const.txt.gz")));
InputSupplier<ByteArrayInputStream> constitutionSupplier = ByteStreams.newInputStreamSupplier(oneConstitution);
InputSupplier<InputStream> temp = ByteStreams.join(constitutionSupplier);
@ -120,24 +122,24 @@ public class BaseBlobIntegrationTest extends BaseBlobStoreIntegrationTest {
Map<Integer, Future<?>> responses = Maps.newHashMap();
for (int i = 0; i < 10; i++) {
responses.put(i, Futures.compose(context.getAsyncBlobStore().getBlob(containerName, key),
new Function<Blob, Void>() {
responses.put(i,
Futures.compose(context.getAsyncBlobStore().getBlob(containerName, key), new Function<Blob, Void>() {
@Override
public Void apply(Blob from) {
try {
assertEquals(CryptoStreams.md5(from.getPayload()), oneHundredOneConstitutionsMD5);
checkContentDisposition(from, expectedContentDisposition);
} catch (IOException e) {
Throwables.propagate(e);
}
return null;
@Override
public Void apply(Blob from) {
try {
assertEquals(CryptoStreams.md5(from.getPayload()), oneHundredOneConstitutionsMD5);
checkContentDisposition(from, expectedContentDisposition);
} catch (IOException e) {
Throwables.propagate(e);
}
return null;
}
}, this.exec));
}, this.exec));
}
Map<Integer, Exception> exceptions = awaitCompletion(responses, exec, 30000l, Logger.CONSOLE,
"get constitution");
"get constitution");
assert exceptions.size() == 0 : exceptions;
} finally {
@ -147,13 +149,11 @@ public class BaseBlobIntegrationTest extends BaseBlobStoreIntegrationTest {
}
private void uploadConstitution(String containerName, String key, String contentDisposition) throws IOException {
Blob sourceObject = context.getBlobStore().newBlob(key);
sourceObject.setPayload(oneHundredOneConstitutions.getInput());
sourceObject.getMetadata().getContentMetadata().setContentType("text/plain");
sourceObject.getMetadata().getContentMetadata().setContentMD5(oneHundredOneConstitutionsMD5);
sourceObject.getMetadata().getContentMetadata().setContentLength(oneHundredOneConstitutionsLength);
sourceObject.getMetadata().getContentMetadata().setContentDisposition(contentDisposition);
context.getBlobStore().putBlob(containerName, sourceObject);
context.getBlobStore().putBlob(
containerName,
context.getBlobStore().blobBuilder(key).payload(oneHundredOneConstitutions.getInput())
.contentType("text/plain").contentMD5(oneHundredOneConstitutionsMD5)
.contentLength(oneHundredOneConstitutionsLength).contentDisposition(contentDisposition).build());
}
@Test(groups = { "integration", "live" })
@ -364,8 +364,8 @@ public class BaseBlobIntegrationTest extends BaseBlobStoreIntegrationTest {
@DataProvider(name = "delete")
public Object[][] createData() {
return new Object[][] { { "normal" }, { "sp ace" }, { "qu?stion" }, { "unic₪de" }, { "path/foo" }, { "colon:" },
{ "asteri*k" }, { "quote\"" }, { "{great<r}" }, { "lesst>en" }, { "p|pe" } };
return new Object[][] { { "normal" }, { "sp ace" }, { "qu?stion" }, { "unic₪de" }, { "path/foo" },
{ "colon:" }, { "asteri*k" }, { "quote\"" }, { "{great<r}" }, { "lesst>en" }, { "p|pe" } };
}
@Test(groups = { "integration", "live" }, dataProvider = "delete")
@ -382,17 +382,19 @@ public class BaseBlobIntegrationTest extends BaseBlobStoreIntegrationTest {
private void assertContainerEmptyDeleting(String containerName, String key) {
Iterable<? extends StorageMetadata> listing = Iterables.filter(context.getBlobStore().list(containerName),
new Predicate<StorageMetadata>() {
new Predicate<StorageMetadata>() {
@Override
public boolean apply(StorageMetadata input) {
return input.getType() == StorageType.BLOB;
}
@Override
public boolean apply(StorageMetadata input) {
return input.getType() == StorageType.BLOB;
}
});
assertEquals(Iterables.size(listing), 0, String.format(
"deleting %s, we still have %s blobs left in container %s, using encoding %s", key, Iterables
.size(listing), containerName, LOCAL_ENCODING));
});
assertEquals(
Iterables.size(listing),
0,
String.format("deleting %s, we still have %s blobs left in container %s, using encoding %s", key,
Iterables.size(listing), containerName, LOCAL_ENCODING));
}
@Test(groups = { "integration", "live" })
@ -412,21 +414,20 @@ public class BaseBlobIntegrationTest extends BaseBlobStoreIntegrationTest {
String realObject = Strings2.toStringAndClose(new FileInputStream("pom.xml"));
return new Object[][] { { "file", "text/xml", new File("pom.xml"), realObject },
{ "string", "text/xml", realObject, realObject },
{ "bytes", "application/octet-stream", realObject.getBytes(), realObject } };
{ "string", "text/xml", realObject, realObject },
{ "bytes", "application/octet-stream", realObject.getBytes(), realObject } };
}
@Test(groups = { "integration", "live" }, dataProvider = "putTests")
public void testPutObject(String key, String type, Object content, Object realObject) throws InterruptedException,
IOException {
Blob blob = context.getBlobStore().newBlob(key);
blob.setPayload(Payloads.newPayload(content));
blob.getMetadata().getContentMetadata().setContentType(type);
addContentMetadata(blob);
IOException {
PayloadBlobBuilder blobBuilder = context.getBlobStore().blobBuilder(key).payload(Payloads.newPayload(content))
.contentType(type);
addContentMetadata(blobBuilder);
if (content instanceof InputStream) {
Payloads.calculateMD5(blob, context.utils().crypto().md5());
blobBuilder.calculateMD5();
}
Blob blob = blobBuilder.build();
String containerName = getContainerName();
try {
assertNotNull(context.getBlobStore().putBlob(containerName, blob));
@ -444,14 +445,15 @@ public class BaseBlobIntegrationTest extends BaseBlobStoreIntegrationTest {
@Test(groups = { "integration", "live" })
public void testPutObjectStream() throws InterruptedException, IOException, ExecutionException {
Blob blob = context.getBlobStore().newBlob("streaming");
blob.setPayload(new StreamingPayload(new WriteTo() {
PayloadBlobBuilder blobBuilder = context.getBlobStore().blobBuilder("streaming").payload(new StreamingPayload(new WriteTo() {
@Override
public void writeTo(OutputStream outstream) throws IOException {
outstream.write("foo".getBytes());
}
}));
addContentMetadata(blob);
addContentMetadata(blobBuilder);
Blob blob = blobBuilder.build();
String containerName = getContainerName();
try {
@ -476,40 +478,40 @@ public class BaseBlobIntegrationTest extends BaseBlobStoreIntegrationTest {
checkContentLanguage(blob, "en");
}
private void addContentMetadata(Blob blob) {
blob.getMetadata().getContentMetadata().setContentType("text/csv");
blob.getMetadata().getContentMetadata().setContentDisposition("attachment; filename=photo.jpg");
blob.getMetadata().getContentMetadata().setContentEncoding("gzip");
blob.getMetadata().getContentMetadata().setContentLanguage("en");
private void addContentMetadata(PayloadBlobBuilder blobBuilder) {
blobBuilder.contentType("text/csv");
blobBuilder.contentDisposition("attachment; filename=photo.jpg");
blobBuilder.contentEncoding("gzip");
blobBuilder.contentLanguage("en");
}
protected void checkContentType(Blob blob, String contentType) {
assert blob.getPayload().getContentMetadata().getContentType().startsWith(contentType) : blob.getPayload()
.getContentMetadata().getContentType();
.getContentMetadata().getContentType();
assert blob.getMetadata().getContentMetadata().getContentType().startsWith(contentType) : blob.getMetadata()
.getContentMetadata().getContentType();
.getContentMetadata().getContentType();
}
protected void checkContentDisposition(Blob blob, String contentDisposition) {
assert blob.getPayload().getContentMetadata().getContentDisposition().startsWith(contentDisposition) : blob
.getPayload().getContentMetadata().getContentDisposition();
.getPayload().getContentMetadata().getContentDisposition();
assert blob.getMetadata().getContentMetadata().getContentDisposition().startsWith(contentDisposition) : blob
.getMetadata().getContentMetadata().getContentDisposition();
.getMetadata().getContentMetadata().getContentDisposition();
}
protected void checkContentEncoding(Blob blob, String contentEncoding) {
assert (blob.getPayload().getContentMetadata().getContentEncoding().indexOf(contentEncoding) != -1) : blob
.getPayload().getContentMetadata().getContentEncoding();
.getPayload().getContentMetadata().getContentEncoding();
assert (blob.getMetadata().getContentMetadata().getContentEncoding().indexOf(contentEncoding) != -1) : blob
.getMetadata().getContentMetadata().getContentEncoding();
.getMetadata().getContentMetadata().getContentEncoding();
}
protected void checkContentLanguage(Blob blob, String contentLanguage) {
assert blob.getPayload().getContentMetadata().getContentLanguage().startsWith(contentLanguage) : blob
.getPayload().getContentMetadata().getContentLanguage();
.getPayload().getContentMetadata().getContentLanguage();
assert blob.getMetadata().getContentMetadata().getContentLanguage().startsWith(contentLanguage) : blob
.getMetadata().getContentMetadata().getContentLanguage();
.getMetadata().getContentMetadata().getContentLanguage();
}
protected volatile static Crypto crypto;
@ -526,15 +528,11 @@ public class BaseBlobIntegrationTest extends BaseBlobStoreIntegrationTest {
@Test(groups = { "integration", "live" })
public void testMetadata() throws InterruptedException, IOException {
String key = "hello";
Blob blob = context.getBlobStore().newBlob(key);
blob.setPayload(TEST_STRING);
blob.getMetadata().getContentMetadata().setContentType(MediaType.TEXT_PLAIN);
// NOTE all metadata in jclouds comes out as lowercase, in an effort to
// normalize the
// providers.
blob.getMetadata().getUserMetadata().put("Adrian", "powderpuff");
Payloads.calculateMD5(blob, context.utils().crypto().md5());
Blob blob = context.getBlobStore().blobBuilder(key).userMetadata(ImmutableMap.of("Adrian", "powderpuff"))
.payload(TEST_STRING).contentType(MediaType.TEXT_PLAIN).calculateMD5().build();
String containerName = getContainerName();
try {
assertNull(context.getBlobStore().blobMetadata(containerName, "powderpuff"));
@ -562,7 +560,7 @@ public class BaseBlobIntegrationTest extends BaseBlobStoreIntegrationTest {
protected void validateMetadata(BlobMetadata metadata) throws IOException {
assert metadata.getContentMetadata().getContentType().startsWith("text/plain") : metadata.getContentMetadata()
.getContentType();
.getContentType();
assertEquals(metadata.getContentMetadata().getContentLength(), new Long(TEST_STRING.length()));
assertEquals(metadata.getUserMetadata().get("adrian"), "powderpuff");
checkMD5(metadata);

View File

@ -46,7 +46,7 @@ public class BaseBlobLiveTest extends BaseBlobStoreIntegrationTest {
private static final String sysHttpStreamETag = System.getProperty("jclouds.blobstore.httpstream.md5");
@Test
@Parameters( { "jclouds.blobstore.httpstream.url", "jclouds.blobstore.httpstream.md5" })
@Parameters({ "jclouds.blobstore.httpstream.url", "jclouds.blobstore.httpstream.md5" })
public void testCopyUrl(@Optional String httpStreamUrl, @Optional String httpStreamETag) throws Exception {
httpStreamUrl = checkNotNull(httpStreamUrl != null ? httpStreamUrl : sysHttpStreamUrl, "httpStreamUrl");
@ -61,10 +61,7 @@ public class BaseBlobLiveTest extends BaseBlobStoreIntegrationTest {
long length = connection.getContentLength();
InputStream input = connection.getInputStream();
Blob blob = context.getBlobStore().newBlob(name);
blob.setPayload(input);
blob.getPayload().getContentMetadata().setContentLength(length);
blob.getPayload().getContentMetadata().setContentMD5(md5);
Blob blob = context.getBlobStore().blobBuilder(name).payload(input).contentLength(length).contentMD5(md5).build();
String container = getContainerName();
try {
context.getBlobStore().putBlob(container, blob);

View File

@ -20,17 +20,16 @@
package org.jclouds.blobstore.integration.internal;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.jclouds.blobstore.options.ListContainerOptions.Builder.inDirectory;
import static org.jclouds.blobstore.options.ListContainerOptions.Builder.maxResults;
import static org.jclouds.blobstore.util.BlobStoreUtils.getContentAsStringOrNullAndClose;
import static org.testng.Assert.assertEquals;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
@ -42,7 +41,12 @@ import org.jclouds.io.Payloads;
import org.jclouds.util.Strings2;
import org.testng.annotations.Test;
import com.google.common.base.Function;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSet.Builder;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
/**
@ -52,19 +56,32 @@ import com.google.common.collect.Sets;
*/
public abstract class BaseBlobMapIntegrationTest extends BaseMapIntegrationTest<Blob> {
private static class StringToBlob implements Function<String, Blob> {
private final BlobMap map;
private StringToBlob(BlobMap map) {
this.map = map;
}
@Override
public Blob apply(String arg0) {
return map.blobBuilder().payload(arg0).build();
}
}
@Override
@Test(groups = { "integration", "live" })
public void testValues() throws IOException, InterruptedException {
String bucketName = getContainerName();
try {
Map<String, Blob> map = createMap(context, bucketName);
BlobMap map = createMap(context, bucketName);
putFiveStrings(map);
putFiveStringsUnderPath(map);
Collection<Blob> blobs = map.values();
assertConsistencyAwareMapSize(map, 5);
Set<String> blobsAsString = new HashSet<String>();
Set<String> blobsAsString = Sets.newLinkedHashSet();
for (Blob blob : blobs) {
blobsAsString.add(getContentAsStringOrNullAndClose(blob));
}
@ -92,7 +109,7 @@ public abstract class BaseBlobMapIntegrationTest extends BaseMapIntegrationTest<
}
private void assertConsistencyAwareContentEquals(final Map<String, Blob> map, final String key, final String blob)
throws InterruptedException {
throws InterruptedException {
assertConsistencyAware(new Runnable() {
public void run() {
Blob old = map.remove(key);
@ -110,7 +127,7 @@ public abstract class BaseBlobMapIntegrationTest extends BaseMapIntegrationTest<
public void testEntrySet() throws IOException, InterruptedException {
String bucketName = getContainerName();
try {
final Map<String, Blob> map = createMap(context, bucketName);
final BlobMap map = createMap(context, bucketName);
putFiveStrings(map);
assertConsistencyAwareMapSize(map, 5);
Set<Entry<String, Blob>> entries = map.entrySet();
@ -145,9 +162,7 @@ public abstract class BaseBlobMapIntegrationTest extends BaseMapIntegrationTest<
try {
Map<String, Blob> map = createMap(context, bucketName);
putStringWithMD5(map, "one", "apple");
Blob blob = context.getBlobStore().newBlob("one");
blob.setPayload("apple");
Payloads.calculateMD5(blob);
Blob blob = context.getBlobStore().blobBuilder("one").payload("apple").calculateMD5().build();
assertConsistencyAwareContainsValue(map, blob);
} finally {
returnContainer(bucketName);
@ -161,7 +176,7 @@ public abstract class BaseBlobMapIntegrationTest extends BaseMapIntegrationTest<
}
void getOneReturnsBearAndOldValueIsApple(Map<String, Blob> map, Blob oldValue) throws IOException,
InterruptedException {
InterruptedException {
assertEquals(getContentAsStringOrNullAndClose(checkNotNull(map.get("one"), "one")), "bear");
assertEquals(getContentAsStringOrNullAndClose(oldValue), "apple");
assertConsistencyAwareMapSize(map, 1);
@ -172,9 +187,8 @@ public abstract class BaseBlobMapIntegrationTest extends BaseMapIntegrationTest<
String bucketName = getContainerName();
try {
Map<String, Blob> map = createMap(context, bucketName);
Blob blob = context.getBlobStore().newBlob("one");
blob.setPayload(Strings2.toInputStream("apple"));
Payloads.calculateMD5(blob);
Blob blob = context.getBlobStore().blobBuilder("one").payload(Strings2.toInputStream("apple")).calculateMD5()
.build();
Blob old = map.put(blob.getMetadata().getName(), blob);
getOneReturnsAppleAndOldValueIsNull(map, old);
blob.setPayload(Strings2.toInputStream("bear"));
@ -191,16 +205,16 @@ public abstract class BaseBlobMapIntegrationTest extends BaseMapIntegrationTest<
String bucketName = getContainerName();
try {
Map<String, Blob> map = createMap(context, bucketName);
Map<String, Blob> newMap = new HashMap<String, Blob>();
ImmutableMap.Builder<String, Blob> newMap = ImmutableMap.<String, Blob> builder();
for (String key : fiveInputs.keySet()) {
Blob blob = context.getBlobStore().newBlob(key);
blob.setPayload(fiveInputs.get(key));
blob.getPayload().getContentMetadata().setContentLength((long) fiveBytes.get(key).length);
newMap.put(key, blob);
newMap.put(
key,
context.getBlobStore().blobBuilder(key).payload(fiveInputs.get(key))
.contentLength((long) fiveBytes.get(key).length).build());
}
map.putAll(newMap);
map.putAll(newMap.build());
assertConsistencyAwareMapSize(map, 5);
assertConsistencyAwareKeySetEquals(map, new HashSet<String>(fiveInputs.keySet()));
assertConsistencyAwareKeySetEquals(map, ImmutableSet.copyOf(fiveInputs.keySet()));
fourLeftRemovingOne(map);
} finally {
returnContainer(bucketName);
@ -213,23 +227,21 @@ public abstract class BaseBlobMapIntegrationTest extends BaseMapIntegrationTest<
return;
String bucketName = getContainerName();
try {
Map<String, Blob> map = createMap(context, bucketName);
Set<String> keySet = Sets.newHashSet();
BlobMap map = createMap(context, bucketName);
Builder<String> keySet = ImmutableSet.<String> builder();
for (int i = 0; i < maxResultsForTestListings() + 1; i++) {
keySet.add(i + "");
}
Map<String, Blob> newMap = new HashMap<String, Blob>();
for (String key : keySet) {
Blob blob = context.getBlobStore().newBlob(key);
blob.setPayload(key);
newMap.put(key, blob);
Map<String, Blob> newMap = Maps.newLinkedHashMap();
for (String key : keySet.build()) {
newMap.put(key, map.blobBuilder().payload(key).build());
}
map.putAll(newMap);
newMap.clear();
assertConsistencyAwareMapSize(map, maxResultsForTestListings() + 1);
assertConsistencyAwareKeySetEquals(map, keySet);
assertConsistencyAwareKeySetEquals(map, keySet.build());
map.clear();
assertConsistencyAwareMapSize(map, 0);
} finally {
@ -239,30 +251,15 @@ public abstract class BaseBlobMapIntegrationTest extends BaseMapIntegrationTest<
@Override
protected void putStringWithMD5(Map<String, Blob> map, String key, String text) throws IOException {
Blob blob = context.getBlobStore().newBlob(key);
blob.setPayload(text);
Payloads.calculateMD5(blob);
map.put(key, blob);
map.put(key, context.getBlobStore().blobBuilder(key).payload(text).calculateMD5().build());
}
protected void putFiveStrings(Map<String, Blob> map) {
Map<String, Blob> newMap = new HashMap<String, Blob>();
for (Map.Entry<String, String> entry : fiveStrings.entrySet()) {
Blob blob = context.getBlobStore().newBlob(entry.getKey());
blob.setPayload(entry.getValue());
newMap.put(entry.getKey(), blob);
}
map.putAll(newMap);
protected void putFiveStrings(BlobMap map) {
map.putAll(Maps.transformValues(fiveStrings, new StringToBlob(map)));
}
protected void putFiveStringsUnderPath(Map<String, Blob> map) {
Map<String, Blob> newMap = new HashMap<String, Blob>();
for (Map.Entry<String, String> entry : fiveStringsUnderPath.entrySet()) {
Blob blob = context.getBlobStore().newBlob(entry.getKey());
blob.setPayload(entry.getValue());
newMap.put(entry.getKey(), blob);
}
map.putAll(newMap);
protected void putFiveStringsUnderPath(BlobMap map) {
map.putAll(Maps.transformValues(fiveStringsUnderPath, new StringToBlob(map)));
}
protected int maxResultsForTestListings() {
@ -276,4 +273,20 @@ public abstract class BaseBlobMapIntegrationTest extends BaseMapIntegrationTest<
protected BlobMap createMap(BlobStoreContext context, String bucket, ListContainerOptions options) {
return context.createBlobMap(bucket, options);
}
@Override
protected void addTenObjectsUnderPrefix(String containerName, String prefix) throws InterruptedException {
BlobMap blobMap = createMap(context, containerName, inDirectory(prefix));
for (int i = 0; i < 10; i++) {
blobMap.put(i + "", blobMap.blobBuilder().payload(i + "content").build());
}
}
@Override
protected void addTenObjectsUnderRoot(String containerName) throws InterruptedException {
BlobMap blobMap = createMap(context, containerName, ListContainerOptions.NONE);
for (int i = 0; i < 10; i++) {
blobMap.put(i + "", blobMap.blobBuilder().payload(i + "content").build());
}
}
}

View File

@ -41,9 +41,7 @@ public class BaseBlobSignerLiveTest extends BaseBlobStoreIntegrationTest {
String name = "hello";
String text = "fooooooooooooooooooooooo";
Blob blob = context.getBlobStore().newBlob(name);
blob.setPayload(text);
blob.getPayload().getContentMetadata().setContentType("text/plain");
Blob blob = context.getBlobStore().blobBuilder(name).payload(text).contentType("text/plain").build();
String container = getContainerName();
try {
context.getBlobStore().putBlob(container, blob);
@ -62,9 +60,7 @@ public class BaseBlobSignerLiveTest extends BaseBlobStoreIntegrationTest {
String name = "hello";
String text = "fooooooooooooooooooooooo";
Blob blob = context.getBlobStore().newBlob(name);
blob.setPayload(text);
blob.getPayload().getContentMetadata().setContentType("text/plain");
Blob blob = context.getBlobStore().blobBuilder(name).payload(text).contentType("text/plain").build();
String container = getContainerName();
try {
context.getBlobStore().putBlob(container, blob);
@ -82,9 +78,7 @@ public class BaseBlobSignerLiveTest extends BaseBlobStoreIntegrationTest {
String name = "hello";
String text = "fooooooooooooooooooooooo";
Blob blob = context.getBlobStore().newBlob(name);
blob.setPayload(text);
blob.getPayload().getContentMetadata().setContentType("text/plain");
Blob blob = context.getBlobStore().blobBuilder(name).payload(text).contentType("text/plain").build();
String container = getContainerName();
try {
HttpRequest request = context.getSigner().signPutBlob(container, blob);

View File

@ -244,17 +244,14 @@ public class BaseBlobStoreIntegrationTest {
}
protected String addBlobToContainer(String sourceContainer, String key, String payload, String contentType) {
Blob sourceObject = context.getBlobStore().newBlob(key);
sourceObject.setPayload(payload);
sourceObject.getMetadata().getContentMetadata().setContentType(contentType);
Blob sourceObject = context.getBlobStore().blobBuilder(key).payload(payload).contentType(contentType).build();
return addBlobToContainer(sourceContainer, sourceObject);
}
protected void add5BlobsUnderPathAnd5UnderRootToContainer(String sourceContainer) {
for (Entry<String, String> entry : Iterables.concat(fiveStrings.entrySet(), fiveStringsUnderPath.entrySet())) {
Blob sourceObject = context.getBlobStore().newBlob(entry.getKey());
sourceObject.setPayload(entry.getValue());
sourceObject.getMetadata().getContentMetadata().setContentType("text/xml");
Blob sourceObject = context.getBlobStore().blobBuilder(entry.getKey()).payload(entry.getValue())
.contentType("text/xml").build();
addBlobToContainer(sourceContainer, sourceObject);
}
}

View File

@ -40,9 +40,10 @@ import org.jclouds.blobstore.domain.PageSet;
import org.jclouds.blobstore.domain.StorageMetadata;
import org.jclouds.crypto.CryptoStreams;
import org.jclouds.io.InputSuppliers;
import org.jclouds.io.Payloads;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableMap;
/**
* @author Adrian Cole
*/
@ -60,8 +61,7 @@ public class BaseContainerIntegrationTest extends BaseBlobStoreIntegrationTest {
try {
context.getBlobStore().createContainerInLocation(null, containerName);
Blob blob = context.getBlobStore().newBlob("hello");
blob.setPayload(TEST_STRING);
Blob blob = context.getBlobStore().blobBuilder("hello").payload(TEST_STRING).build();
context.getBlobStore().putBlob(containerName, blob);
context.getBlobStore().createContainerInLocation(null, containerName);
@ -74,27 +74,22 @@ public class BaseContainerIntegrationTest extends BaseBlobStoreIntegrationTest {
@Test(groups = { "integration", "live" })
public void testWithDetails() throws InterruptedException, IOException {
String key = "hello";
Blob object = context.getBlobStore().newBlob(key);
object.setPayload(TEST_STRING);
object.getMetadata().getContentMetadata().setContentType(MediaType.TEXT_PLAIN);
// NOTE all metadata in jclouds comes out as lowercase, in an effort to
// normalize the
// providers.
object.getMetadata().getUserMetadata().put("Adrian", "powderpuff");
Payloads.calculateMD5(object, context.utils().crypto().md5());
String containerName = getContainerName();
try {
addBlobToContainer(containerName, object);
addBlobToContainer(containerName,
// NOTE all metadata in jclouds comes out as lowercase, in an effort to
// normalize the providers.
context.getBlobStore().blobBuilder(key).userMetadata(ImmutableMap.of("Adrian", "powderpuff"))
.payload(TEST_STRING).contentType(MediaType.TEXT_PLAIN).calculateMD5().build());
validateContent(containerName, key);
PageSet<? extends StorageMetadata> container = context.getBlobStore().list(containerName,
maxResults(1).withDetails());
maxResults(1).withDetails());
BlobMetadata metadata = BlobMetadata.class.cast(get(container, 0));
assert metadata.getContentMetadata().getContentType().startsWith("text/plain") : metadata.getContentMetadata()
.getContentType();
.getContentType();
assertEquals(metadata.getContentMetadata().getContentLength(), new Long(TEST_STRING.length()));
assertEquals(metadata.getUserMetadata().get("adrian"), "powderpuff");
checkMD5(metadata);
@ -134,7 +129,7 @@ public class BaseContainerIntegrationTest extends BaseBlobStoreIntegrationTest {
container = context.getBlobStore().list(containerName, afterMarker(marker));
assertEquals(container.getNextMarker(), null);
assert container.size() == 25 : String.format("size should have been 25, but was %d: %s", container.size(),
container);
container);
assert container.getNextMarker() == null;
} finally {
@ -305,7 +300,7 @@ public class BaseContainerIntegrationTest extends BaseBlobStoreIntegrationTest {
public void run() {
try {
assert !context.getBlobStore().containerExists(containerName) : "container " + containerName
+ " still exists";
+ " still exists";
} catch (Exception e) {
propagateIfPossible(e);
}
@ -315,7 +310,7 @@ public class BaseContainerIntegrationTest extends BaseBlobStoreIntegrationTest {
@Test(groups = { "integration", "live" })
public void testListContainer() throws InterruptedException, ExecutionException, TimeoutException,
UnsupportedEncodingException {
UnsupportedEncodingException {
String containerName = getContainerName();
try {
add15UnderRoot(containerName);
@ -329,9 +324,8 @@ public class BaseContainerIntegrationTest extends BaseBlobStoreIntegrationTest {
protected void addAlphabetUnderRoot(String containerName) throws InterruptedException {
for (char letter = 'a'; letter <= 'z'; letter++) {
Blob blob = context.getBlobStore().newBlob(letter + "");
blob.setPayload(letter + "content");
context.getBlobStore().putBlob(containerName, blob);
context.getBlobStore().putBlob(containerName,
context.getBlobStore().blobBuilder(letter + "").payload(letter + "content").build());
}
assertContainerSize(containerName, 26);
@ -351,17 +345,15 @@ public class BaseContainerIntegrationTest extends BaseBlobStoreIntegrationTest {
protected void add15UnderRoot(String containerName) throws InterruptedException {
for (int i = 0; i < 15; i++) {
Blob blob = context.getBlobStore().newBlob(i + "");
blob.setPayload(i + "content");
context.getBlobStore().putBlob(containerName, blob);
context.getBlobStore().putBlob(containerName,
context.getBlobStore().blobBuilder(i + "").payload(i + "content").build());
}
}
protected void addTenObjectsUnderPrefix(String containerName, String prefix) throws InterruptedException {
for (int i = 0; i < 10; i++) {
Blob blob = context.getBlobStore().newBlob(prefix + "/" + i);
blob.setPayload(i + "content");
context.getBlobStore().putBlob(containerName, blob);
context.getBlobStore().putBlob(containerName,
context.getBlobStore().blobBuilder(prefix + "/" + i).payload(i + "content").build());
}
}
}

View File

@ -27,11 +27,10 @@ import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeSet;
import java.util.Map.Entry;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
@ -61,7 +60,7 @@ public abstract class BaseInputStreamMapIntegrationTest extends BaseMapIntegrati
assertConsistencyAwareMapSize(map, 5);
Collection<InputStream> values = map.values();
assertEquals(values.size(), 5);
Set<String> valuesAsString = new HashSet<String>();
Set<String> valuesAsString = Sets.newLinkedHashSet();
for (InputStream stream : values) {
valuesAsString.add(Strings2.toStringAndClose(stream));
}
@ -77,7 +76,7 @@ public abstract class BaseInputStreamMapIntegrationTest extends BaseMapIntegrati
String containerName = getContainerName();
try {
InputStreamMap map = createMap(context, containerName);
Set<String> keySet = Sets.newHashSet();
Set<String> keySet = Sets.newLinkedHashSet();
for (int i = 0; i < maxResultsForTestListings() + 1; i++) {
keySet.add(i + "");
}

View File

@ -30,15 +30,14 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeSet;
import java.util.Map.Entry;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
import org.jclouds.blobstore.BlobStoreContext;
import org.jclouds.blobstore.ListableMap;
import org.jclouds.blobstore.domain.Blob;
import org.jclouds.blobstore.options.ListContainerOptions;
import org.jclouds.util.Strings2;
import org.testng.annotations.BeforeClass;
@ -50,6 +49,7 @@ import org.testng.annotations.Test;
import com.google.common.base.Charsets;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
@ -82,14 +82,14 @@ public abstract class BaseMapIntegrationTest<V> extends BaseBlobStoreIntegration
}
@BeforeClass(groups = { "integration", "live" })
@Parameters( { "basedir" })
@Parameters({ "basedir" })
protected void setUpTempDir(@Optional String basedir) throws InterruptedException, ExecutionException,
FileNotFoundException, IOException, TimeoutException {
FileNotFoundException, IOException, TimeoutException {
if (basedir == null) {
basedir = System.getProperty("java.io.tmpdir");
}
tmpDirectory = basedir + File.separator + "target" + File.separator + "testFiles" + File.separator
+ getClass().getSimpleName();
+ getClass().getSimpleName();
new File(tmpDirectory).mkdirs();
fiveFiles = Maps.newHashMap();
for (Entry<String, String> entry : fiveStrings.entrySet()) {
@ -102,7 +102,7 @@ public abstract class BaseMapIntegrationTest<V> extends BaseBlobStoreIntegration
protected abstract Map<String, V> createMap(BlobStoreContext context, String containerName);
protected abstract Map<String, V> createMap(BlobStoreContext context, String containerName,
ListContainerOptions options);
ListContainerOptions options);
@Test(groups = { "integration", "live" })
public void testClear() throws InterruptedException, ExecutionException, TimeoutException, IOException {
@ -138,17 +138,15 @@ public abstract class BaseMapIntegrationTest<V> extends BaseBlobStoreIntegration
protected void addTenObjectsUnderPrefix(String containerName, String prefix) throws InterruptedException {
for (int i = 0; i < 10; i++) {
Blob blob = context.getBlobStore().newBlob(prefix + "/" + i);
blob.setPayload(i + "content");
context.getBlobStore().putBlob(containerName, blob);
context.getBlobStore().putBlob(containerName,
context.getBlobStore().blobBuilder(prefix + "/" + i).payload(i + "content").build());
}
}
protected void addTenObjectsUnderRoot(String containerName) throws InterruptedException {
for (int i = 0; i < 10; i++) {
Blob blob = context.getBlobStore().newBlob(i + "");
blob.setPayload(i + "content");
context.getBlobStore().putBlob(containerName, blob);
context.getBlobStore().putBlob(containerName,
context.getBlobStore().blobBuilder(i + "").payload(i + "content").build());
}
}
@ -165,15 +163,28 @@ public abstract class BaseMapIntegrationTest<V> extends BaseBlobStoreIntegration
context.getBlobStore().createDirectory(containerName, directory);
addTenObjectsUnderRoot(containerName);
assertEquals(rootMap.size(), 10);
assertEquals(ImmutableSortedSet.copyOf(rootMap.keySet()),
ImmutableSortedSet.of("0", "1", "2", "3", "4", "5", "6", "7", "8", "9"));
assertEquals(rootRecursiveMap.size(), 10);
assertEquals(ImmutableSortedSet.copyOf(rootRecursiveMap.keySet()),
ImmutableSortedSet.of("0", "1", "2", "3", "4", "5", "6", "7", "8", "9"));
assertEquals(inDirectoryMap.size(), 0);
assertEquals(inDirectoryRecursiveMap.size(), 0);
addTenObjectsUnderPrefix(containerName, directory);
assertEquals(rootMap.size(), 10);
assertEquals(ImmutableSortedSet.copyOf(rootMap.keySet()),
ImmutableSortedSet.of("0", "1", "2", "3", "4", "5", "6", "7", "8", "9"));
assertEquals(rootRecursiveMap.size(), 20);
assertEquals(ImmutableSortedSet.copyOf(rootRecursiveMap.keySet()), ImmutableSet.of("0", "1", "2", "3", "4",
"5", "6", "7", "8", "9", "apps/0", "apps/1", "apps/2", "apps/3", "apps/4", "apps/5", "apps/6", "apps/7",
"apps/8", "apps/9"));
assertEquals(inDirectoryMap.size(), 10);
assertEquals(ImmutableSortedSet.copyOf(inDirectoryMap.keySet()),
ImmutableSortedSet.of("0", "1", "2", "3", "4", "5", "6", "7", "8", "9"));
assertEquals(inDirectoryRecursiveMap.size(), 10);
assertEquals(ImmutableSortedSet.copyOf(inDirectoryRecursiveMap.keySet()),
ImmutableSortedSet.of("0", "1", "2", "3", "4", "5", "6", "7", "8", "9"));
context.getBlobStore().createDirectory(containerName, directory + "/" + directory);
assertEquals(rootMap.size(), 10);
@ -200,7 +211,7 @@ public abstract class BaseMapIntegrationTest<V> extends BaseBlobStoreIntegration
}
protected void assertConsistencyAwareKeySetEquals(final Map<String, V> map, final Set<String> expected)
throws InterruptedException {
throws InterruptedException {
assertConsistencyAware(new Runnable() {
public void run() {
Set<String> toMatch = map.keySet();
@ -214,7 +225,7 @@ public abstract class BaseMapIntegrationTest<V> extends BaseBlobStoreIntegration
}
protected void assertConsistencyAwareRemoveEquals(final Map<String, V> map, final String key, final Object equals)
throws InterruptedException {
throws InterruptedException {
assertConsistencyAware(new Runnable() {
public void run() {
assertEquals(map.remove(key), equals);
@ -223,7 +234,7 @@ public abstract class BaseMapIntegrationTest<V> extends BaseBlobStoreIntegration
}
protected void assertConsistencyAwareGetEquals(final Map<String, V> map, final String key, final Object equals)
throws InterruptedException {
throws InterruptedException {
assertConsistencyAware(new Runnable() {
public void run() {
assertEquals(map.get(key), equals);
@ -257,7 +268,7 @@ public abstract class BaseMapIntegrationTest<V> extends BaseBlobStoreIntegration
* eventual consistency problems.
*/
protected void assertConsistencyAwareContainsValue(final Map<String, V> map, final Object value)
throws InterruptedException {
throws InterruptedException {
assertConsistencyAware(new Runnable() {
public void run() {
assert map.containsValue(value);
@ -311,7 +322,7 @@ public abstract class BaseMapIntegrationTest<V> extends BaseBlobStoreIntegration
}
abstract protected void putStringWithMD5(Map<String, V> map, String key, String value) throws InterruptedException,
ExecutionException, TimeoutException, IOException;
ExecutionException, TimeoutException, IOException;
protected void fourLeftRemovingOne(Map<String, V> map) throws InterruptedException {
map.remove("one");
@ -342,7 +353,7 @@ public abstract class BaseMapIntegrationTest<V> extends BaseBlobStoreIntegration
}
protected void assertConsistencyAwareListContainer(final ListableMap<?, ?> map, final String containerNameName)
throws InterruptedException {
throws InterruptedException {
assertConsistencyAware(new Runnable() {
public void run() {
assertTrue(Iterables.size(map.list()) >= 0);

View File

@ -25,10 +25,7 @@ import java.io.IOException;
import org.jclouds.blobstore.BlobStore;
import org.jclouds.blobstore.BlobStoreContextFactory;
import org.jclouds.blobstore.domain.Blob;
import org.jclouds.blobstore.options.ListContainerOptions;
import org.jclouds.blobstore.strategy.internal.ConcatenateContainerLists;
import org.jclouds.blobstore.strategy.internal.ListContainerAndRecurseThroughFolders;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
@ -48,9 +45,7 @@ public class BiggerThanPageSizeTest {
public void test() throws IOException {
blobstore.createContainerInLocation(null, "goodies");
for (int i = 0; i < 1001; i++) {
Blob blob = blobstore.newBlob(i + "");
blob.setPayload(i + "");
blobstore.putBlob("goodies", blob);
blobstore.putBlob("goodies", blobstore.blobBuilder(i + "").payload(i + "").build());
}
assertEquals(blobstore.countBlobs("goodies"), 1001);
blobstore.clearContainer("goodies");
@ -60,9 +55,7 @@ public class BiggerThanPageSizeTest {
public void testStrategies() throws IOException {
blobstore.createContainerInLocation(null, "poo");
for (int i = 0; i < 1001; i++) {
Blob blob = blobstore.newBlob(i + "");
blob.setPayload(i + "");
blobstore.putBlob("poo", blob);
blobstore.putBlob("poo", blobstore.blobBuilder(i + "").payload(i + "").build());
}
ListContainerAndRecurseThroughFolders lister = new ListContainerAndRecurseThroughFolders(

View File

@ -48,7 +48,7 @@ public class MutableResourceMetadataImpl<T extends Enum<T>> implements MutableRe
private Map<String, String> userMetadata;
public MutableResourceMetadataImpl() {
userMetadata = Maps.newHashMap();
userMetadata = Maps.newLinkedHashMap();
}
public MutableResourceMetadataImpl(ResourceMetadata<T> from) {

View File

@ -89,7 +89,7 @@ public class Payloads {
}
public static UrlEncodedFormPayload newUrlEncodedFormPayload(Multimap<String, String> formParams,
@Nullable Comparator<Map.Entry<String, String>> sorter, char... skips) {
@Nullable Comparator<Map.Entry<String, String>> sorter, char... skips) {
return new UrlEncodedFormPayload(formParams, sorter, skips);
}
@ -109,14 +109,16 @@ public class Payloads {
public static Payload calculateMD5(Payload payload, MessageDigest md5) throws IOException {
checkNotNull(payload, "payload");
if (!payload.isRepeatable()) {
String oldContentType = payload.getContentMetadata().getContentType();
MutableContentMetadata oldContentMetadata = payload.getContentMetadata();
Payload oldPayload = payload;
try {
payload = newByteArrayPayload(toByteArray(payload));
} finally {
oldPayload.release();
}
payload.getContentMetadata().setContentType(oldContentType);
oldContentMetadata.setContentLength(payload.getContentMetadata().getContentLength());
oldContentMetadata.setContentMD5(payload.getContentMetadata().getContentMD5());
payload.setContentMetadata(oldContentMetadata);
}
payload.getContentMetadata().setContentMD5(CryptoStreams.digest(payload, md5));
return payload;

View File

@ -35,7 +35,7 @@ import com.google.common.base.Function;
*/
@Singleton
public class AzureBlobToBlob implements Function<AzureBlob, Blob> {
private final Blob.Factory blobFactory;
private final Factory blobFactory;
private final BlobPropertiesToBlobMetadata blobPr2BlobMd;
@Inject