From affed4d676fc8b42a35ec4007aea87e336a6c999 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Wed, 3 Feb 2016 16:15:50 -0500 Subject: [PATCH 01/38] Cloud Storage Java 7 nio library This is an import of previous work which was reviewed internally. --- gcloud-java-contrib/gcloud-java-nio/pom.xml | 140 +++++ .../nio/CloudStorageConfiguration.java | 148 +++++ .../nio/CloudStorageFileAttributeView.java | 78 +++ .../nio/CloudStorageFileAttributes.java | 59 ++ .../contrib/nio/CloudStorageFileSystem.java | 189 ++++++ .../nio/CloudStorageFileSystemProvider.java | 471 ++++++++++++++ .../nio/CloudStorageObjectAttributes.java | 155 +++++ .../CloudStorageObjectImmutableException.java | 8 + .../contrib/nio/CloudStorageOption.java | 17 + .../contrib/nio/CloudStorageOptions.java | 71 +++ .../storage/contrib/nio/CloudStoragePath.java | 332 ++++++++++ ...CloudStoragePseudoDirectoryAttributes.java | 97 +++ .../CloudStoragePseudoDirectoryException.java | 10 + .../contrib/nio/CloudStorageReadChannel.java | 137 ++++ .../storage/contrib/nio/CloudStorageUtil.java | 110 ++++ .../contrib/nio/CloudStorageWriteChannel.java | 96 +++ .../gcloud/storage/contrib/nio/OptionAcl.java | 13 + .../storage/contrib/nio/OptionBlockSize.java | 13 + .../contrib/nio/OptionCacheControl.java | 13 + .../contrib/nio/OptionContentDisposition.java | 13 + .../contrib/nio/OptionContentEncoding.java | 13 + .../storage/contrib/nio/OptionMimeType.java | 13 + .../contrib/nio/OptionUserMetadata.java | 14 + .../gcloud/storage/contrib/nio/UnixPath.java | 494 +++++++++++++++ .../storage/contrib/nio/package-info.java | 82 +++ .../storage/contrib/nio/AppEngineRule.java | 25 + .../nio/CloudStorageConfigurationTest.java | 58 ++ .../CloudStorageFileAttributeViewTest.java | 100 +++ .../nio/CloudStorageFileAttributesTest.java | 143 +++++ .../CloudStorageFileSystemProviderTest.java | 592 ++++++++++++++++++ .../nio/CloudStorageFileSystemTest.java | 111 ++++ .../contrib/nio/CloudStorageOptionsTest.java | 105 ++++ .../contrib/nio/CloudStoragePathTest.java | 486 ++++++++++++++ .../nio/CloudStorageReadChannelTest.java | 146 +++++ .../nio/CloudStorageWriteChannelTest.java | 107 ++++ .../storage/contrib/nio/UnixPathTest.java | 387 ++++++++++++ gcloud-java-contrib/pom.xml | 5 +- 37 files changed, 5050 insertions(+), 1 deletion(-) create mode 100644 gcloud-java-contrib/gcloud-java-nio/pom.xml create mode 100644 gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageConfiguration.java create mode 100644 gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileAttributeView.java create mode 100644 gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileAttributes.java create mode 100644 gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystem.java create mode 100644 gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystemProvider.java create mode 100644 gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageObjectAttributes.java create mode 100644 gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageObjectImmutableException.java create mode 100644 gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageOption.java create mode 100644 gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageOptions.java create mode 100644 gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStoragePath.java create mode 100644 gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStoragePseudoDirectoryAttributes.java create mode 100644 gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStoragePseudoDirectoryException.java create mode 100644 gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageReadChannel.java create mode 100644 gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageUtil.java create mode 100644 gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageWriteChannel.java create mode 100644 gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/OptionAcl.java create mode 100644 gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/OptionBlockSize.java create mode 100644 gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/OptionCacheControl.java create mode 100644 gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/OptionContentDisposition.java create mode 100644 gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/OptionContentEncoding.java create mode 100644 gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/OptionMimeType.java create mode 100644 gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/OptionUserMetadata.java create mode 100644 gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/UnixPath.java create mode 100644 gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/package-info.java create mode 100644 gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/AppEngineRule.java create mode 100644 gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageConfigurationTest.java create mode 100644 gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileAttributeViewTest.java create mode 100644 gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileAttributesTest.java create mode 100644 gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystemProviderTest.java create mode 100644 gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystemTest.java create mode 100644 gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageOptionsTest.java create mode 100644 gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStoragePathTest.java create mode 100644 gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageReadChannelTest.java create mode 100644 gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageWriteChannelTest.java create mode 100644 gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/UnixPathTest.java diff --git a/gcloud-java-contrib/gcloud-java-nio/pom.xml b/gcloud-java-contrib/gcloud-java-nio/pom.xml new file mode 100644 index 000000000000..69d3c5157c96 --- /dev/null +++ b/gcloud-java-contrib/gcloud-java-nio/pom.xml @@ -0,0 +1,140 @@ + + + 4.0.0 + com.google.gcloud + gcloud-java-nio + jar + GCloud Java NIO + + FileSystemProvider for Java NIO to access GCS transparently. + + + com.google.gcloud + gcloud-java-contrib + 0.1.4-SNAPSHOT + + + nio + + + + ${project.groupId} + gcloud-java + ${project.version} + + + com.google.guava + guava + 19.0 + + + com.google.code.findbugs + jsr305 + 2.0.1 + + + javax.inject + javax.inject + 1 + + + com.google.auto.service + auto-service + 1.0-rc2 + provided + + + com.google.auto.value + auto-value + 1.1 + provided + + + com.google.appengine.tools + appengine-gcs-client + 0.5 + + + junit + junit + 4.12 + test + + + com.google.guava + guava-testlib + 19.0 + test + + + com.google.truth + truth + 0.27 + test + + + org.mockito + mockito-core + 1.9.5 + + + com.google.appengine + appengine-testing + 1.9.30 + test + + + com.google.appengine + appengine-api-stubs + 1.9.30 + test + + + com.google.appengine + appengine-local-endpoints + 1.9.30 + test + + + + + + org.codehaus.mojo + exec-maven-plugin + + false + + + + maven-compiler-plugin + 3.1 + + 1.7 + 1.7 + UTF-8 + -Xlint:unchecked + + + + maven-jar-plugin + 2.6 + + + true + true + + true + true + + + ${project.artifactId} + ${project.groupId} + ${project.version} + ${buildNumber} + + + + + + + diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageConfiguration.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageConfiguration.java new file mode 100644 index 000000000000..e939edfb5a19 --- /dev/null +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageConfiguration.java @@ -0,0 +1,148 @@ +package com.google.gcloud.storage.contrib.nio; + +import static com.google.common.base.Preconditions.checkArgument; + +import com.google.auto.value.AutoValue; + +import java.util.Map; + +/** Configuration class for {@link CloudStorageFileSystem#forBucket} */ +@AutoValue +public abstract class CloudStorageConfiguration { + + /** Returns the path of the current working directory. Defaults to the root directory. */ + public abstract String workingDirectory(); + + /** + * Returns {@code true} if we shouldn't throw an exception when encountering object names + * containing superfluous slashes, e.g. {@code a//b}. + */ + public abstract boolean permitEmptyPathComponents(); + + /** + * Returns {@code true} if '/' prefix on absolute object names should be removed before I/O. + * + *

If you disable this feature, please take into consideration that all paths created from a + * URI will have the leading slash. + */ + public abstract boolean stripPrefixSlash(); + + /** Return {@code true} if paths with a trailing slash should be treated as fake directories. */ + public abstract boolean usePseudoDirectories(); + + /** Returns the block size (in bytes) used when talking to the GCS HTTP server. */ + public abstract int blockSize(); + + /** + * Creates a new builder, initialized with the following settings: + * + *

+ */ + public static Builder builder() { + return new Builder(); + } + + /** Builder for {@link CloudStorageConfiguration}. */ + public static final class Builder { + + private String workingDirectory = UnixPath.ROOT; + private boolean permitEmptyPathComponents = false; + private boolean stripPrefixSlash = true; + private boolean usePseudoDirectories = true; + private int blockSize = CloudStorageFileSystem.BLOCK_SIZE_DEFAULT; + + /** + * Changes the current working directory for a new filesystem. This cannot be changed once it's + * been set. You'll need to simply create another filesystem object. + * + * @throws IllegalArgumentException if {@code path} is not absolute. + */ + public Builder workingDirectory(String path) { + checkArgument(UnixPath.getPath(false, path).isAbsolute(), "not absolute: %s", path); + workingDirectory = path; + return this; + } + + /** + * Configures whether or not we should throw an exception when encountering object names + * containing superfluous slashes, e.g. {@code a//b} + */ + public Builder permitEmptyPathComponents(boolean value) { + permitEmptyPathComponents = value; + return this; + } + + /** + * Configures if the '/' prefix on absolute object names should be removed before I/O. + * + *

If you disable this feature, please take into consideration that all paths created from a + * URI will have the leading slash. + */ + public Builder stripPrefixSlash(boolean value) { + stripPrefixSlash = value; + return this; + } + + /** Configures if paths with a trailing slash should be treated as fake directories. */ + public Builder usePseudoDirectories(boolean value) { + usePseudoDirectories = value; + return this; + } + + /** + * Sets the block size in bytes that should be used for each HTTP request to the API. + * + *

The default is {@value CloudStorageFileSystem#BLOCK_SIZE_DEFAULT}. + */ + public Builder blockSize(int value) { + blockSize = value; + return this; + } + + /** Creates a new instance, but does not destroy the builder. */ + public CloudStorageConfiguration build() { + return new AutoValue_CloudStorageConfiguration( + workingDirectory, + permitEmptyPathComponents, + stripPrefixSlash, + usePseudoDirectories, + blockSize); + } + + Builder() {} + } + + static final CloudStorageConfiguration DEFAULT = builder().build(); + + static CloudStorageConfiguration fromMap(Map env) { + Builder builder = builder(); + for (Map.Entry entry : env.entrySet()) { + switch (entry.getKey()) { + case "workingDirectory": + builder.workingDirectory((String) entry.getValue()); + break; + case "permitEmptyPathComponents": + builder.permitEmptyPathComponents((Boolean) entry.getValue()); + break; + case "stripPrefixSlash": + builder.stripPrefixSlash((Boolean) entry.getValue()); + break; + case "usePseudoDirectories": + builder.usePseudoDirectories((Boolean) entry.getValue()); + break; + case "blockSize": + builder.blockSize((Integer) entry.getValue()); + break; + default: + throw new IllegalArgumentException(entry.getKey()); + } + } + return builder.build(); + } + + CloudStorageConfiguration() {} +} diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileAttributeView.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileAttributeView.java new file mode 100644 index 000000000000..5a4d4bc7c28c --- /dev/null +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileAttributeView.java @@ -0,0 +1,78 @@ +package com.google.gcloud.storage.contrib.nio; + +import static com.google.common.base.Verify.verifyNotNull; + +import com.google.appengine.tools.cloudstorage.GcsFileMetadata; +import com.google.common.base.MoreObjects; + +import java.io.IOException; +import java.nio.file.NoSuchFileException; +import java.nio.file.attribute.BasicFileAttributeView; +import java.nio.file.attribute.FileTime; +import java.util.Objects; + +import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; + +/** Metadata view for a Google Cloud Storage object. */ +@Immutable +public final class CloudStorageFileAttributeView implements BasicFileAttributeView { + + private final CloudStorageFileSystemProvider provider; + private final CloudStoragePath path; + + CloudStorageFileAttributeView(CloudStorageFileSystemProvider provider, CloudStoragePath path) { + this.provider = verifyNotNull(provider); + this.path = verifyNotNull(path); + } + + /** Returns {@value CloudStorageFileSystem#GCS_VIEW} */ + @Override + public String name() { + return CloudStorageFileSystem.GCS_VIEW; + } + + @Override + public CloudStorageFileAttributes readAttributes() throws IOException { + if (path.seemsLikeADirectory() + && path.getFileSystem().config().usePseudoDirectories()) { + return CloudStoragePseudoDirectoryAttributes.SINGLETON_INSTANCE; + } + GcsFileMetadata metadata = provider.getGcsService().getMetadata(path.getGcsFilename()); + if (metadata == null) { + throw new NoSuchFileException(path.toUri().toString()); + } + return new CloudStorageObjectAttributes(metadata); + } + + /** + * This feature is not supported, since Cloud Storage objects are immutable. + * + * @throws UnsupportedOperationException + */ + @Override + public void setTimes(FileTime lastModifiedTime, FileTime lastAccessTime, FileTime createTime) { + throw new CloudStorageObjectImmutableException(); + } + + @Override + public boolean equals(@Nullable Object other) { + return this == other + || other instanceof CloudStorageFileAttributeView + && Objects.equals(provider, ((CloudStorageFileAttributeView) other).provider) + && Objects.equals(path, ((CloudStorageFileAttributeView) other).path); + } + + @Override + public int hashCode() { + return Objects.hash(provider, path); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("provider", provider) + .add("path", path) + .toString(); + } +} diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileAttributes.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileAttributes.java new file mode 100644 index 000000000000..774a5499431d --- /dev/null +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileAttributes.java @@ -0,0 +1,59 @@ +package com.google.gcloud.storage.contrib.nio; + +import com.google.common.base.Optional; +import com.google.common.collect.ImmutableMap; + +import java.nio.file.attribute.BasicFileAttributes; + +/** Interface for attributes on a cloud storage file or pseudo-directory. */ +public interface CloudStorageFileAttributes extends BasicFileAttributes { + + /** + * Returns the HTTP etag hash for this object. + * + * @see "https://developers.google.com/storage/docs/hashes-etags" + */ + Optional etag(); + + /** + * Returns the mime type (e.g. text/plain) if it was set for this object. + * + * @see "http://en.wikipedia.org/wiki/Internet_media_type#List_of_common_media_types" + */ + Optional mimeType(); + + /** + * Returns the ACL value on this Cloud Storage object. + * + * @see "https://developers.google.com/storage/docs/reference-headers#acl" + */ + Optional acl(); + + /** + * Returns the {@code Cache-Control} HTTP header value, if set on this object. + * + * @see "https://developers.google.com/storage/docs/reference-headers#cachecontrol" + */ + Optional cacheControl(); + + /** + * Returns the {@code Content-Encoding} HTTP header value, if set on this object. + * + * @see "https://developers.google.com/storage/docs/reference-headers#contentencoding" + */ + Optional contentEncoding(); + + /** + * Returns the {@code Content-Disposition} HTTP header value, if set on this object. + * + * @see "https://developers.google.com/storage/docs/reference-headers#contentdisposition" + */ + Optional contentDisposition(); + + /** + * Returns user-specified metadata associated with this object. + * + * @see "https://developers.google.com/storage/docs/reference-headers#contentdisposition" + */ + ImmutableMap userMetadata(); +} diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystem.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystem.java new file mode 100644 index 000000000000..b4318be63f31 --- /dev/null +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystem.java @@ -0,0 +1,189 @@ +package com.google.gcloud.storage.contrib.nio; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.collect.ImmutableSet; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.file.FileStore; +import java.nio.file.FileSystem; +import java.nio.file.Path; +import java.nio.file.PathMatcher; +import java.nio.file.WatchService; +import java.nio.file.attribute.FileTime; +import java.nio.file.attribute.UserPrincipalLookupService; +import java.util.Objects; +import java.util.Set; + +import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; + +/** + * Google Cloud Storage {@link FileSystem} + * + * @see + * Concepts and Terminology + * @see + * Bucket and Object Naming Guidelines + */ +@Immutable +public final class CloudStorageFileSystem extends FileSystem { + + /** + * Returns Google Cloud Storage {@link FileSystem} object for a given bucket name. + * + *

NOTE: You may prefer to use Java's standard API instead:

   {@code
+   *
+   *   FileSystem fs = FileSystems.getFileSystem(URI.create("gs://bucket"));}
+ * + *

However some systems and build environments might be flaky when it comes to Java SPI. This + * is because services are generally runtime dependencies and depend on a META-INF file being + * present in your jar (generated by Google Auto at compile-time). In such cases, this method + * provides a simpler alternative. + * + * @see #forBucket(String, CloudStorageConfiguration) + * @see java.nio.file.FileSystems#getFileSystem(java.net.URI) + */ + public static CloudStorageFileSystem forBucket(String bucket) { + return forBucket(bucket, CloudStorageConfiguration.DEFAULT); + } + + /** + * Creates a new filesystem for a particular bucket, with customizable settings. + * + * @see #forBucket(String) + */ + public static CloudStorageFileSystem forBucket(String bucket, CloudStorageConfiguration config) { + checkArgument(!bucket.startsWith(URI_SCHEME + ":"), + "Bucket name must not have schema: %s", bucket); + return new CloudStorageFileSystem( + new CloudStorageFileSystemProvider(), bucket, checkNotNull(config)); + } + + public static final String URI_SCHEME = "gs"; + public static final String GCS_VIEW = "gcs"; + public static final String BASIC_VIEW = "basic"; + public static final int BLOCK_SIZE_DEFAULT = 2 * 1024 * 1024; + public static final FileTime FILE_TIME_UNKNOWN = FileTime.fromMillis(0); + public static final ImmutableSet SUPPORTED_VIEWS = ImmutableSet.of(BASIC_VIEW, GCS_VIEW); + + private final CloudStorageFileSystemProvider provider; + private final String bucket; + private final CloudStorageConfiguration config; + + CloudStorageFileSystem( + CloudStorageFileSystemProvider provider, + String bucket, + CloudStorageConfiguration config) { + checkArgument(!bucket.isEmpty(), "bucket"); + this.provider = provider; + this.bucket = bucket; + this.config = config; + } + + @Override + public CloudStorageFileSystemProvider provider() { + return provider; + } + + /** Returns the Cloud Storage bucket name being served by this file system. */ + public String bucket() { + return bucket; + } + + /** Returns the configuration object for this filesystem instance. */ + public CloudStorageConfiguration config() { + return config; + } + + /** Converts a cloud storage object name to a {@link Path} object. */ + @Override + public CloudStoragePath getPath(String first, String... more) { + checkArgument(!first.startsWith(URI_SCHEME + ":"), + "GCS FileSystem.getPath() must not have schema and bucket name: %s", first); + return CloudStoragePath.getPath(this, first, more); + } + + /** Does nothing. */ + @Override + public void close() {} + + /** Returns {@code true} */ + @Override + public boolean isOpen() { + return true; + } + + /** Returns {@code false} */ + @Override + public boolean isReadOnly() { + return false; + } + + /** Returns {@value UnixPath#SEPARATOR} */ + @Override + public String getSeparator() { + return "" + UnixPath.SEPARATOR; + } + + @Override + public Iterable getRootDirectories() { + return ImmutableSet.of(CloudStoragePath.getPath(this, UnixPath.ROOT)); + } + + @Override + public Iterable getFileStores() { + return ImmutableSet.of(); + } + + @Override + public Set supportedFileAttributeViews() { + return SUPPORTED_VIEWS; + } + + /** @throws UnsupportedOperationException */ + @Override + public PathMatcher getPathMatcher(String syntaxAndPattern) { + // TODO(b/18997520): Implement me. + throw new UnsupportedOperationException(); + } + + /** @throws UnsupportedOperationException */ + @Override + public UserPrincipalLookupService getUserPrincipalLookupService() { + // TODO(b/18997520): Implement me. + throw new UnsupportedOperationException(); + } + + /** @throws UnsupportedOperationException */ + @Override + public WatchService newWatchService() throws IOException { + // TODO(b/18997520): Implement me. + throw new UnsupportedOperationException(); + } + + @Override + public boolean equals(@Nullable Object other) { + return this == other + || other instanceof CloudStorageFileSystem + && Objects.equals(config, ((CloudStorageFileSystem) other).config) + && Objects.equals(bucket, ((CloudStorageFileSystem) other).bucket); + } + + @Override + public int hashCode() { + return Objects.hash(bucket); + } + + @Override + public String toString() { + try { + return new URI(URI_SCHEME, bucket, null, null).toString(); + } catch (URISyntaxException e) { + throw new AssertionError(e); + } + } +} diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystemProvider.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystemProvider.java new file mode 100644 index 000000000000..f1964b231ed1 --- /dev/null +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystemProvider.java @@ -0,0 +1,471 @@ +package com.google.gcloud.storage.contrib.nio; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Strings.isNullOrEmpty; +import static com.google.common.base.Suppliers.memoize; +import static com.google.gcloud.storage.contrib.nio.CloudStorageFileSystem.URI_SCHEME; +import static com.google.gcloud.storage.contrib.nio.CloudStorageUtil.buildFileOptions; +import static com.google.gcloud.storage.contrib.nio.CloudStorageUtil.checkBucket; +import static com.google.gcloud.storage.contrib.nio.CloudStorageUtil.checkNotNullArray; +import static com.google.gcloud.storage.contrib.nio.CloudStorageUtil.checkPath; +import static com.google.gcloud.storage.contrib.nio.CloudStorageUtil.copyFileOptions; +import static com.google.gcloud.storage.contrib.nio.CloudStorageUtil.stripPathFromUri; + +import com.google.appengine.tools.cloudstorage.GcsFileMetadata; +import com.google.appengine.tools.cloudstorage.GcsFileOptions; +import com.google.appengine.tools.cloudstorage.GcsFilename; +import com.google.appengine.tools.cloudstorage.GcsInputChannel; +import com.google.appengine.tools.cloudstorage.GcsOutputChannel; +import com.google.appengine.tools.cloudstorage.GcsService; +import com.google.appengine.tools.cloudstorage.GcsServiceFactory; +import com.google.auto.service.AutoService; +import com.google.common.base.MoreObjects; +import com.google.common.base.Supplier; +import com.google.common.primitives.Ints; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.nio.ByteBuffer; +import java.nio.channels.SeekableByteChannel; +import java.nio.file.AccessMode; +import java.nio.file.AtomicMoveNotSupportedException; +import java.nio.file.CopyOption; +import java.nio.file.DirectoryStream; +import java.nio.file.DirectoryStream.Filter; +import java.nio.file.FileAlreadyExistsException; +import java.nio.file.FileStore; +import java.nio.file.LinkOption; +import java.nio.file.NoSuchFileException; +import java.nio.file.OpenOption; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.nio.file.StandardOpenOption; +import java.nio.file.attribute.BasicFileAttributeView; +import java.nio.file.attribute.BasicFileAttributes; +import java.nio.file.attribute.FileAttribute; +import java.nio.file.attribute.FileAttributeView; +import java.nio.file.spi.FileSystemProvider; +import java.util.Collections; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +import javax.annotation.Nullable; +import javax.annotation.concurrent.ThreadSafe; + +/** Google Cloud Storage {@link FileSystemProvider} */ +@ThreadSafe +@AutoService(FileSystemProvider.class) +public final class CloudStorageFileSystemProvider extends FileSystemProvider { + + private static final Supplier gcsServiceSupplier = + memoize(new Supplier() { + @Override + public GcsService get() { + return GcsServiceFactory.createGcsService(); + }}); + + private final GcsService gcsService; + + /** + * Default constructor which should only be called by Java SPI. + * + * @see java.nio.file.FileSystems#getFileSystem(URI) + * @see CloudStorageFileSystem#forBucket(String) + */ + public CloudStorageFileSystemProvider() { + this(gcsServiceSupplier.get()); + } + + private CloudStorageFileSystemProvider(GcsService gcsService) { + this.gcsService = checkNotNull(gcsService); + } + + GcsService getGcsService() { + return gcsService; + } + + @Override + public String getScheme() { + return URI_SCHEME; + } + + /** Returns cloud storage file system, provided a URI with no path, e.g. {@code gs://bucket} */ + @Override + public CloudStorageFileSystem getFileSystem(URI uri) { + return newFileSystem(uri, Collections.emptyMap()); + } + + /** Returns cloud storage file system, provided a URI with no path, e.g. {@code gs://bucket} */ + @Override + public CloudStorageFileSystem newFileSystem(URI uri, Map env) { + checkArgument(uri.getScheme().equalsIgnoreCase(URI_SCHEME), + "Cloud Storage URIs must have '%s' scheme: %s", URI_SCHEME, uri); + checkArgument(!isNullOrEmpty(uri.getHost()), + "%s:// URIs must have a host: %s", URI_SCHEME, uri); + checkArgument(uri.getPort() == -1 + && isNullOrEmpty(uri.getPath()) + && isNullOrEmpty(uri.getQuery()) + && isNullOrEmpty(uri.getFragment()) + && isNullOrEmpty(uri.getUserInfo()), + "GCS FileSystem URIs mustn't have: port, userinfo, path, query, or fragment: %s", uri); + checkBucket(uri.getHost()); + return new CloudStorageFileSystem(this, uri.getHost(), CloudStorageConfiguration.fromMap(env)); + } + + @Override + public CloudStoragePath getPath(URI uri) { + return CloudStoragePath.getPath(getFileSystem(stripPathFromUri(uri)), uri.getPath()); + } + + @Override + public SeekableByteChannel newByteChannel( + Path path, Set options, FileAttribute... attrs) throws IOException { + checkNotNull(path); + checkNotNullArray(attrs); + if (options.contains(StandardOpenOption.WRITE)) { + // TODO(b/18997618): Make our OpenOptions implement FileAttribute. Also remove buffer option. + return newWriteChannel(path, options); + } else { + return newReadChannel(path, options); + } + } + + private SeekableByteChannel newReadChannel( + Path path, Set options) throws IOException { + for (OpenOption option : options) { + if (option instanceof StandardOpenOption) { + switch ((StandardOpenOption) option) { + case READ: + // Default behavior. + break; + case SPARSE: + case TRUNCATE_EXISTING: + // Ignored by specification. + break; + case WRITE: + throw new IllegalArgumentException("READ+WRITE not supported yet"); + case APPEND: + case CREATE: + case CREATE_NEW: + case DELETE_ON_CLOSE: + case DSYNC: + case SYNC: + default: + throw new UnsupportedOperationException(option.toString()); + } + } else { + throw new UnsupportedOperationException(option.toString()); + } + } + CloudStoragePath cloudPath = checkPath(path); + if (cloudPath.seemsLikeADirectoryAndUsePseudoDirectories()) { + throw new CloudStoragePseudoDirectoryException(cloudPath); + } + return CloudStorageReadChannel.create(gcsService, cloudPath.getGcsFilename(), 0); + } + + private SeekableByteChannel newWriteChannel( + Path path, Set options) throws IOException { + boolean wantCreateNew = false; + for (OpenOption option : options) { + if (option instanceof StandardOpenOption) { + switch ((StandardOpenOption) option) { + case CREATE: + case TRUNCATE_EXISTING: + case WRITE: + // Default behavior. + break; + case SPARSE: + // Ignored by specification. + break; + case CREATE_NEW: + wantCreateNew = true; + break; + case READ: + throw new IllegalArgumentException("READ+WRITE not supported yet"); + case APPEND: + case DELETE_ON_CLOSE: + case DSYNC: + case SYNC: + default: + throw new UnsupportedOperationException(option.toString()); + } + } else if (option instanceof CloudStorageOption) { + // These will be interpreted later. + } else { + throw new UnsupportedOperationException(option.toString()); + } + } + CloudStoragePath cloudPath = checkPath(path); + if (cloudPath.seemsLikeADirectoryAndUsePseudoDirectories()) { + throw new CloudStoragePseudoDirectoryException(cloudPath); + } + if (wantCreateNew) { + // XXX: Java's documentation says this should be atomic. + if (gcsService.getMetadata(cloudPath.getGcsFilename()) != null) { + throw new FileAlreadyExistsException(cloudPath.toString()); + } + } + GcsFilename file = cloudPath.getGcsFilename(); + GcsFileOptions fileOptions = buildFileOptions(new GcsFileOptions.Builder(), options.toArray()); + return new CloudStorageWriteChannel(gcsService.createOrReplace(file, fileOptions)); + } + + @Override + public InputStream newInputStream(Path path, OpenOption... options) throws IOException { + InputStream result = super.newInputStream(path, options); + CloudStoragePath cloudPath = checkPath(path); + int blockSize = cloudPath.getFileSystem().config().blockSize(); + for (OpenOption option : options) { + if (option instanceof OptionBlockSize) { + blockSize = ((OptionBlockSize) option).size(); + } + } + return new BufferedInputStream(result, blockSize); + } + + @Override + public final boolean deleteIfExists(Path path) throws IOException { + CloudStoragePath cloudPath = checkPath(path); + if (cloudPath.seemsLikeADirectoryAndUsePseudoDirectories()) { + throw new CloudStoragePseudoDirectoryException(cloudPath); + } + return gcsService.delete(cloudPath.getGcsFilename()); + } + + @Override + public void delete(Path path) throws IOException { + CloudStoragePath cloudPath = checkPath(path); + if (!deleteIfExists(cloudPath)) { + throw new NoSuchFileException(cloudPath.toString()); + } + } + + @Override + public void move(Path source, Path target, CopyOption... options) throws IOException { + for (CopyOption option : options) { + if (option == StandardCopyOption.ATOMIC_MOVE) { + throw new AtomicMoveNotSupportedException(source.toString(), target.toString(), + "Google Cloud Storage does not support atomic move operations."); + } + } + copy(source, target, options); + delete(source); + } + + @Override + public void copy(Path source, Path target, CopyOption... options) throws IOException { + boolean wantCopyAttributes = false; + boolean wantReplaceExisting = false; + int blockSize = -1; + for (CopyOption option : options) { + if (option instanceof StandardCopyOption) { + switch ((StandardCopyOption) option) { + case COPY_ATTRIBUTES: + wantCopyAttributes = true; + break; + case REPLACE_EXISTING: + wantReplaceExisting = true; + break; + case ATOMIC_MOVE: + default: + throw new UnsupportedOperationException(option.toString()); + } + } else if (option instanceof CloudStorageOption) { + if (option instanceof OptionBlockSize) { + blockSize = ((OptionBlockSize) option).size(); + } + // The rest will be interpreted later. + } else { + throw new UnsupportedOperationException(option.toString()); + } + } + CloudStoragePath fromPath = checkPath(source); + CloudStoragePath toPath = checkPath(target); + + blockSize = blockSize != -1 ? blockSize + : Ints.max(fromPath.getFileSystem().config().blockSize(), + toPath.getFileSystem().config().blockSize()); + if (fromPath.seemsLikeADirectory() && toPath.seemsLikeADirectory()) { + if (fromPath.getFileSystem().config().usePseudoDirectories() + && toPath.getFileSystem().config().usePseudoDirectories()) { + // NOOP: This would normally create an empty directory. + return; + } else { + checkArgument(!fromPath.getFileSystem().config().usePseudoDirectories() + && !toPath.getFileSystem().config().usePseudoDirectories(), + "File systems associated with paths don't agree on pseudo-directories."); + } + } + if (fromPath.seemsLikeADirectoryAndUsePseudoDirectories()) { + throw new CloudStoragePseudoDirectoryException(fromPath); + } + if (toPath.seemsLikeADirectoryAndUsePseudoDirectories()) { + throw new CloudStoragePseudoDirectoryException(toPath); + } + GcsFilename from = fromPath.getGcsFilename(); + GcsFilename to = toPath.getGcsFilename(); + GcsFileMetadata metadata = gcsService.getMetadata(from); + if (metadata == null) { + throw new NoSuchFileException(source.toString()); + } + if (fromPath.equals(toPath)) { + return; + } + if (!wantReplaceExisting && gcsService.getMetadata(to) != null) { + throw new FileAlreadyExistsException(target.toString()); + } + GcsFileOptions.Builder builder = wantCopyAttributes + ? copyFileOptions(metadata.getOptions()) + : new GcsFileOptions.Builder(); + GcsFileOptions fileOptions = buildFileOptions(builder, options); + try (GcsInputChannel input = gcsService.openReadChannel(from, 0); + GcsOutputChannel output = gcsService.createOrReplace(to, fileOptions)) { + ByteBuffer block = ByteBuffer.allocate(blockSize); + while (input.read(block) != -1) { + block.flip(); + while (block.hasRemaining()) { + output.write(block); + } + block.clear(); + } + } + } + + @Override + public boolean isSameFile(Path path, Path path2) { + return checkPath(path).equals(checkPath(path2)); + } + + /** Returns {@code false} */ + @Override + public boolean isHidden(Path path) { + checkPath(path); + return false; + } + + @Override + public void checkAccess(Path path, AccessMode... modes) throws IOException { + for (AccessMode mode : modes) { + switch (mode) { + case READ: + case WRITE: + break; + case EXECUTE: + default: + throw new UnsupportedOperationException(mode.toString()); + } + } + CloudStoragePath cloudPath = checkPath(path); + if (cloudPath.seemsLikeADirectoryAndUsePseudoDirectories()) { + return; + } + if (gcsService.getMetadata(cloudPath.getGcsFilename()) == null) { + throw new NoSuchFileException(path.toString()); + } + } + + @Override + public A readAttributes( + Path path, Class type, LinkOption... options) throws IOException { + CloudStoragePath cloudPath = checkPath(path); + checkNotNull(type); + checkNotNullArray(options); + if (type != CloudStorageFileAttributes.class && type != BasicFileAttributes.class) { + throw new UnsupportedOperationException(type.getSimpleName()); + } + if (cloudPath.seemsLikeADirectoryAndUsePseudoDirectories()) { + @SuppressWarnings("unchecked") + A result = (A) CloudStoragePseudoDirectoryAttributes.SINGLETON_INSTANCE; + return result; + } + GcsFileMetadata metadata = gcsService.getMetadata(cloudPath.getGcsFilename()); + if (metadata == null) { + throw new NoSuchFileException(path.toString()); + } + @SuppressWarnings("unchecked") + A result = (A) new CloudStorageObjectAttributes(metadata); + return result; + } + + @Override + public V getFileAttributeView( + Path path, Class type, LinkOption... options) { + CloudStoragePath cloudPath = checkPath(path); + checkNotNull(type); + checkNotNullArray(options); + if (type != CloudStorageFileAttributeView.class && type != BasicFileAttributeView.class) { + throw new UnsupportedOperationException(type.getSimpleName()); + } + @SuppressWarnings("unchecked") + V result = (V) new CloudStorageFileAttributeView(this, cloudPath); + return result; + } + + /** Does nothing since GCS uses fake directories. */ + @Override + public void createDirectory(Path dir, FileAttribute... attrs) { + checkPath(dir); + checkNotNullArray(attrs); + } + + /** @throws UnsupportedOperationException */ + @Override + public DirectoryStream newDirectoryStream(Path dir, Filter filter) { + // TODO(b/18997618): Implement me. + throw new UnsupportedOperationException(); + } + + /** + * This feature is not supported. Please use {@link #readAttributes(Path, Class, LinkOption...)} + * + * @throws UnsupportedOperationException + */ + @Override + public Map readAttributes(Path path, String attributes, LinkOption... options) { + throw new UnsupportedOperationException(); + } + + /** + * This feature is not supported, since Cloud Storage objects are immutable. + * + * @throws UnsupportedOperationException + */ + @Override + public void setAttribute(Path path, String attribute, Object value, LinkOption... options) { + throw new CloudStorageObjectImmutableException(); + } + + /** + * This feature is not supported. + * + * @throws UnsupportedOperationException + */ + @Override + public FileStore getFileStore(Path path) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean equals(@Nullable Object other) { + return this == other + || other instanceof CloudStorageFileSystemProvider + && Objects.equals(gcsService, ((CloudStorageFileSystemProvider) other).gcsService); + } + + @Override + public int hashCode() { + return Objects.hash(gcsService); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("gcsService", gcsService) + .toString(); + } +} diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageObjectAttributes.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageObjectAttributes.java new file mode 100644 index 000000000000..b3265cd6276b --- /dev/null +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageObjectAttributes.java @@ -0,0 +1,155 @@ +package com.google.gcloud.storage.contrib.nio; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.gcloud.storage.contrib.nio.CloudStorageFileSystem.FILE_TIME_UNKNOWN; + +import com.google.appengine.tools.cloudstorage.GcsFileMetadata; +import com.google.common.base.MoreObjects; +import com.google.common.base.Optional; +import com.google.common.collect.ImmutableMap; + +import java.nio.file.attribute.FileTime; +import java.util.Objects; + +import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; + +/** Metadata for a Google Cloud Storage object. */ +@Immutable +final class CloudStorageObjectAttributes implements CloudStorageFileAttributes { + + private final GcsFileMetadata metadata; + + CloudStorageObjectAttributes(GcsFileMetadata metadata) { + this.metadata = checkNotNull(metadata); + } + + @Override + public long size() { + return metadata.getLength(); + } + + @Override + public FileTime creationTime() { + if (metadata.getLastModified() == null) { + return FILE_TIME_UNKNOWN; + } + return FileTime.fromMillis(metadata.getLastModified().getTime()); + } + + @Override + public FileTime lastModifiedTime() { + return creationTime(); + } + + /** Returns the HTTP etag hash for this object. */ + @Override + public Optional etag() { + return Optional.fromNullable(metadata.getEtag()); + } + + /** Returns the mime type (e.g. text/plain) if it was set for this object. */ + @Override + public Optional mimeType() { + return Optional.fromNullable(metadata.getOptions().getMimeType()); + } + + /** + * Returns the ACL value on this Cloud Storage object. + * + * @see "https://developers.google.com/storage/docs/reference-headers#acl" + */ + @Override + public Optional acl() { + return Optional.fromNullable(metadata.getOptions().getAcl()); + } + + /** + * Returns the {@code Cache-Control} HTTP header value, if set on this object. + * + * @see "https://developers.google.com/storage/docs/reference-headers#cachecontrol" + */ + @Override + public Optional cacheControl() { + return Optional.fromNullable(metadata.getOptions().getCacheControl()); + } + + /** + * Returns the {@code Content-Encoding} HTTP header value, if set on this object. + * + * @see "https://developers.google.com/storage/docs/reference-headers#contentencoding" + */ + @Override + public Optional contentEncoding() { + return Optional.fromNullable(metadata.getOptions().getContentEncoding()); + } + + /** + * Returns the {@code Content-Disposition} HTTP header value, if set on this object. + * + * @see "https://developers.google.com/storage/docs/reference-headers#contentdisposition" + */ + @Override + public Optional contentDisposition() { + return Optional.fromNullable(metadata.getOptions().getContentDisposition()); + } + + /** + * Returns user-specified metadata associated with this object. + * + * @see "https://developers.google.com/storage/docs/reference-headers#contentdisposition" + */ + @Override + public ImmutableMap userMetadata() { + return ImmutableMap.copyOf(metadata.getOptions().getUserMetadata()); + } + + @Override + public boolean isDirectory() { + return false; + } + + @Override + public boolean isOther() { + return false; + } + + @Override + public boolean isRegularFile() { + return true; + } + + @Override + public boolean isSymbolicLink() { + return false; + } + + @Override + public FileTime lastAccessTime() { + return FILE_TIME_UNKNOWN; + } + + @Override + public Object fileKey() { + return metadata.getFilename(); + } + + @Override + public boolean equals(@Nullable Object other) { + return this == other + || other instanceof CloudStorageObjectAttributes + && Objects.equals(metadata, ((CloudStorageObjectAttributes) other).metadata); + } + + @Override + public int hashCode() { + return Objects.hash(metadata); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("metadata", metadata) + .toString(); + } +} diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageObjectImmutableException.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageObjectImmutableException.java new file mode 100644 index 000000000000..669f1c9ae91b --- /dev/null +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageObjectImmutableException.java @@ -0,0 +1,8 @@ +package com.google.gcloud.storage.contrib.nio; + +/** Exception thrown to indicate we don't support a mutation of a cloud storage object. */ +public final class CloudStorageObjectImmutableException extends UnsupportedOperationException { + CloudStorageObjectImmutableException() { + super("Cloud Storage objects are immutable."); + } +} diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageOption.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageOption.java new file mode 100644 index 000000000000..4142cef2bb6a --- /dev/null +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageOption.java @@ -0,0 +1,17 @@ +package com.google.gcloud.storage.contrib.nio; + +import java.nio.file.CopyOption; +import java.nio.file.OpenOption; + +/** Master interface for file operation option classes related to Google Cloud Storage. */ +public interface CloudStorageOption { + + /** Interface for GCS options that can be specified when opening files. */ + public interface Open extends CloudStorageOption, OpenOption {} + + /** Interface for GCS options that can be specified when copying files. */ + public interface Copy extends CloudStorageOption, CopyOption {} + + /** Interface for GCS options that can be specified when opening or copying files. */ + public interface OpenCopy extends Open, Copy {} +} diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageOptions.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageOptions.java new file mode 100644 index 000000000000..c66842d44c03 --- /dev/null +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageOptions.java @@ -0,0 +1,71 @@ +package com.google.gcloud.storage.contrib.nio; + +/** Helper class for specifying options when opening and copying Cloud Storage files. */ +public final class CloudStorageOptions { + + /** Sets the mime type header on an object, e.g. {@code "text/plain"}. */ + public static CloudStorageOption.OpenCopy withMimeType(String mimeType) { + return OptionMimeType.create(mimeType); + } + + /** Disables caching on an object. Same as: {@code withCacheControl("no-cache")}. */ + public static CloudStorageOption.OpenCopy withoutCaching() { + return withCacheControl("no-cache"); + } + + /** + * Sets the {@code Cache-Control} HTTP header on an object. + * + * @see "https://developers.google.com/storage/docs/reference-headers#cachecontrol" + */ + public static CloudStorageOption.OpenCopy withCacheControl(String cacheControl) { + return OptionCacheControl.create(cacheControl); + } + + /** + * Sets the {@code Content-Disposition} HTTP header on an object. + * + * @see "https://developers.google.com/storage/docs/reference-headers#contentdisposition" + */ + public static CloudStorageOption.OpenCopy withContentDisposition(String contentDisposition) { + return OptionContentDisposition.create(contentDisposition); + } + + /** + * Sets the {@code Content-Encoding} HTTP header on an object. + * + * @see "https://developers.google.com/storage/docs/reference-headers#contentencoding" + */ + public static CloudStorageOption.OpenCopy withContentEncoding(String contentEncoding) { + return OptionContentEncoding.create(contentEncoding); + } + + /** + * Sets the ACL value on a Cloud Storage object. + * + * @see "https://developers.google.com/storage/docs/reference-headers#acl" + */ + public static CloudStorageOption.OpenCopy withAcl(String acl) { + return OptionAcl.create(acl); + } + + /** + * Sets an unmodifiable piece of user metadata on a Cloud Storage object. + * + * @see "https://developers.google.com/storage/docs/reference-headers#xgoogmeta" + */ + public static CloudStorageOption.OpenCopy withUserMetadata(String key, String value) { + return OptionUserMetadata.create(key, value); + } + + /** + * Sets the block size (in bytes) when talking to the GCS server. + * + *

The default is {@value CloudStorageFileSystem#BLOCK_SIZE_DEFAULT}. + */ + public static CloudStorageOption.OpenCopy withBlockSize(int size) { + return OptionBlockSize.create(size); + } + + private CloudStorageOptions() {} +} diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStoragePath.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStoragePath.java new file mode 100644 index 000000000000..7d8e88753ce8 --- /dev/null +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStoragePath.java @@ -0,0 +1,332 @@ +package com.google.gcloud.storage.contrib.nio; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.gcloud.storage.contrib.nio.CloudStorageFileSystem.URI_SCHEME; +import static com.google.gcloud.storage.contrib.nio.CloudStorageUtil.checkNotNullArray; +import static com.google.gcloud.storage.contrib.nio.CloudStorageUtil.checkPath; + +import com.google.appengine.tools.cloudstorage.GcsFilename; +import com.google.common.collect.UnmodifiableIterator; + +import java.io.File; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.file.LinkOption; +import java.nio.file.Path; +import java.nio.file.WatchEvent.Kind; +import java.nio.file.WatchEvent.Modifier; +import java.nio.file.WatchKey; +import java.nio.file.WatchService; +import java.util.Collections; +import java.util.Iterator; +import java.util.Objects; +import java.util.regex.Pattern; + +import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; + +/** + * Google Cloud Storage {@link Path} + * + * @see UnixPath + */ +@Immutable +public final class CloudStoragePath implements Path { + + private static final Pattern EXTRA_SLASHES_OR_DOT_DIRS_PATTERN = + Pattern.compile("^\\.\\.?/|//|/\\.\\.?/|/\\.\\.?$"); + + private final CloudStorageFileSystem fileSystem; + private final UnixPath path; + + private CloudStoragePath(CloudStorageFileSystem fileSystem, UnixPath path) { + this.fileSystem = fileSystem; + this.path = path; + } + + static CloudStoragePath getPath( + CloudStorageFileSystem fileSystem, String path, String... more) { + return new CloudStoragePath( + fileSystem, UnixPath.getPath(fileSystem.config().permitEmptyPathComponents(), path, more)); + } + + /** Returns the Cloud Storage bucket name being served by this file system. */ + public String bucket() { + return fileSystem.bucket(); + } + + /** Returns path converted to a {@link GcsFilename} so I/O can be performed. */ + GcsFilename getGcsFilename() { + return new GcsFilename(bucket(), toRealPath().path.toString()); + } + + boolean seemsLikeADirectory() { + return path.seemsLikeADirectory(); + } + + boolean seemsLikeADirectoryAndUsePseudoDirectories() { + return path.seemsLikeADirectory() && fileSystem.config().usePseudoDirectories(); + } + + @Override + public CloudStorageFileSystem getFileSystem() { + return fileSystem; + } + + @Nullable + @Override + public CloudStoragePath getRoot() { + return newPath(path.getRoot()); + } + + @Override + public boolean isAbsolute() { + return path.isAbsolute(); + } + + /** + * Changes relative path to absolute, using + * {@link CloudStorageConfiguration#workingDirectory() workingDirectory} as the current dir. + */ + @Override + public CloudStoragePath toAbsolutePath() { + return newPath(path.toAbsolutePath(getWorkingDirectory())); + } + + /** + * Returns this path rewritten to the Cloud Storage object name that'd be used to perform i/o. + * + *

This method makes path {@link #toAbsolutePath() absolute} and removes the prefix slash from + * the absolute path when {@link CloudStorageConfiguration#stripPrefixSlash() stripPrefixSlash} + * is {@code true}. + * + * @throws IllegalArgumentException if path contains extra slashes or dot-dirs when + * {@link CloudStorageConfiguration#permitEmptyPathComponents() permitEmptyPathComponents} + * is {@code false}, or if the resulting path is empty. + */ + @Override + public CloudStoragePath toRealPath(LinkOption... options) { + checkNotNullArray(options); + return newPath(toRealPathInternal(true)); + } + + private UnixPath toRealPathInternal(boolean errorCheck) { + UnixPath objectName = path.toAbsolutePath(getWorkingDirectory()); + if (errorCheck && !fileSystem.config().permitEmptyPathComponents()) { + checkArgument(!EXTRA_SLASHES_OR_DOT_DIRS_PATTERN.matcher(objectName).find(), + "I/O not allowed on dot-dirs or extra slashes when !permitEmptyPathComponents: %s", + objectName); + } + if (fileSystem.config().stripPrefixSlash()) { + objectName = objectName.removeBeginningSeparator(); + } + checkArgument(!errorCheck || !objectName.isEmpty(), + "I/O not allowed on empty GCS object names."); + return objectName; + } + + /** + * Returns path without extra slashes or {@code .} and {@code ..} and preserves trailing slash. + * + * @see java.nio.file.Path#normalize() + */ + @Override + public CloudStoragePath normalize() { + return newPath(path.normalize()); + } + + @Override + public CloudStoragePath resolve(Path object) { + return newPath(path.resolve(checkPath(object).path)); + } + + @Override + public CloudStoragePath resolve(String other) { + return newPath(path.resolve(getUnixPath(other))); + } + + @Override + public CloudStoragePath resolveSibling(Path other) { + return newPath(path.resolveSibling(checkPath(other).path)); + } + + @Override + public CloudStoragePath resolveSibling(String other) { + return newPath(path.resolveSibling(getUnixPath(other))); + } + + @Override + public CloudStoragePath relativize(Path object) { + return newPath(path.relativize(checkPath(object).path)); + } + + @Nullable + @Override + public CloudStoragePath getParent() { + return newPath(path.getParent()); + } + + @Nullable + @Override + public CloudStoragePath getFileName() { + return newPath(path.getFileName()); + } + + @Override + public CloudStoragePath subpath(int beginIndex, int endIndex) { + return newPath(path.subpath(beginIndex, endIndex)); + } + + @Override + public int getNameCount() { + return path.getNameCount(); + } + + @Override + public CloudStoragePath getName(int index) { + return newPath(path.getName(index)); + } + + @Override + public boolean startsWith(Path other) { + if (!(checkNotNull(other) instanceof CloudStoragePath)) { + return false; + } + CloudStoragePath that = (CloudStoragePath) other; + if (!bucket().equals(that.bucket())) { + return false; + } + return path.startsWith(that.path); + } + + @Override + public boolean startsWith(String other) { + return path.startsWith(getUnixPath(other)); + } + + @Override + public boolean endsWith(Path other) { + if (!(checkNotNull(other) instanceof CloudStoragePath)) { + return false; + } + CloudStoragePath that = (CloudStoragePath) other; + if (!bucket().equals(that.bucket())) { + return false; + } + return path.endsWith(that.path); + } + + @Override + public boolean endsWith(String other) { + return path.endsWith(getUnixPath(other)); + } + + /** @throws UnsupportedOperationException */ + @Override + public WatchKey register(WatchService watcher, Kind[] events, Modifier... modifiers) { + // TODO(b/18998105): Implement me. + throw new UnsupportedOperationException(); + } + + /** @throws UnsupportedOperationException */ + @Override + public WatchKey register(WatchService watcher, Kind... events) { + // TODO(b/18998105): Implement me. + throw new UnsupportedOperationException(); + } + + /** + * This operation is not supported, since GCS files aren't backed by the local file system. + * + * @throws UnsupportedOperationException + */ + @Override + public File toFile() { + throw new UnsupportedOperationException("GCS objects aren't available locally"); + } + + @Override + public Iterator iterator() { + if (path.isEmpty()) { + return Collections.singleton(this).iterator(); + } else if (path.isRoot()) { + return Collections.emptyIterator(); + } else { + return new PathIterator(); + } + } + + @Override + public int compareTo(Path other) { + // Documented to throw CCE if other is associated with a different FileSystemProvider. + CloudStoragePath that = (CloudStoragePath) other; + int res = bucket().compareTo(that.bucket()); + if (res != 0) { + return res; + } + return toRealPathInternal(false).compareTo(that.toRealPathInternal(false)); + } + + @Override + public boolean equals(@Nullable Object other) { + return this == other + || other instanceof CloudStoragePath + && Objects.equals(bucket(), ((CloudStoragePath) other).bucket()) + && Objects.equals(toRealPathInternal(false), + ((CloudStoragePath) other).toRealPathInternal(false)); + } + + @Override + public int hashCode() { + return Objects.hash(bucket(), toRealPathInternal(false)); + } + + @Override + public String toString() { + return path.toString(); + } + + @Override + public URI toUri() { + try { + return new URI(URI_SCHEME, bucket(), path.toAbsolutePath().toString(), null); + } catch (URISyntaxException e) { + throw new AssertionError(e); + } + } + + @Nullable + private CloudStoragePath newPath(@Nullable UnixPath newPath) { + if (newPath == path) { // Nonuse of equals is intentional. + return this; + } else if (newPath != null) { + return new CloudStoragePath(fileSystem, newPath); + } else { + return null; + } + } + + private UnixPath getUnixPath(String newPath) { + return UnixPath.getPath(fileSystem.config().permitEmptyPathComponents(), newPath); + } + + private UnixPath getWorkingDirectory() { + return getUnixPath(fileSystem.config().workingDirectory()); + } + + /** Transform iterator providing a slight performance boost over {@code FluentIterable}. */ + private final class PathIterator extends UnmodifiableIterator { + private final Iterator delegate = path.split(); + + @Override + public Path next() { + return newPath(getUnixPath(delegate.next())); + } + + @Override + public boolean hasNext() { + return delegate.hasNext(); + } + } +} diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStoragePseudoDirectoryAttributes.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStoragePseudoDirectoryAttributes.java new file mode 100644 index 000000000000..fc381de3244b --- /dev/null +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStoragePseudoDirectoryAttributes.java @@ -0,0 +1,97 @@ +package com.google.gcloud.storage.contrib.nio; + +import static com.google.gcloud.storage.contrib.nio.CloudStorageFileSystem.FILE_TIME_UNKNOWN; + +import com.google.common.base.Optional; +import com.google.common.collect.ImmutableMap; + +import java.nio.file.attribute.FileTime; + +/** Metadata for a cloud storage pseudo-directory. */ +final class CloudStoragePseudoDirectoryAttributes implements CloudStorageFileAttributes { + + static final CloudStoragePseudoDirectoryAttributes SINGLETON_INSTANCE = + new CloudStoragePseudoDirectoryAttributes(); + + @Override + public boolean isDirectory() { + return true; + } + + @Override + public boolean isOther() { + return false; + } + + @Override + public boolean isRegularFile() { + return false; + } + + @Override + public boolean isSymbolicLink() { + return false; + } + + @Override + public Object fileKey() { + return null; + } + + @Override + public long size() { + return 1; // Allow I/O to happen before we fail. + } + + @Override + public FileTime lastModifiedTime() { + return FILE_TIME_UNKNOWN; + } + + @Override + public FileTime creationTime() { + return FILE_TIME_UNKNOWN; + } + + @Override + public FileTime lastAccessTime() { + return FILE_TIME_UNKNOWN; + } + + @Override + public Optional etag() { + return Optional.absent(); + } + + @Override + public Optional mimeType() { + return Optional.absent(); + } + + @Override + public Optional acl() { + return Optional.absent(); + } + + @Override + public Optional cacheControl() { + return Optional.absent(); + } + + @Override + public Optional contentEncoding() { + return Optional.absent(); + } + + @Override + public Optional contentDisposition() { + return Optional.absent(); + } + + @Override + public ImmutableMap userMetadata() { + return ImmutableMap.of(); + } + + private CloudStoragePseudoDirectoryAttributes() {} +} diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStoragePseudoDirectoryException.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStoragePseudoDirectoryException.java new file mode 100644 index 000000000000..d4ebbdad6e20 --- /dev/null +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStoragePseudoDirectoryException.java @@ -0,0 +1,10 @@ +package com.google.gcloud.storage.contrib.nio; + +import java.nio.file.InvalidPathException; + +/** Exception thrown when erroneously trying to operate on a path with a trailing slash. */ +public final class CloudStoragePseudoDirectoryException extends InvalidPathException { + CloudStoragePseudoDirectoryException(CloudStoragePath path) { + super(path.toString(), "Can't perform I/O on pseudo-directories (trailing slash)"); + } +} diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageReadChannel.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageReadChannel.java new file mode 100644 index 000000000000..f4a29dde941c --- /dev/null +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageReadChannel.java @@ -0,0 +1,137 @@ +package com.google.gcloud.storage.contrib.nio; + +import static com.google.common.base.Preconditions.checkArgument; + +import com.google.appengine.tools.cloudstorage.GcsFileMetadata; +import com.google.appengine.tools.cloudstorage.GcsFilename; +import com.google.appengine.tools.cloudstorage.GcsInputChannel; +import com.google.appengine.tools.cloudstorage.GcsService; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.NonWritableChannelException; +import java.nio.channels.SeekableByteChannel; +import java.nio.file.NoSuchFileException; + +import javax.annotation.concurrent.ThreadSafe; + +/** + * Cloud Storage read channel. + * + * @see CloudStorageWriteChannel + */ +@ThreadSafe +final class CloudStorageReadChannel implements SeekableByteChannel { + + static CloudStorageReadChannel create( + GcsService gcsService, GcsFilename file, long position) throws IOException { + // XXX: Reading size and opening file should be atomic. + long size = fetchSize(gcsService, file); + return new CloudStorageReadChannel(gcsService, file, position, size, + gcsService.openReadChannel(file, position)); + } + + private final GcsService gcsService; + private final GcsFilename file; + private long position; + private long size; + private GcsInputChannel channel; + + private CloudStorageReadChannel( + GcsService gcsService, GcsFilename file, long position, long size, GcsInputChannel channel) { + this.gcsService = gcsService; + this.file = file; + this.position = position; + this.size = size; + this.channel = channel; + } + + @Override + public boolean isOpen() { + synchronized (this) { + return channel.isOpen(); + } + } + + @Override + public void close() throws IOException { + synchronized (this) { + channel.close(); + } + } + + @Override + public int read(ByteBuffer dst) throws IOException { + synchronized (this) { + checkOpen(); + int amt = channel.read(dst); + if (amt > 0) { + position += amt; + // XXX: This would only ever happen if the fetchSize() race-condition occurred. + if (position > size) { + size = position; + } + } + return amt; + } + } + + @Override + public long size() throws IOException { + synchronized (this) { + checkOpen(); + return size; + } + } + + @Override + public long position() throws IOException { + synchronized (this) { + checkOpen(); + return position; + } + } + + @Override + public SeekableByteChannel position(long newPosition) throws IOException { + checkArgument(newPosition >= 0); + synchronized (this) { + checkOpen(); + if (newPosition == position) { + return this; + } + position = newPosition; + size = fetchSize(gcsService, file); + channel.close(); + channel = gcsService.openReadChannel(file, position); + return this; + } + } + + @Override + public int write(ByteBuffer src) throws IOException { + throw new NonWritableChannelException(); + } + + @Override + public SeekableByteChannel truncate(long size) throws IOException { + throw new NonWritableChannelException(); + } + + private void checkOpen() throws ClosedChannelException { + if (!channel.isOpen()) { + throw new ClosedChannelException(); + } + } + + private static long fetchSize(GcsService gcsService, GcsFilename file) throws IOException, + NoSuchFileException { + GcsFileMetadata metadata = gcsService.getMetadata(file); + if (metadata == null) { + throw new NoSuchFileException( + String.format("gs://%s/%s", file.getBucketName(), file.getObjectName())); + } + return metadata.getLength(); + } +} diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageUtil.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageUtil.java new file mode 100644 index 000000000000..79f46af172ee --- /dev/null +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageUtil.java @@ -0,0 +1,110 @@ +package com.google.gcloud.storage.contrib.nio; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Strings.isNullOrEmpty; + +import com.google.appengine.tools.cloudstorage.GcsFileOptions; + +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.file.Path; +import java.nio.file.ProviderMismatchException; +import java.util.Map; +import java.util.regex.Pattern; + +final class CloudStorageUtil { + + private static final Pattern BUCKET_PATTERN = Pattern.compile("[a-z0-9][-._a-z0-9.]+[a-z0-9]"); + + static void checkBucket(String bucket) { + // TODO(b/18998200): The true check is actually more complicated. Consider implementing it. + checkArgument(BUCKET_PATTERN.matcher(bucket).matches(), "" + + "Invalid bucket name: '" + bucket + "'. " + + "GCS bucket names must contain only lowercase letters, numbers, dashes (-), " + + "underscores (_), and dots (.). Bucket names must start and end with a number or letter. " + + "See https://developers.google.com/storage/docs/bucketnaming for more details."); + } + + static CloudStoragePath checkPath(Path path) { + if (!(checkNotNull(path) instanceof CloudStoragePath)) { + throw new ProviderMismatchException(String.format( + "Not a cloud storage path: %s (%s)", path, path.getClass().getSimpleName())); + } + return (CloudStoragePath) path; + } + + static GcsFileOptions.Builder copyFileOptions(GcsFileOptions options) { + GcsFileOptions.Builder builder = new GcsFileOptions.Builder(); + if (!isNullOrEmpty(options.getAcl())) { + builder.acl(options.getAcl()); + } + if (!isNullOrEmpty(options.getCacheControl())) { + builder.cacheControl(options.getCacheControl()); + } + if (!isNullOrEmpty(options.getContentDisposition())) { + builder.contentDisposition(options.getContentDisposition()); + } + if (!isNullOrEmpty(options.getContentEncoding())) { + builder.contentEncoding(options.getContentEncoding()); + } + if (!isNullOrEmpty(options.getMimeType())) { + builder.mimeType(options.getMimeType()); + } + for (Map.Entry entry : options.getUserMetadata().entrySet()) { + builder.addUserMetadata(entry.getKey(), entry.getValue()); + } + return builder; + } + + @SafeVarargs + static GcsFileOptions buildFileOptions(GcsFileOptions.Builder builder, T... options) { + for (Object option : options) { + if (option instanceof OptionAcl) { + builder.acl(((OptionAcl) option).acl()); + } else if (option instanceof OptionCacheControl) { + builder.cacheControl(((OptionCacheControl) option).cacheControl()); + } else if (option instanceof OptionContentDisposition) { + builder.contentDisposition(((OptionContentDisposition) option).contentDisposition()); + } else if (option instanceof OptionContentEncoding) { + builder.contentEncoding(((OptionContentEncoding) option).contentEncoding()); + } else if (option instanceof OptionMimeType) { + builder.mimeType(((OptionMimeType) option).mimeType()); + } else if (option instanceof OptionUserMetadata) { + OptionUserMetadata metadata = (OptionUserMetadata) option; + builder.addUserMetadata(metadata.key(), metadata.value()); + } + } + return builder.build(); + } + + static URI stripPathFromUri(URI uri) { + try { + return new URI( + uri.getScheme(), + uri.getUserInfo(), + uri.getHost(), + uri.getPort(), + null, + uri.getQuery(), + uri.getFragment()); + } catch (URISyntaxException e) { + throw new IllegalArgumentException(e.getMessage()); + } + } + + /** Makes NullPointerTester happy. */ + @SafeVarargs + static void checkNotNullArray(T... values) { + for (T value : values) { + checkNotNull(value); + } + } + + static boolean getPropertyBoolean(String property, boolean defaultValue) { + String value = System.getProperty(property); + return value != null ? Boolean.valueOf(value) : defaultValue; + } + + private CloudStorageUtil() {} +} diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageWriteChannel.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageWriteChannel.java new file mode 100644 index 000000000000..2b5a004e1bc4 --- /dev/null +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageWriteChannel.java @@ -0,0 +1,96 @@ +package com.google.gcloud.storage.contrib.nio; + +import com.google.appengine.tools.cloudstorage.GcsOutputChannel; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.NonReadableChannelException; +import java.nio.channels.SeekableByteChannel; + +import javax.annotation.concurrent.ThreadSafe; + +/** + * Cloud Storage write channel. + * + *

This class does not support seeking, reading, or append. + * + * @see CloudStorageReadChannel + */ +@ThreadSafe +final class CloudStorageWriteChannel implements SeekableByteChannel { + + private GcsOutputChannel channel; + private long position; + private long size; + + CloudStorageWriteChannel(GcsOutputChannel channel) { + this.channel = channel; + } + + @Override + public boolean isOpen() { + synchronized (this) { + return channel.isOpen(); + } + } + + @Override + public void close() throws IOException { + synchronized (this) { + channel.close(); + } + } + + @Override + public int read(ByteBuffer dst) throws IOException { + throw new NonReadableChannelException(); + } + + @Override + public int write(ByteBuffer src) throws IOException { + synchronized (this) { + checkOpen(); + int amt = channel.write(src); + if (amt > 0) { + position += amt; + size += amt; + } + return amt; + } + } + + @Override + public long position() throws IOException { + synchronized (this) { + checkOpen(); + return position; + } + } + + @Override + public SeekableByteChannel position(long newPosition) throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public long size() throws IOException { + synchronized (this) { + checkOpen(); + return size; + } + } + + @Override + public SeekableByteChannel truncate(long newSize) throws IOException { + // TODO(b/18997913): Emulate this functionality by closing and rewriting old file up to newSize. + // Or maybe just swap out GcsService for the Apiary client. + throw new UnsupportedOperationException(); + } + + private void checkOpen() throws ClosedChannelException { + if (!channel.isOpen()) { + throw new ClosedChannelException(); + } + } +} diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/OptionAcl.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/OptionAcl.java new file mode 100644 index 000000000000..c0068ee1c61d --- /dev/null +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/OptionAcl.java @@ -0,0 +1,13 @@ +package com.google.gcloud.storage.contrib.nio; + +import com.google.auto.value.AutoValue; + +@AutoValue +abstract class OptionAcl implements CloudStorageOption.OpenCopy { + + static OptionAcl create(String acl) { + return new AutoValue_OptionAcl(acl); + } + + abstract String acl(); +} diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/OptionBlockSize.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/OptionBlockSize.java new file mode 100644 index 000000000000..f33f601fad61 --- /dev/null +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/OptionBlockSize.java @@ -0,0 +1,13 @@ +package com.google.gcloud.storage.contrib.nio; + +import com.google.auto.value.AutoValue; + +@AutoValue +abstract class OptionBlockSize implements CloudStorageOption.OpenCopy { + + static OptionBlockSize create(int size) { + return new AutoValue_OptionBlockSize(size); + } + + abstract int size(); +} diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/OptionCacheControl.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/OptionCacheControl.java new file mode 100644 index 000000000000..c33d4394fa24 --- /dev/null +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/OptionCacheControl.java @@ -0,0 +1,13 @@ +package com.google.gcloud.storage.contrib.nio; + +import com.google.auto.value.AutoValue; + +@AutoValue +abstract class OptionCacheControl implements CloudStorageOption.OpenCopy { + + static OptionCacheControl create(String cacheControl) { + return new AutoValue_OptionCacheControl(cacheControl); + } + + abstract String cacheControl(); +} diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/OptionContentDisposition.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/OptionContentDisposition.java new file mode 100644 index 000000000000..7bebefcba19c --- /dev/null +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/OptionContentDisposition.java @@ -0,0 +1,13 @@ +package com.google.gcloud.storage.contrib.nio; + +import com.google.auto.value.AutoValue; + +@AutoValue +abstract class OptionContentDisposition implements CloudStorageOption.OpenCopy { + + static OptionContentDisposition create(String contentDisposition) { + return new AutoValue_OptionContentDisposition(contentDisposition); + } + + abstract String contentDisposition(); +} diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/OptionContentEncoding.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/OptionContentEncoding.java new file mode 100644 index 000000000000..879d05983f37 --- /dev/null +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/OptionContentEncoding.java @@ -0,0 +1,13 @@ +package com.google.gcloud.storage.contrib.nio; + +import com.google.auto.value.AutoValue; + +@AutoValue +abstract class OptionContentEncoding implements CloudStorageOption.OpenCopy { + + static OptionContentEncoding create(String contentEncoding) { + return new AutoValue_OptionContentEncoding(contentEncoding); + } + + abstract String contentEncoding(); +} diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/OptionMimeType.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/OptionMimeType.java new file mode 100644 index 000000000000..feb1b0c6dd1b --- /dev/null +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/OptionMimeType.java @@ -0,0 +1,13 @@ +package com.google.gcloud.storage.contrib.nio; + +import com.google.auto.value.AutoValue; + +@AutoValue +abstract class OptionMimeType implements CloudStorageOption.OpenCopy { + + static OptionMimeType create(String mimeType) { + return new AutoValue_OptionMimeType(mimeType); + } + + abstract String mimeType(); +} diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/OptionUserMetadata.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/OptionUserMetadata.java new file mode 100644 index 000000000000..9851d2ad5c4f --- /dev/null +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/OptionUserMetadata.java @@ -0,0 +1,14 @@ +package com.google.gcloud.storage.contrib.nio; + +import com.google.auto.value.AutoValue; + +@AutoValue +abstract class OptionUserMetadata implements CloudStorageOption.OpenCopy { + + static OptionUserMetadata create(String key, String value) { + return new AutoValue_OptionUserMetadata(key, value); + } + + abstract String key(); + abstract String value(); +} diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/UnixPath.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/UnixPath.java new file mode 100644 index 000000000000..7b80a6299be6 --- /dev/null +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/UnixPath.java @@ -0,0 +1,494 @@ +package com.google.gcloud.storage.contrib.nio; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.base.Joiner; +import com.google.common.base.Splitter; +import com.google.common.collect.Iterators; +import com.google.common.collect.Lists; +import com.google.common.collect.Ordering; +import com.google.common.collect.PeekingIterator; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; + +/** + * Unix file system path. + * + *

This class is helpful for writing {@link java.nio.file.Path Path} implementations. + * + *

This implementation behaves almost identically to {@code sun.nio.fs.UnixPath}. The only + * difference is that some methods (like {@link #relativize(UnixPath)} go to greater lengths to + * preserve trailing backslashes, in order to ensure the path will continue to be recognized as a + * directory. + * + *

NOTE: This code might not play nice with + * Supplementary + * Characters as Surrogates. + */ +@Immutable +final class UnixPath implements CharSequence { + + public static final char DOT = '.'; + public static final char SEPARATOR = '/'; + public static final String ROOT = "" + SEPARATOR; + public static final String CURRENT_DIR = "" + DOT; + public static final String PARENT_DIR = "" + DOT + DOT; + public static final UnixPath EMPTY_PATH = new UnixPath(false, ""); + public static final UnixPath ROOT_PATH = new UnixPath(false, ROOT); + + private static final Splitter SPLITTER = Splitter.on(SEPARATOR).omitEmptyStrings(); + private static final Splitter SPLITTER_PERMIT_EMPTY_COMPONENTS = Splitter.on(SEPARATOR); + private static final Joiner JOINER = Joiner.on(SEPARATOR); + private static final Ordering> ORDERING = Ordering.natural().lexicographical(); + + private final String path; + private List lazyStringParts; + private final boolean permitEmptyComponents; + + private UnixPath(boolean permitEmptyComponents, String path) { + this.path = checkNotNull(path); + this.permitEmptyComponents = permitEmptyComponents; + } + + /** Returns new UnixPath of {@code first}. */ + public static UnixPath getPath(boolean permitEmptyComponents, String path) { + if (path.isEmpty()) { + return EMPTY_PATH; + } else if (isRootInternal(path)) { + return ROOT_PATH; + } else { + return new UnixPath(permitEmptyComponents, path); + } + } + + /** + * Returns new UnixPath of {@code first} with {@code more} components resolved against it. + * + * @see #resolve(UnixPath) + * @see java.nio.file.FileSystem#getPath(String, String...) + */ + public static UnixPath getPath(boolean permitEmptyComponents, String first, String... more) { + if (more.length == 0) { + return getPath(permitEmptyComponents, first); + } + StringBuilder builder = new StringBuilder(first); + for (int i = 0; i < more.length; i++) { + String part = more[i]; + if (part.isEmpty()) { + continue; + } else if (isAbsoluteInternal(part)) { + if (i == more.length - 1) { + return new UnixPath(permitEmptyComponents, part); + } else { + builder.replace(0, builder.length(), part); + } + } else if (hasTrailingSeparatorInternal(builder)) { + builder.append(part); + } else { + builder.append(SEPARATOR); + builder.append(part); + } + } + return new UnixPath(permitEmptyComponents, builder.toString()); + } + + /** Returns {@code true} consists only of {@code separator}. */ + public boolean isRoot() { + return isRootInternal(path); + } + + private static boolean isRootInternal(String path) { + return path.length() == 1 && path.charAt(0) == SEPARATOR; + } + + /** Returns {@code true} if path starts with {@code separator}. */ + public boolean isAbsolute() { + return isAbsoluteInternal(path); + } + + private static boolean isAbsoluteInternal(String path) { + return !path.isEmpty() && path.charAt(0) == SEPARATOR; + } + + /** Returns {@code true} if path ends with {@code separator}. */ + public boolean hasTrailingSeparator() { + return hasTrailingSeparatorInternal(path); + } + + private static boolean hasTrailingSeparatorInternal(CharSequence path) { + return path.length() != 0 && path.charAt(path.length() - 1) == SEPARATOR; + } + + /** Returns {@code true} if path ends with a trailing slash, or would after normalization. */ + public boolean seemsLikeADirectory() { + int length = path.length(); + return path.isEmpty() + || path.charAt(length - 1) == SEPARATOR + || path.endsWith(".") && (length == 1 || path.charAt(length - 2) == SEPARATOR) + || path.endsWith("..") && (length == 2 || path.charAt(length - 3) == SEPARATOR); + } + + /** + * Returns last component in {@code path}. + * + * @see java.nio.file.Path#getFileName() + */ + @Nullable + public UnixPath getFileName() { + if (path.isEmpty()) { + return EMPTY_PATH; + } else if (isRoot()) { + return null; + } else { + List parts = getParts(); + String last = parts.get(parts.size() - 1); + return parts.size() == 1 && path.equals(last) + ? this : new UnixPath(permitEmptyComponents, last); + } + } + + /** + * Returns parent directory (including trailing separator) or {@code null} if no parent remains. + * + * @see java.nio.file.Path#getParent() + */ + @Nullable + public UnixPath getParent() { + if (path.isEmpty() || isRoot()) { + return null; + } + int index = hasTrailingSeparator() + ? path.lastIndexOf(SEPARATOR, path.length() - 2) + : path.lastIndexOf(SEPARATOR); + if (index == -1) { + return isAbsolute() ? ROOT_PATH : null; + } else { + return new UnixPath(permitEmptyComponents, path.substring(0, index + 1)); + } + } + + /** + * Returns root component if an absolute path, otherwise {@code null}. + * + * @see java.nio.file.Path#getRoot() + */ + @Nullable + public UnixPath getRoot() { + return isAbsolute() ? ROOT_PATH : null; + } + + /** + * Returns specified range of sub-components in path joined together. + * + * @see java.nio.file.Path#subpath(int, int) + */ + public UnixPath subpath(int beginIndex, int endIndex) { + if (path.isEmpty() && beginIndex == 0 && endIndex == 1) { + return this; + } + checkArgument(beginIndex >= 0 && endIndex > beginIndex); + List subList; + try { + subList = getParts().subList(beginIndex, endIndex); + } catch (IndexOutOfBoundsException e) { + throw new IllegalArgumentException(); + } + return new UnixPath(permitEmptyComponents, JOINER.join(subList)); + } + + /** + * Returns number of components in {@code path}. + * + * @see java.nio.file.Path#getNameCount() + */ + public int getNameCount() { + if (path.isEmpty()) { + return 1; + } else if (isRoot()) { + return 0; + } else { + return getParts().size(); + } + } + + /** + * Returns component in {@code path} at {@code index}. + * + * @see java.nio.file.Path#getName(int) + */ + public UnixPath getName(int index) { + if (path.isEmpty()) { + return this; + } + try { + return new UnixPath(permitEmptyComponents, getParts().get(index)); + } catch (IndexOutOfBoundsException e) { + throw new IllegalArgumentException(); + } + } + + /** + * Returns path without extra separators or {@code .} and {@code ..}, preserving trailing slash. + * + * @see java.nio.file.Path#normalize() + */ + public UnixPath normalize() { + List parts = new ArrayList<>(); + boolean mutated = false; + int resultLength = 0; + int mark = 0; + int index; + do { + index = path.indexOf(SEPARATOR, mark); + String part = path.substring(mark, index == -1 ? path.length() : index + 1); + switch (part) { + case CURRENT_DIR: + case CURRENT_DIR + SEPARATOR: + mutated = true; + break; + case PARENT_DIR: + case PARENT_DIR + SEPARATOR: + mutated = true; + if (!parts.isEmpty()) { + resultLength -= parts.remove(parts.size() - 1).length(); + } + break; + default: + if (index != mark || index == 0) { + parts.add(part); + resultLength = part.length(); + } else { + mutated = true; + } + } + mark = index + 1; + } while (index != -1); + if (!mutated) { + return this; + } + StringBuilder result = new StringBuilder(resultLength); + for (String part : parts) { + result.append(part); + } + return new UnixPath(permitEmptyComponents, result.toString()); + } + + /** + * Returns {@code other} appended to {@code path}. + * + * @see java.nio.file.Path#resolve(java.nio.file.Path) + */ + public UnixPath resolve(UnixPath other) { + if (other.path.isEmpty()) { + return this; + } else if (other.isAbsolute()) { + return other; + } else if (hasTrailingSeparator()) { + return new UnixPath(permitEmptyComponents, path + other.path); + } else { + return new UnixPath(permitEmptyComponents, path + SEPARATOR + other.path); + } + } + + /** + * Returns {@code other} resolved against parent of {@code path}. + * + * @see java.nio.file.Path#resolveSibling(java.nio.file.Path) + */ + public UnixPath resolveSibling(UnixPath other) { + checkNotNull(other); + UnixPath parent = getParent(); + return parent == null ? other : parent.resolve(other); + } + + /** + * Returns {@code other} made relative to {@code path}. + * + * @see java.nio.file.Path#relativize(java.nio.file.Path) + */ + public UnixPath relativize(UnixPath other) { + checkArgument(isAbsolute() == other.isAbsolute(), "'other' is different type of Path"); + if (path.isEmpty()) { + return other; + } + PeekingIterator left = Iterators.peekingIterator(split()); + PeekingIterator right = Iterators.peekingIterator(other.split()); + while (left.hasNext() && right.hasNext()) { + if (!left.peek().equals(right.peek())) { + break; + } + left.next(); + right.next(); + } + StringBuilder result = new StringBuilder(path.length() + other.path.length()); + while (left.hasNext()) { + result.append(PARENT_DIR); + result.append(SEPARATOR); + left.next(); + } + while (right.hasNext()) { + result.append(right.next()); + result.append(SEPARATOR); + } + if (result.length() > 0 && !other.hasTrailingSeparator()) { + result.deleteCharAt(result.length() - 1); + } + return new UnixPath(permitEmptyComponents, result.toString()); + } + + /** + * Returns {@code true} if {@code path} starts with {@code other}. + * + * @see java.nio.file.Path#startsWith(java.nio.file.Path) + */ + public boolean startsWith(UnixPath other) { + UnixPath me = removeTrailingSeparator(); + other = other.removeTrailingSeparator(); + if (other.path.length() > me.path.length()) { + return false; + } else if (me.isAbsolute() != other.isAbsolute()) { + return false; + } else if (!me.path.isEmpty() && other.path.isEmpty()) { + return false; + } + return startsWith(split(), other.split()); + } + + private static boolean startsWith(Iterator lefts, Iterator rights) { + while (rights.hasNext()) { + if (!lefts.hasNext() || !rights.next().equals(lefts.next())) { + return false; + } + } + return true; + } + + /** + * Returns {@code true} if {@code path} ends with {@code other}. + * + * @see java.nio.file.Path#endsWith(java.nio.file.Path) + */ + public boolean endsWith(UnixPath other) { + UnixPath me = removeTrailingSeparator(); + other = other.removeTrailingSeparator(); + if (other.path.length() > me.path.length()) { + return false; + } else if (!me.path.isEmpty() && other.path.isEmpty()) { + return false; + } else if (other.isAbsolute()) { + return me.isAbsolute() && me.path.equals(other.path); + } + return startsWith(me.splitReverse(), other.splitReverse()); + } + + /** + * Compares two paths lexicographically for ordering. + * + * @see java.nio.file.Path#compareTo(java.nio.file.Path) + */ + public int compareTo(UnixPath other) { + return ORDERING.compare(getParts(), other.getParts()); + } + + /** Converts relative path to an absolute path. */ + public UnixPath toAbsolutePath(UnixPath currentWorkingDirectory) { + checkArgument(currentWorkingDirectory.isAbsolute()); + return isAbsolute() ? this : currentWorkingDirectory.resolve(this); + } + + /** Returns {@code toAbsolutePath(ROOT_PATH)} */ + public UnixPath toAbsolutePath() { + return toAbsolutePath(ROOT_PATH); + } + + /** Removes beginning separator from path, if an absolute path. */ + public UnixPath removeBeginningSeparator() { + return isAbsolute() ? new UnixPath(permitEmptyComponents, path.substring(1)) : this; + } + + /** Adds trailing separator to path, if it isn't present. */ + public UnixPath addTrailingSeparator() { + return hasTrailingSeparator() ? this : new UnixPath(permitEmptyComponents, path + SEPARATOR); + } + + /** Removes trailing separator from path, unless it's root. */ + public UnixPath removeTrailingSeparator() { + if (!isRoot() && hasTrailingSeparator()) { + return new UnixPath(permitEmptyComponents, path.substring(0, path.length() - 1)); + } else { + return this; + } + } + + /** Splits path into components, excluding separators and empty strings. */ + public Iterator split() { + return getParts().iterator(); + } + + /** Splits path into components in reverse, excluding separators and empty strings. */ + public Iterator splitReverse() { + return Lists.reverse(getParts()).iterator(); + } + + @Override + public boolean equals(@Nullable Object other) { + return this == other + || other instanceof UnixPath + && path.equals(((UnixPath) other).path); + } + + @Override + public int hashCode() { + return path.hashCode(); + } + + /** Returns path as a string. */ + @Override + public String toString() { + return path; + } + + @Override + public int length() { + return path.length(); + } + + @Override + public char charAt(int index) { + return path.charAt(index); + } + + @Override + public CharSequence subSequence(int start, int end) { + return path.subSequence(start, end); + } + + /** Returns {@code true} if this path is an empty string. */ + public boolean isEmpty() { + return path.isEmpty(); + } + + /** Returns list of path components, excluding slashes. */ + private List getParts() { + List result = lazyStringParts; + return result != null + ? result : (lazyStringParts = path.isEmpty() || isRoot() + ? Collections.emptyList() : createParts()); + } + + private List createParts() { + if (permitEmptyComponents) { + return SPLITTER_PERMIT_EMPTY_COMPONENTS + .splitToList(path.charAt(0) == SEPARATOR ? path.substring(1) : path); + } else { + return SPLITTER.splitToList(path); + } + } +} diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/package-info.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/package-info.java new file mode 100644 index 000000000000..e07c960b5512 --- /dev/null +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/package-info.java @@ -0,0 +1,82 @@ +/** + * Java 7 nio FileSystem client library for Google Cloud Storage. + * + *

This client library allows you to easily interact with Google Cloud Storage, using Java's + * standard file system API, introduced in Java 7. + * + *

How It Works

+ * + * The simplest way to get started is with {@code Paths} and {@code Files}:
   {@code
+ *
+ *   Path path = Paths.get(URI.create("gs://bucket/lolcat.csv"));
+ *   List lines = Files.readAllLines(path, StandardCharsets.UTF_8);}
+ * + *

If you want to configure the bucket per-environment, it might make more sense to use the + * {@code FileSystem} API: + *

+ *   class Foo {
+ *     static String bucket = System.getProperty(...);
+ *     static FileSystem fs = FileSystems.getFileSystem(URI.create("gs://" + bucket));
+ *     void bar() {
+ *       byte[] data = "hello kitty".getBytes(StandardCharsets.UTF_8);
+ *       Path path = fs.getPath("/object");
+ *       Files.write(path, data);
+ *       data = Files.readBytes(path);
+ *     }
+ *     void doodle() {
+ *       Path path = fs.getPath("/path/to/doodle.csv");
+ *       List<String> lines = Files.readAllLines(path, StandardCharsets.UTF_8);
+ *     }
+ *   }
+ * + *

You can also use InputStream and OutputStream for streaming: + *

+ *   Path path = Paths.get(URI.create("gs://bucket/lolcat.csv"));
+ *   try (InputStream input = Files.openInputStream(path)) {
+ *     // ...
+ *   }
+ * + *

You can set various attributes using + * {@link com.google.gcloud.storage.contrib.nio.CloudStorageOptions CloudStorageOptions} static + * helpers: + *

+ *   Files.write(csvPath, csvLines, StandardCharsets.UTF_8,
+ *       withMimeType(MediaType.CSV_UTF8),
+ *       withoutCaching());
+ * + *

NOTE: Cloud Storage uses a flat namespace and therefore doesn't support real + * directories. So this library supports what's known as "pseudo-directories". Any path that + * includes a trailing slash, will be considered a directory. It will always be assumed to exist, + * without performing any I/O. This allows you to do path manipulation in the same manner as you + * would with the normal UNIX file system implementation. You can disable this feature with + * {@link com.google.gcloud.storage.contrib.nio.CloudStorageConfiguration#usePseudoDirectories()}. + * + *

Unit Testing

+ * + *

Here's a simple unit test:

+ *
+ *   class MyTest {
+ *     {@literal @}Rule
+ *     public final AppEngineRule appEngine = AppEngineRule.builder().build();
+ *
+ *     {@literal @}Test
+ *     public test_fileWrite() throws Exception {
+ *       Path path = Paths.get(URI.create("gs://bucket/traditional"));
+ *       Files.write(path, "eyebrow".getBytes(StandardCharsets.US_ASCII));
+ *       assertEquals("eyebrow", new String(Files.readBytes(path), StandardCharsets.US_ASCII));
+ *     }
+ *   }
+ * + *

Non-SPI Interface

+ * + *

If you don't want to rely on Java SPI, which requires a META-INF file in your jar generated by + * Google Auto, you can instantiate this file system directly as follows:

   {@code
+ *
+ *   CloudStorageFileSystem fs = CloudStorageFileSystemProvider.forBucket("bucket");
+ *   byte[] data = "hello kitty".getBytes(StandardCharsets.UTF_8);
+ *   Path path = fs.getPath("/object");
+ *   Files.write(path, data);
+ *   data = Files.readBytes(path);}
+ */ +@javax.annotation.ParametersAreNonnullByDefault +package com.google.gcloud.storage.contrib.nio; diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/AppEngineRule.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/AppEngineRule.java new file mode 100644 index 000000000000..090ffa04ef77 --- /dev/null +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/AppEngineRule.java @@ -0,0 +1,25 @@ +package com.google.gcloud.storage.contrib.nio; + +import com.google.appengine.tools.development.testing.LocalDatastoreServiceTestConfig; +import com.google.appengine.tools.development.testing.LocalServiceTestHelper; + +import org.junit.rules.ExternalResource; + +import java.io.IOException; + +/** JUnit rule for App Engine testing environment. */ +public final class AppEngineRule extends ExternalResource { + + private LocalServiceTestHelper helper; + + @Override + protected void before() throws IOException { + helper = new LocalServiceTestHelper(new LocalDatastoreServiceTestConfig()); + helper.setUp(); + } + + @Override + protected void after() { + helper.tearDown(); + } +} diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageConfigurationTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageConfigurationTest.java new file mode 100644 index 000000000000..9bb987e86707 --- /dev/null +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageConfigurationTest.java @@ -0,0 +1,58 @@ +package com.google.gcloud.storage.contrib.nio; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.common.collect.ImmutableMap; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Unit tests for {@link CloudStorageConfiguration}. */ +@RunWith(JUnit4.class) +public class CloudStorageConfigurationTest { + + @Rule + public final ExpectedException thrown = ExpectedException.none(); + + @Test + public void testBuilder() { + CloudStorageConfiguration config = CloudStorageConfiguration.builder() + .workingDirectory("/omg") + .permitEmptyPathComponents(true) + .stripPrefixSlash(false) + .usePseudoDirectories(false) + .blockSize(666) + .build(); + assertThat(config.workingDirectory()).isEqualTo("/omg"); + assertThat(config.permitEmptyPathComponents()).isTrue(); + assertThat(config.stripPrefixSlash()).isFalse(); + assertThat(config.usePseudoDirectories()).isFalse(); + assertThat(config.blockSize()).isEqualTo(666); + } + + @Test + public void testFromMap() { + CloudStorageConfiguration config = CloudStorageConfiguration.fromMap( + new ImmutableMap.Builder() + .put("workingDirectory", "/omg") + .put("permitEmptyPathComponents", true) + .put("stripPrefixSlash", false) + .put("usePseudoDirectories", false) + .put("blockSize", 666) + .build()); + assertThat(config.workingDirectory()).isEqualTo("/omg"); + assertThat(config.permitEmptyPathComponents()).isTrue(); + assertThat(config.stripPrefixSlash()).isFalse(); + assertThat(config.usePseudoDirectories()).isFalse(); + assertThat(config.blockSize()).isEqualTo(666); + } + + @Test + public void testFromMap_badKey_throwsIae() { + thrown.expect(IllegalArgumentException.class); + CloudStorageConfiguration.fromMap(ImmutableMap.of("lol", "/omg")); + } +} diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileAttributeViewTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileAttributeViewTest.java new file mode 100644 index 000000000000..c376d2456dfc --- /dev/null +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileAttributeViewTest.java @@ -0,0 +1,100 @@ +package com.google.gcloud.storage.contrib.nio; + +import static com.google.common.truth.Truth.assertThat; +import static com.google.gcloud.storage.contrib.nio.CloudStorageOptions.withCacheControl; +import static java.nio.charset.StandardCharsets.UTF_8; + +import com.google.common.testing.EqualsTester; +import com.google.common.testing.NullPointerTester; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.attribute.FileTime; + +/** Unit tests for {@link CloudStorageFileAttributeView}. */ +@RunWith(JUnit4.class) +public class CloudStorageFileAttributeViewTest { + + private static final byte[] HAPPY = "(✿◕ ‿◕ )ノ".getBytes(UTF_8); + + @Rule + public final ExpectedException thrown = ExpectedException.none(); + + @Rule + public final AppEngineRule appEngineRule = new AppEngineRule(); + + private Path path; + + @Before + public void before() throws Exception { + path = Paths.get(URI.create("gs://red/water")); + } + + @Test + public void testReadAttributes() throws Exception { + Files.write(path, HAPPY, withCacheControl("potato")); + CloudStorageFileAttributeView lazyAttributes = + Files.getFileAttributeView(path, CloudStorageFileAttributeView.class); + assertThat(lazyAttributes.readAttributes().cacheControl().get()).isEqualTo("potato"); + } + + @Test + public void testReadAttributes_notFound_throwsNoSuchFileException() throws Exception { + CloudStorageFileAttributeView lazyAttributes = + Files.getFileAttributeView(path, CloudStorageFileAttributeView.class); + thrown.expect(NoSuchFileException.class); + lazyAttributes.readAttributes(); + } + + @Test + public void testReadAttributes_pseudoDirectory() throws Exception { + Path dir = Paths.get(URI.create("gs://red/rum/")); + CloudStorageFileAttributeView lazyAttributes = + Files.getFileAttributeView(dir, CloudStorageFileAttributeView.class); + assertThat(lazyAttributes.readAttributes()) + .isInstanceOf(CloudStoragePseudoDirectoryAttributes.class); + } + + @Test + public void testName() throws Exception { + Files.write(path, HAPPY, withCacheControl("potato")); + CloudStorageFileAttributeView lazyAttributes = + Files.getFileAttributeView(path, CloudStorageFileAttributeView.class); + assertThat(lazyAttributes.name()).isEqualTo("gcs"); + } + + @Test + public void testEquals_equalsTester() throws Exception { + new EqualsTester() + .addEqualityGroup( + Files.getFileAttributeView( + Paths.get(URI.create("gs://red/rum")), + CloudStorageFileAttributeView.class), + Files.getFileAttributeView( + Paths.get(URI.create("gs://red/rum")), + CloudStorageFileAttributeView.class)) + .addEqualityGroup( + Files.getFileAttributeView( + Paths.get(URI.create("gs://red/lol/dog")), + CloudStorageFileAttributeView.class)) + .testEquals(); + } + + @Test + public void testNullness() throws Exception { + new NullPointerTester() + .setDefault(FileTime.class, FileTime.fromMillis(0)) + .testAllPublicInstanceMethods( + Files.getFileAttributeView(path, CloudStorageFileAttributeView.class)); + } +} diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileAttributesTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileAttributesTest.java new file mode 100644 index 000000000000..856a5dcb2c8c --- /dev/null +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileAttributesTest.java @@ -0,0 +1,143 @@ +package com.google.gcloud.storage.contrib.nio; + +import static com.google.common.truth.Truth.assertThat; +import static com.google.gcloud.storage.contrib.nio.CloudStorageOptions.withAcl; +import static com.google.gcloud.storage.contrib.nio.CloudStorageOptions.withCacheControl; +import static com.google.gcloud.storage.contrib.nio.CloudStorageOptions.withContentDisposition; +import static com.google.gcloud.storage.contrib.nio.CloudStorageOptions.withContentEncoding; +import static com.google.gcloud.storage.contrib.nio.CloudStorageOptions.withMimeType; +import static com.google.gcloud.storage.contrib.nio.CloudStorageOptions.withUserMetadata; +import static java.nio.charset.StandardCharsets.UTF_8; + +import com.google.common.testing.EqualsTester; +import com.google.common.testing.NullPointerTester; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +/** Unit tests for {@link CloudStorageFileAttributes}. */ +@RunWith(JUnit4.class) +public class CloudStorageFileAttributesTest { + + private static final byte[] HAPPY = "(✿◕ ‿◕ )ノ".getBytes(UTF_8); + + @Rule + public final AppEngineRule appEngineRule = new AppEngineRule(); + + private Path path; + private Path dir; + + @Before + public void before() throws Exception { + path = Paths.get(URI.create("gs://bucket/randompath")); + dir = Paths.get(URI.create("gs://bucket/randompath/")); + } + + @Test + public void testCacheControl() throws Exception { + Files.write(path, HAPPY, withCacheControl("potato")); + assertThat(Files.readAttributes(path, CloudStorageFileAttributes.class).cacheControl().get()) + .isEqualTo("potato"); + } + + @Test + public void testMimeType() throws Exception { + Files.write(path, HAPPY, withMimeType("text/potato")); + assertThat(Files.readAttributes(path, CloudStorageFileAttributes.class).mimeType().get()) + .isEqualTo("text/potato"); + } + + @Test + public void testAcl() throws Exception { + Files.write(path, HAPPY, withAcl("potato")); + assertThat(Files.readAttributes(path, CloudStorageFileAttributes.class).acl().get()) + .isEqualTo("potato"); + } + + @Test + public void testContentDisposition() throws Exception { + Files.write(path, HAPPY, withContentDisposition("crash call")); + assertThat( + Files.readAttributes(path, CloudStorageFileAttributes.class).contentDisposition().get()) + .isEqualTo("crash call"); + } + + @Test + public void testContentEncoding() throws Exception { + Files.write(path, HAPPY, withContentEncoding("my content encoding")); + assertThat(Files.readAttributes(path, CloudStorageFileAttributes.class).contentEncoding().get()) + .isEqualTo("my content encoding"); + } + + @Test + public void testUserMetadata() throws Exception { + Files.write(path, HAPPY, withUserMetadata("green", "bean")); + assertThat( + Files.readAttributes(path, CloudStorageFileAttributes.class).userMetadata().get("green")) + .isEqualTo("bean"); + } + + @Test + public void testIsDirectory() throws Exception { + Files.write(path, HAPPY); + assertThat(Files.readAttributes(path, CloudStorageFileAttributes.class).isDirectory()) + .isFalse(); + assertThat(Files.readAttributes(dir, CloudStorageFileAttributes.class).isDirectory()).isTrue(); + } + + @Test + public void testIsRegularFile() throws Exception { + Files.write(path, HAPPY); + assertThat(Files.readAttributes(path, CloudStorageFileAttributes.class).isRegularFile()) + .isTrue(); + assertThat(Files.readAttributes(dir, CloudStorageFileAttributes.class).isRegularFile()) + .isFalse(); + } + + @Test + public void testIsOther() throws Exception { + Files.write(path, HAPPY); + assertThat(Files.readAttributes(path, CloudStorageFileAttributes.class).isOther()).isFalse(); + assertThat(Files.readAttributes(dir, CloudStorageFileAttributes.class).isOther()).isFalse(); + } + + @Test + public void testIsSymbolicLink() throws Exception { + Files.write(path, HAPPY); + assertThat(Files.readAttributes(path, CloudStorageFileAttributes.class).isSymbolicLink()) + .isFalse(); + assertThat(Files.readAttributes(dir, CloudStorageFileAttributes.class).isSymbolicLink()) + .isFalse(); + } + + @Test + public void testEquals_equalsTester() throws Exception { + Files.write(path, HAPPY, withMimeType("text/plain")); + CloudStorageFileAttributes a1 = Files.readAttributes(path, CloudStorageFileAttributes.class); + CloudStorageFileAttributes a2 = Files.readAttributes(path, CloudStorageFileAttributes.class); + Files.write(path, HAPPY, withMimeType("text/potato")); + CloudStorageFileAttributes b1 = Files.readAttributes(path, CloudStorageFileAttributes.class); + CloudStorageFileAttributes b2 = Files.readAttributes(path, CloudStorageFileAttributes.class); + new EqualsTester().addEqualityGroup(a1, a2).addEqualityGroup(b1, b2).testEquals(); + } + + @Test + public void testNullness() throws Exception { + Files.write(path, HAPPY); + CloudStorageFileAttributes pathAttributes = + Files.readAttributes(path, CloudStorageFileAttributes.class); + CloudStorageFileAttributes dirAttributes = + Files.readAttributes(dir, CloudStorageFileAttributes.class); + NullPointerTester tester = new NullPointerTester(); + tester.testAllPublicInstanceMethods(pathAttributes); + tester.testAllPublicInstanceMethods(dirAttributes); + } +} diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystemProviderTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystemProviderTest.java new file mode 100644 index 000000000000..8dad56ff984e --- /dev/null +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystemProviderTest.java @@ -0,0 +1,592 @@ +package com.google.gcloud.storage.contrib.nio; + +import static com.google.common.truth.Truth.assertThat; +import static com.google.gcloud.storage.contrib.nio.CloudStorageFileSystem.forBucket; +import static com.google.gcloud.storage.contrib.nio.CloudStorageOptions.withCacheControl; +import static com.google.gcloud.storage.contrib.nio.CloudStorageOptions.withMimeType; +import static java.nio.charset.StandardCharsets.UTF_8; +import static java.nio.file.StandardCopyOption.ATOMIC_MOVE; +import static java.nio.file.StandardCopyOption.COPY_ATTRIBUTES; +import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; +import static java.nio.file.StandardOpenOption.CREATE_NEW; +import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING; +import static java.nio.file.StandardOpenOption.WRITE; +import static org.junit.Assume.assumeTrue; + +import com.google.appengine.tools.cloudstorage.GcsFileMetadata; +import com.google.appengine.tools.cloudstorage.GcsFilename; +import com.google.appengine.tools.cloudstorage.GcsService; +import com.google.appengine.tools.cloudstorage.GcsServiceFactory; +import com.google.common.collect.ImmutableList; +import com.google.common.testing.NullPointerTester; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URI; +import java.nio.ByteBuffer; +import java.nio.channels.ReadableByteChannel; +import java.nio.channels.SeekableByteChannel; +import java.nio.file.AtomicMoveNotSupportedException; +import java.nio.file.CopyOption; +import java.nio.file.FileAlreadyExistsException; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import java.nio.file.OpenOption; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.nio.file.StandardOpenOption; +import java.util.List; + +/** Unit tests for {@link CloudStorageFileSystemProvider}. */ +@RunWith(JUnit4.class) +public class CloudStorageFileSystemProviderTest { + + private static final List FILE_CONTENTS = ImmutableList.of( + "To be, or not to be, that is the question—", + "Whether 'tis Nobler in the mind to suffer", + "The Slings and Arrows of outrageous Fortune,", + "Or to take Arms against a Sea of troubles,", + "And by opposing, end them? To die, to sleep—", + "No more; and by a sleep, to say we end", + "The Heart-ache, and the thousand Natural shocks", + "That Flesh is heir to? 'Tis a consummation"); + + private static final String SINGULARITY = "A string"; + + @Rule + public final ExpectedException thrown = ExpectedException.none(); + + @Rule + public final AppEngineRule appEngineRule = new AppEngineRule(); + + @Test + public void testSize() throws Exception { + Path path = Paths.get(URI.create("gs://bucket/wat")); + Files.write(path, SINGULARITY.getBytes(UTF_8)); + assertThat(Files.size(path)).isEqualTo(SINGULARITY.getBytes(UTF_8).length); + } + + @Test + public void testSize_trailingSlash_returnsFakePseudoDirectorySize() throws Exception { + assertThat(Files.size(Paths.get(URI.create("gs://bucket/wat/")))).isEqualTo(1); + } + + @Test + public void testSize_trailingSlash_disablePseudoDirectories() throws Exception { + try (CloudStorageFileSystem fs = forBucket("doodle", usePseudoDirectories(false))) { + Path path = fs.getPath("wat/"); + byte[] rapture = SINGULARITY.getBytes(UTF_8); + Files.write(path, rapture); + assertThat(Files.size(path)).isEqualTo(rapture.length); + } + } + + @Test + public void testReadAllBytes() throws Exception { + Path path = Paths.get(URI.create("gs://bucket/wat")); + Files.write(path, SINGULARITY.getBytes(UTF_8)); + assertThat(new String(Files.readAllBytes(path), UTF_8)).isEqualTo(SINGULARITY); + } + + @Test + public void testReadAllBytes_trailingSlash() throws Exception { + thrown.expect(CloudStoragePseudoDirectoryException.class); + Files.readAllBytes(Paths.get(URI.create("gs://bucket/wat/"))); + } + + @Test + public void testNewByteChannelRead() throws Exception { + Path path = Paths.get(URI.create("gs://bucket/wat")); + byte[] data = SINGULARITY.getBytes(UTF_8); + Files.write(path, data); + try (ReadableByteChannel input = Files.newByteChannel(path)) { + ByteBuffer buffer = ByteBuffer.allocate(data.length); + assertThat(input.read(buffer)).isEqualTo(data.length); + assertThat(new String(buffer.array(), UTF_8)).isEqualTo(SINGULARITY); + buffer.rewind(); + assertThat(input.read(buffer)).isEqualTo(-1); + } + } + + @Test + public void testNewByteChannelRead_seeking() throws Exception { + Path path = Paths.get(URI.create("gs://lol/cat")); + Files.write(path, "helloworld".getBytes(UTF_8)); + try (SeekableByteChannel input = Files.newByteChannel(path)) { + ByteBuffer buffer = ByteBuffer.allocate(5); + input.position(5); + assertThat(input.position()).isEqualTo(5); + assertThat(input.read(buffer)).isEqualTo(5); + assertThat(input.position()).isEqualTo(10); + assertThat(new String(buffer.array(), UTF_8)).isEqualTo("world"); + buffer.rewind(); + assertThat(input.read(buffer)).isEqualTo(-1); + input.position(0); + assertThat(input.position()).isEqualTo(0); + assertThat(input.read(buffer)).isEqualTo(5); + assertThat(input.position()).isEqualTo(5); + assertThat(new String(buffer.array(), UTF_8)).isEqualTo("hello"); + } + } + + @Test + public void testNewByteChannelRead_seekBeyondSize_reportsEofOnNextRead() throws Exception { + Path path = Paths.get(URI.create("gs://lol/cat")); + Files.write(path, "hellocat".getBytes(UTF_8)); + try (SeekableByteChannel input = Files.newByteChannel(path)) { + ByteBuffer buffer = ByteBuffer.allocate(5); + input.position(10); + assertThat(input.read(buffer)).isEqualTo(-1); + input.position(11); + assertThat(input.read(buffer)).isEqualTo(-1); + assertThat(input.size()).isEqualTo(8); + } + } + + @Test + public void testNewByteChannelRead_trailingSlash() throws Exception { + Path path = Paths.get(URI.create("gs://bucket/wat/")); + thrown.expect(CloudStoragePseudoDirectoryException.class); + Files.newByteChannel(path); + } + + @Test + public void testNewByteChannelRead_notFound() throws Exception { + Path path = Paths.get(URI.create("gs://bucket/wednesday")); + thrown.expect(NoSuchFileException.class); + Files.newByteChannel(path); + } + + @Test + public void testNewByteChannelWrite() throws Exception { + Path path = Paths.get(URI.create("gs://bucket/tests")); + try (SeekableByteChannel output = Files.newByteChannel(path, WRITE)) { + assertThat(output.position()).isEqualTo(0); + assertThat(output.size()).isEqualTo(0); + ByteBuffer buffer = ByteBuffer.wrap("filec".getBytes(UTF_8)); + assertThat(output.write(buffer)).isEqualTo(5); + assertThat(output.position()).isEqualTo(5); + assertThat(output.size()).isEqualTo(5); + buffer = ByteBuffer.wrap("onten".getBytes(UTF_8)); + assertThat(output.write(buffer)).isEqualTo(5); + assertThat(output.position()).isEqualTo(10); + assertThat(output.size()).isEqualTo(10); + } + assertThat(new String(Files.readAllBytes(path), UTF_8)).isEqualTo("fileconten"); + } + + @Test + public void testNewInputStream() throws Exception { + Path path = Paths.get(URI.create("gs://bucket/wat")); + Files.write(path, SINGULARITY.getBytes(UTF_8)); + try (InputStream input = Files.newInputStream(path)) { + byte[] data = new byte[SINGULARITY.getBytes(UTF_8).length]; + input.read(data); + assertThat(new String(data, UTF_8)).isEqualTo(SINGULARITY); + } + } + + @Test + public void testNewInputStream_trailingSlash() throws Exception { + Path path = Paths.get(URI.create("gs://bucket/wat/")); + thrown.expect(CloudStoragePseudoDirectoryException.class); + try (InputStream input = Files.newInputStream(path)) { + input.read(); + } + } + + @Test + public void testNewInputStream_notFound() throws Exception { + Path path = Paths.get(URI.create("gs://cry/wednesday")); + thrown.expect(NoSuchFileException.class); + try (InputStream input = Files.newInputStream(path)) { + input.read(); + } + } + + @Test + public void testNewOutputStream() throws Exception { + Path path = Paths.get(URI.create("gs://bucket/wat")); + Files.write(path, SINGULARITY.getBytes(UTF_8)); + try (OutputStream output = Files.newOutputStream(path)) { + output.write(SINGULARITY.getBytes(UTF_8)); + } + assertThat(new String(Files.readAllBytes(path), UTF_8)).isEqualTo(SINGULARITY); + } + + @Test + public void testNewOutputStream_truncateByDefault() throws Exception { + Path path = Paths.get(URI.create("gs://bucket/wat")); + Files.write(path, SINGULARITY.getBytes(UTF_8)); + Files.write(path, "hello".getBytes(UTF_8)); + try (OutputStream output = Files.newOutputStream(path)) { + output.write(SINGULARITY.getBytes(UTF_8)); + } + assertThat(new String(Files.readAllBytes(path), UTF_8)).isEqualTo(SINGULARITY); + } + + @Test + public void testNewOutputStream_truncateExplicitly() throws Exception { + Path path = Paths.get(URI.create("gs://bucket/wat")); + Files.write(path, SINGULARITY.getBytes(UTF_8)); + Files.write(path, "hello".getBytes(UTF_8)); + try (OutputStream output = Files.newOutputStream(path, TRUNCATE_EXISTING)) { + output.write(SINGULARITY.getBytes(UTF_8)); + } + assertThat(new String(Files.readAllBytes(path), UTF_8)).isEqualTo(SINGULARITY); + } + + @Test + public void testNewOutputStream_trailingSlash() throws Exception { + Path path = Paths.get(URI.create("gs://bucket/wat/")); + thrown.expect(CloudStoragePseudoDirectoryException.class); + try (OutputStream output = Files.newOutputStream(path)) { + } + } + + @Test + public void testNewOutputStream_createNew() throws Exception { + Path path = Paths.get(URI.create("gs://cry/wednesday")); + try (OutputStream output = Files.newOutputStream(path, CREATE_NEW)) { + } + } + + @Test + public void testNewOutputStream_createNew_alreadyExists() throws Exception { + Path path = Paths.get(URI.create("gs://cry/wednesday")); + Files.write(path, SINGULARITY.getBytes(UTF_8)); + thrown.expect(FileAlreadyExistsException.class); + try (OutputStream output = Files.newOutputStream(path, CREATE_NEW)) { + } + } + + @Test + public void testWrite_objectNameWithExtraSlashes_throwsIae() throws Exception { + Path path = Paths.get(URI.create("gs://double/slash//yep")); + thrown.expect(IllegalArgumentException.class); + Files.write(path, FILE_CONTENTS, UTF_8); + } + + @Test + public void testWrite_objectNameWithExtraSlashes_canBeNormalized() throws Exception { + try (CloudStorageFileSystem fs = forBucket("greenbean", permitEmptyPathComponents(false))) { + Path path = fs.getPath("adipose//yep").normalize(); + Files.write(path, FILE_CONTENTS, UTF_8); + assertThat(Files.readAllLines(path, UTF_8)).isEqualTo(FILE_CONTENTS); + GcsService gcsService = GcsServiceFactory.createGcsService(); + assertThat(gcsService.getMetadata(new GcsFilename("greenbean", "adipose/yep"))).isNotNull(); + assertThat(Files.exists(path)).isTrue(); + } + } + + @Test + public void testWrite_objectNameWithExtraSlashes_permitEmptyPathComponents() throws Exception { + try (CloudStorageFileSystem fs = forBucket("greenbean", permitEmptyPathComponents(true))) { + Path path = fs.getPath("adipose//yep"); + Files.write(path, FILE_CONTENTS, UTF_8); + assertThat(Files.readAllLines(path, UTF_8)).isEqualTo(FILE_CONTENTS); + GcsService gcsService = GcsServiceFactory.createGcsService(); + assertThat(gcsService.getMetadata(new GcsFilename("greenbean", "adipose//yep"))).isNotNull(); + assertThat(Files.exists(path)).isTrue(); + } + } + + @Test + public void testWrite_absoluteObjectName_prefixSlashGetsRemoved() throws Exception { + Path path = Paths.get(URI.create("gs://greenbean/adipose/yep")); + Files.write(path, FILE_CONTENTS, UTF_8); + assertThat(Files.readAllLines(path, UTF_8)).isEqualTo(FILE_CONTENTS); + GcsService gcsService = GcsServiceFactory.createGcsService(); + assertThat(gcsService.getMetadata(new GcsFilename("greenbean", "adipose/yep"))).isNotNull(); + assertThat(Files.exists(path)).isTrue(); + } + + @Test + public void testWrite_absoluteObjectName_disableStrip_slashGetsPreserved() throws Exception { + try (CloudStorageFileSystem fs = + forBucket("greenbean", + CloudStorageConfiguration.builder() + .stripPrefixSlash(false) + .build())) { + Path path = fs.getPath("/adipose/yep"); + Files.write(path, FILE_CONTENTS, UTF_8); + assertThat(Files.readAllLines(path, UTF_8)).isEqualTo(FILE_CONTENTS); + GcsService gcsService = GcsServiceFactory.createGcsService(); + assertThat(gcsService.getMetadata(new GcsFilename("greenbean", "/adipose/yep"))).isNotNull(); + assertThat(Files.exists(path)).isTrue(); + } + } + + @Test + public void testWrite() throws Exception { + Path path = Paths.get(URI.create("gs://greenbean/adipose")); + Files.write(path, FILE_CONTENTS, UTF_8); + assertThat(Files.readAllLines(path, UTF_8)).isEqualTo(FILE_CONTENTS); + } + + @Test + public void testWrite_trailingSlash() throws Exception { + thrown.expect(CloudStoragePseudoDirectoryException.class); + Files.write(Paths.get(URI.create("gs://greenbean/adipose/")), FILE_CONTENTS, UTF_8); + } + + @Test + public void testExists() throws Exception { + assertThat(Files.exists(Paths.get(URI.create("gs://military/fashion")))).isFalse(); + Files.write(Paths.get(URI.create("gs://military/fashion")), "(✿◕ ‿◕ )ノ".getBytes(UTF_8)); + assertThat(Files.exists(Paths.get(URI.create("gs://military/fashion")))).isTrue(); + } + + @Test + public void testExists_trailingSlash() throws Exception { + assertThat(Files.exists(Paths.get(URI.create("gs://military/fashion/")))).isTrue(); + assertThat(Files.exists(Paths.get(URI.create("gs://military/fashion/.")))).isTrue(); + assertThat(Files.exists(Paths.get(URI.create("gs://military/fashion/..")))).isTrue(); + } + + @Test + public void testExists_trailingSlash_disablePseudoDirectories() throws Exception { + try (CloudStorageFileSystem fs = forBucket("military", usePseudoDirectories(false))) { + assertThat(Files.exists(fs.getPath("fashion/"))).isFalse(); + } + } + + @Test + public void testDelete() throws Exception { + Files.write(Paths.get(URI.create("gs://love/fashion")), "(✿◕ ‿◕ )ノ".getBytes(UTF_8)); + assertThat(Files.exists(Paths.get(URI.create("gs://love/fashion")))).isTrue(); + Files.delete(Paths.get(URI.create("gs://love/fashion"))); + assertThat(Files.exists(Paths.get(URI.create("gs://love/fashion")))).isFalse(); + } + + @Test + public void testDelete_dotDirNotNormalized_throwsIae() throws Exception { + thrown.expect(IllegalArgumentException.class); + Files.delete(Paths.get(URI.create("gs://love/fly/../passion"))); + } + + @Test + public void testDelete_trailingSlash() throws Exception { + thrown.expect(CloudStoragePseudoDirectoryException.class); + Files.delete(Paths.get(URI.create("gs://love/passion/"))); + } + + @Test + public void testDelete_trailingSlash_disablePseudoDirectories() throws Exception { + try (CloudStorageFileSystem fs = forBucket("pumpkin", usePseudoDirectories(false))) { + Path path = fs.getPath("wat/"); + Files.write(path, FILE_CONTENTS, UTF_8); + GcsService gcsService = GcsServiceFactory.createGcsService(); + assertThat(gcsService.getMetadata(new GcsFilename("pumpkin", "wat/"))).isNotNull(); + Files.delete(path); + assertThat(gcsService.getMetadata(new GcsFilename("pumpkin", "wat/"))).isNull(); + } + } + + @Test + public void testDelete_notFound() throws Exception { + GcsService gcsService = GcsServiceFactory.createGcsService(); + assumeTrue(!gcsService.delete(new GcsFilename("loveh", "passionehu"))); // XXX: b/15832793 + thrown.expect(NoSuchFileException.class); + Files.delete(Paths.get(URI.create("gs://loveh/passionehu"))); + } + + @Test + public void testDeleteIfExists() throws Exception { + GcsService gcsService = GcsServiceFactory.createGcsService(); + assumeTrue(!gcsService.delete(new GcsFilename("loveh", "passionehu"))); // XXX: b/15832793 + assertThat(Files.deleteIfExists(Paths.get(URI.create("gs://love/passionz")))).isFalse(); + Files.write(Paths.get(URI.create("gs://love/passionz")), "(✿◕ ‿◕ )ノ".getBytes(UTF_8)); + assertThat(Files.deleteIfExists(Paths.get(URI.create("gs://love/passionz")))).isTrue(); + } + + @Test + public void testDeleteIfExists_trailingSlash() throws Exception { + thrown.expect(CloudStoragePseudoDirectoryException.class); + Files.deleteIfExists(Paths.get(URI.create("gs://love/passion/"))); + } + + @Test + public void testCopy() throws Exception { + Path source = Paths.get(URI.create("gs://military/fashion.show")); + Path target = Paths.get(URI.create("gs://greenbean/adipose")); + Files.write(source, "(✿◕ ‿◕ )ノ".getBytes(UTF_8)); + Files.copy(source, target); + assertThat(new String(Files.readAllBytes(target), UTF_8)).isEqualTo("(✿◕ ‿◕ )ノ"); + assertThat(Files.exists(source)).isTrue(); + assertThat(Files.exists(target)).isTrue(); + } + + @Test + public void testCopy_sourceMissing_throwsNoSuchFileException() throws Exception { + thrown.expect(NoSuchFileException.class); + Files.copy( + Paths.get(URI.create("gs://military/fashion.show")), + Paths.get(URI.create("gs://greenbean/adipose"))); + } + + @Test + public void testCopy_targetExists_throwsFileAlreadyExistsException() throws Exception { + Path source = Paths.get(URI.create("gs://military/fashion.show")); + Path target = Paths.get(URI.create("gs://greenbean/adipose")); + Files.write(source, "(✿◕ ‿◕ )ノ".getBytes(UTF_8)); + Files.write(target, "(✿◕ ‿◕ )ノ".getBytes(UTF_8)); + thrown.expect(FileAlreadyExistsException.class); + Files.copy(source, target); + } + + @Test + public void testCopyReplace_targetExists_works() throws Exception { + Path source = Paths.get(URI.create("gs://military/fashion.show")); + Path target = Paths.get(URI.create("gs://greenbean/adipose")); + Files.write(source, "(✿◕ ‿◕ )ノ".getBytes(UTF_8)); + Files.write(target, "(✿◕ ‿◕ )ノ".getBytes(UTF_8)); + Files.copy(source, target, REPLACE_EXISTING); + } + + @Test + public void testCopy_directory_doesNothing() throws Exception { + Path source = Paths.get(URI.create("gs://military/fundir/")); + Path target = Paths.get(URI.create("gs://greenbean/loldir/")); + Files.copy(source, target); + } + + @Test + public void testCopy_atomic_throwsUnsupported() throws Exception { + Path source = Paths.get(URI.create("gs://military/fashion.show")); + Path target = Paths.get(URI.create("gs://greenbean/adipose")); + Files.write(source, "(✿◕ ‿◕ )ノ".getBytes(UTF_8)); + thrown.expect(UnsupportedOperationException.class); + Files.copy(source, target, ATOMIC_MOVE); + } + + @Test + public void testMove() throws Exception { + Path source = Paths.get(URI.create("gs://military/fashion.show")); + Path target = Paths.get(URI.create("gs://greenbean/adipose")); + Files.write(source, "(✿◕ ‿◕ )ノ".getBytes(UTF_8)); + Files.move(source, target); + assertThat(new String(Files.readAllBytes(target), UTF_8)).isEqualTo("(✿◕ ‿◕ )ノ"); + assertThat(Files.exists(source)).isFalse(); + assertThat(Files.exists(target)).isTrue(); + } + + @Test + public void testCreateDirectory() throws Exception { + Path path = Paths.get(URI.create("gs://greenbean/dir/")); + Files.createDirectory(path); + assertThat(Files.exists(path)).isTrue(); + } + + @Test + public void testMove_atomicMove_notSupported() throws Exception { + Path source = Paths.get(URI.create("gs://military/fashion.show")); + Path target = Paths.get(URI.create("gs://greenbean/adipose")); + Files.write(source, "(✿◕ ‿◕ )ノ".getBytes(UTF_8)); + thrown.expect(AtomicMoveNotSupportedException.class); + Files.move(source, target, ATOMIC_MOVE); + } + + @Test + public void testIsDirectory() throws Exception { + try (FileSystem fs = FileSystems.getFileSystem(URI.create("gs://doodle"))) { + assertThat(Files.isDirectory(fs.getPath(""))).isTrue(); + assertThat(Files.isDirectory(fs.getPath("/"))).isTrue(); + assertThat(Files.isDirectory(fs.getPath("."))).isTrue(); + assertThat(Files.isDirectory(fs.getPath("./"))).isTrue(); + assertThat(Files.isDirectory(fs.getPath("cat/.."))).isTrue(); + assertThat(Files.isDirectory(fs.getPath("hello/cat/.."))).isTrue(); + assertThat(Files.isDirectory(fs.getPath("cat/../"))).isTrue(); + assertThat(Files.isDirectory(fs.getPath("hello/cat/../"))).isTrue(); + } + } + + @Test + public void testIsDirectory_trailingSlash_alwaysTrue() throws Exception { + assertThat(Files.isDirectory(Paths.get(URI.create("gs://military/fundir/")))).isTrue(); + } + + @Test + public void testIsDirectory_trailingSlash_pseudoDirectoriesDisabled_false() throws Exception { + try (CloudStorageFileSystem fs = forBucket("doodle", usePseudoDirectories(false))) { + assertThat(Files.isDirectory(fs.getPath("fundir/"))).isFalse(); + } + } + + @Test + public void testCopy_withCopyAttributes_preservesAttributes() throws Exception { + Path source = Paths.get(URI.create("gs://military/fashion.show")); + Path target = Paths.get(URI.create("gs://greenbean/adipose")); + Files.write(source, "(✿◕ ‿◕ )ノ".getBytes(UTF_8), + withMimeType("text/lolcat"), + withCacheControl("public; max-age=666")); + Files.copy(source, target, COPY_ATTRIBUTES); + GcsService gcsService = GcsServiceFactory.createGcsService(); + GcsFileMetadata metadata = gcsService.getMetadata(new GcsFilename("greenbean", "adipose")); + assertThat(metadata.getOptions().getMimeType()).isEqualTo("text/lolcat"); + assertThat(metadata.getOptions().getCacheControl()).isEqualTo("public; max-age=666"); + } + + @Test + public void testCopy_withoutOptions_doesntPreservesAttributes() throws Exception { + Path source = Paths.get(URI.create("gs://military/fashion.show")); + Path target = Paths.get(URI.create("gs://greenbean/adipose")); + Files.write(source, "(✿◕ ‿◕ )ノ".getBytes(UTF_8), + withMimeType("text/lolcat"), + withCacheControl("public; max-age=666")); + Files.copy(source, target); + GcsService gcsService = GcsServiceFactory.createGcsService(); + GcsFileMetadata metadata = gcsService.getMetadata(new GcsFilename("greenbean", "adipose")); + assertThat(metadata.getOptions().getMimeType()).isNull(); + assertThat(metadata.getOptions().getCacheControl()).isNull(); + } + + @Test + public void testCopy_overwriteAttributes() throws Exception { + Path source = Paths.get(URI.create("gs://military/fashion.show")); + Path target = Paths.get(URI.create("gs://greenbean/adipose")); + Files.write(source, "(✿◕ ‿◕ )ノ".getBytes(UTF_8), + withMimeType("text/lolcat"), + withCacheControl("public; max-age=666")); + Files.copy(source, target, COPY_ATTRIBUTES, + withMimeType("text/palfun")); + GcsService gcsService = GcsServiceFactory.createGcsService(); + GcsFileMetadata metadata = gcsService.getMetadata(new GcsFilename("greenbean", "adipose")); + assertThat(metadata.getOptions().getMimeType()).isEqualTo("text/palfun"); + assertThat(metadata.getOptions().getCacheControl()).isEqualTo("public; max-age=666"); + } + + @Test + public void testNullness() throws Exception { + try (FileSystem fs = FileSystems.getFileSystem(URI.create("gs://blood"))) { + NullPointerTester tester = new NullPointerTester() + .setDefault(URI.class, URI.create("gs://blood")) + .setDefault(Path.class, fs.getPath("and/one")) + .setDefault(OpenOption.class, StandardOpenOption.CREATE) + .setDefault(CopyOption.class, StandardCopyOption.COPY_ATTRIBUTES); + tester.testAllPublicStaticMethods(CloudStorageFileSystemProvider.class); + tester.testAllPublicInstanceMethods(new CloudStorageFileSystemProvider()); + } + } + + private static CloudStorageConfiguration permitEmptyPathComponents(boolean value) { + return CloudStorageConfiguration.builder() + .permitEmptyPathComponents(value) + .build(); + } + + private static CloudStorageConfiguration usePseudoDirectories(boolean value) { + return CloudStorageConfiguration.builder() + .usePseudoDirectories(value) + .build(); + } +} diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystemTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystemTest.java new file mode 100644 index 000000000000..7cb750238b14 --- /dev/null +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystemTest.java @@ -0,0 +1,111 @@ +package com.google.gcloud.storage.contrib.nio; + +import static com.google.common.truth.Truth.assertThat; +import static java.nio.charset.StandardCharsets.UTF_8; + +import com.google.common.testing.EqualsTester; +import com.google.common.testing.NullPointerTester; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.net.URI; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Paths; + +/** Unit tests for {@link CloudStorageFileSystem}. */ +@RunWith(JUnit4.class) +public class CloudStorageFileSystemTest { + + private static final String ALONE = "" + + "To be, or not to be, that is the question—\n" + + "Whether 'tis Nobler in the mind to suffer\n" + + "The Slings and Arrows of outrageous Fortune,\n" + + "Or to take Arms against a Sea of troubles,\n" + + "And by opposing, end them? To die, to sleep—\n" + + "No more; and by a sleep, to say we end\n" + + "The Heart-ache, and the thousand Natural shocks\n" + + "That Flesh is heir to? 'Tis a consummation\n"; + + @Rule + public final AppEngineRule appEngineRule = new AppEngineRule(); + + @Test + public void testGetPath() throws Exception { + try (FileSystem fs = CloudStorageFileSystem.forBucket("bucket")) { + assertThat(fs.getPath("/angel").toString()).isEqualTo("/angel"); + assertThat(fs.getPath("/angel").toUri().toString()).isEqualTo("gs://bucket/angel"); + } + } + + @Test + public void testWrite() throws Exception { + try (FileSystem fs = CloudStorageFileSystem.forBucket("bucket")) { + Files.write(fs.getPath("/angel"), ALONE.getBytes(UTF_8)); + } + assertThat(new String(Files.readAllBytes(Paths.get(URI.create("gs://bucket/angel"))), UTF_8)) + .isEqualTo(ALONE); + } + + @Test + public void testRead() throws Exception { + Files.write(Paths.get(URI.create("gs://bucket/angel")), ALONE.getBytes(UTF_8)); + try (FileSystem fs = CloudStorageFileSystem.forBucket("bucket")) { + assertThat(new String(Files.readAllBytes(fs.getPath("/angel")), UTF_8)).isEqualTo(ALONE); + } + } + + @Test + public void testExists_false() throws Exception { + try (FileSystem fs = FileSystems.getFileSystem(URI.create("gs://bucket"))) { + assertThat(Files.exists(fs.getPath("/angel"))).isFalse(); + } + } + + @Test + public void testExists_true() throws Exception { + Files.write(Paths.get(URI.create("gs://bucket/angel")), ALONE.getBytes(UTF_8)); + try (FileSystem fs = CloudStorageFileSystem.forBucket("bucket")) { + assertThat(Files.exists(fs.getPath("/angel"))).isTrue(); + } + } + + @Test + public void testGetters() throws Exception { + try (FileSystem fs = CloudStorageFileSystem.forBucket("bucket")) { + assertThat(fs.isOpen()).isTrue(); + assertThat(fs.isReadOnly()).isFalse(); + assertThat(fs.getRootDirectories()).containsExactly(fs.getPath("/")); + assertThat(fs.getFileStores()).isEmpty(); + assertThat(fs.getSeparator()).isEqualTo("/"); + assertThat(fs.supportedFileAttributeViews()).containsExactly("basic", "gcs"); + } + } + + @Test + public void testEquals() throws Exception { + try (FileSystem bucket1 = CloudStorageFileSystem.forBucket("bucket"); + FileSystem bucket2 = FileSystems.getFileSystem(URI.create("gs://bucket")); + FileSystem doge1 = CloudStorageFileSystem.forBucket("doge"); + FileSystem doge2 = FileSystems.getFileSystem(URI.create("gs://doge"))) { + new EqualsTester() + .addEqualityGroup(bucket1, bucket2) + .addEqualityGroup(doge1, doge2) + .testEquals(); + } + } + + @Test + public void testNullness() throws Exception { + try (FileSystem fs = FileSystems.getFileSystem(URI.create("gs://bucket"))) { + NullPointerTester tester = new NullPointerTester() + .setDefault(CloudStorageConfiguration.class, CloudStorageConfiguration.DEFAULT); + tester.testAllPublicStaticMethods(CloudStorageFileSystem.class); + tester.testAllPublicInstanceMethods(fs); + } + } +} diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageOptionsTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageOptionsTest.java new file mode 100644 index 000000000000..0ed9dc5f7507 --- /dev/null +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageOptionsTest.java @@ -0,0 +1,105 @@ +package com.google.gcloud.storage.contrib.nio; + +import static com.google.appengine.tools.cloudstorage.GcsServiceFactory.createGcsService; +import static com.google.common.truth.Truth.assertThat; +import static com.google.gcloud.storage.contrib.nio.CloudStorageOptions.withAcl; +import static com.google.gcloud.storage.contrib.nio.CloudStorageOptions.withCacheControl; +import static com.google.gcloud.storage.contrib.nio.CloudStorageOptions.withContentDisposition; +import static com.google.gcloud.storage.contrib.nio.CloudStorageOptions.withContentEncoding; +import static com.google.gcloud.storage.contrib.nio.CloudStorageOptions.withMimeType; +import static com.google.gcloud.storage.contrib.nio.CloudStorageOptions.withUserMetadata; +import static com.google.gcloud.storage.contrib.nio.CloudStorageOptions.withoutCaching; +import static java.nio.charset.StandardCharsets.UTF_8; + +import com.google.appengine.tools.cloudstorage.GcsFileMetadata; +import com.google.appengine.tools.cloudstorage.GcsFilename; +import com.google.common.testing.NullPointerTester; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +/** Unit tests for {@link CloudStorageOptions}. */ +@RunWith(JUnit4.class) +public class CloudStorageOptionsTest { + + @Rule + public final AppEngineRule appEngineRule = new AppEngineRule(); + + @Test + public void testWithoutCaching() throws Exception { + Path path = Paths.get(URI.create("gs://bucket/path")); + Files.write(path, "(✿◕ ‿◕ )ノ".getBytes(UTF_8), + withoutCaching()); + assertThat(getMetadata("bucket", "path").getOptions().getCacheControl()).isEqualTo("no-cache"); + } + + @Test + public void testCacheControl() throws Exception { + Path path = Paths.get(URI.create("gs://bucket/path")); + Files.write(path, "(✿◕ ‿◕ )ノ".getBytes(UTF_8), + withCacheControl("potato")); + assertThat(getMetadata("bucket", "path").getOptions().getCacheControl()).isEqualTo("potato"); + } + + @Test + public void testWithAcl() throws Exception { + Path path = Paths.get(URI.create("gs://bucket/path")); + Files.write(path, "(✿◕ ‿◕ )ノ".getBytes(UTF_8), + withAcl("mine empire of dirt")); + assertThat(getMetadata("bucket", "path").getOptions().getAcl()) + .isEqualTo("mine empire of dirt"); + } + + @Test + public void testWithContentDisposition() throws Exception { + Path path = Paths.get(URI.create("gs://bucket/path")); + Files.write(path, "(✿◕ ‿◕ )ノ".getBytes(UTF_8), + withContentDisposition("bubbly fun")); + assertThat(getMetadata("bucket", "path").getOptions().getContentDisposition()) + .isEqualTo("bubbly fun"); + } + + @Test + public void testWithContentEncoding() throws Exception { + Path path = Paths.get(URI.create("gs://bucket/path")); + Files.write(path, "(✿◕ ‿◕ )ノ".getBytes(UTF_8), + withContentEncoding("gzip")); + assertThat(getMetadata("bucket", "path").getOptions().getContentEncoding()).isEqualTo("gzip"); + } + + @Test + public void testWithUserMetadata() throws Exception { + Path path = Paths.get(URI.create("gs://bucket/path")); + Files.write(path, "(✿◕ ‿◕ )ノ".getBytes(UTF_8), + withUserMetadata("nolo", "contendere"), + withUserMetadata("eternal", "sadness")); + GcsFileMetadata metadata = getMetadata("bucket", "path"); + assertThat(metadata.getOptions().getUserMetadata().get("nolo")).isEqualTo("contendere"); + assertThat(metadata.getOptions().getUserMetadata().get("eternal")).isEqualTo("sadness"); + } + + @Test + public void testWithMimeType_string() throws Exception { + Path path = Paths.get(URI.create("gs://bucket/path")); + Files.write(path, "(✿◕ ‿◕ )ノ".getBytes(UTF_8), + withMimeType("text/plain")); + assertThat(getMetadata("bucket", "path").getOptions().getMimeType()).isEqualTo("text/plain"); + } + + private static GcsFileMetadata getMetadata(String bucket, String objectName) throws Exception { + return createGcsService().getMetadata(new GcsFilename(bucket, objectName)); + } + + @Test + public void testNullness() throws Exception { + NullPointerTester tester = new NullPointerTester(); + tester.testAllPublicStaticMethods(CloudStorageOptions.class); + } +} diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStoragePathTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStoragePathTest.java new file mode 100644 index 000000000000..aaf9e5b4fd16 --- /dev/null +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStoragePathTest.java @@ -0,0 +1,486 @@ +package com.google.gcloud.storage.contrib.nio; + +import static com.google.common.truth.Truth.assertThat; +import static com.google.gcloud.storage.contrib.nio.CloudStorageFileSystem.forBucket; + +import com.google.common.collect.Iterables; +import com.google.common.testing.EqualsTester; +import com.google.common.testing.NullPointerTester; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.net.URI; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.ProviderMismatchException; + +/** Unit tests for {@link CloudStoragePath}. */ +@RunWith(JUnit4.class) +public class CloudStoragePathTest { + + @Rule + public final ExpectedException thrown = ExpectedException.none(); + + @Rule + public final AppEngineRule appEngineRule = new AppEngineRule(); + + @Test + public void testCreate_neverRemoveExtraSlashes() { + try (CloudStorageFileSystem fs = forBucket("doodle")) { + assertThat(fs.getPath("lol//cat").toString()).isEqualTo("lol//cat"); + assertThat((Object) fs.getPath("lol//cat")).isEqualTo(fs.getPath("lol//cat")); + } + } + + @Test + public void testCreate_preservesTrailingSlash() { + try (CloudStorageFileSystem fs = forBucket("doodle")) { + assertThat(fs.getPath("lol/cat/").toString()).isEqualTo("lol/cat/"); + assertThat((Object) fs.getPath("lol/cat/")).isEqualTo(fs.getPath("lol/cat/")); + } + } + + @Test + public void testGetGcsFilename_empty_notAllowed() { + try (CloudStorageFileSystem fs = forBucket("doodle")) { + thrown.expect(IllegalArgumentException.class); + fs.getPath("").getGcsFilename(); + } + } + + @Test + public void testGetGcsFilename_stripsPrefixSlash() { + try (CloudStorageFileSystem fs = forBucket("doodle")) { + assertThat(fs.getPath("/hi").getGcsFilename().getObjectName()).isEqualTo("hi"); + } + } + + @Test + public void testGetGcsFilename_overrideStripPrefixSlash_doesntStripPrefixSlash() { + try (CloudStorageFileSystem fs = forBucket("doodle", stripPrefixSlash(false))) { + assertThat(fs.getPath("/hi").getGcsFilename().getObjectName()).isEqualTo("/hi"); + } + } + + @Test + public void testGetGcsFilename_extraSlashes_throwsIae() { + try (CloudStorageFileSystem fs = forBucket("doodle")) { + thrown.expect(IllegalArgumentException.class); + fs.getPath("a//b").getGcsFilename(); + } + } + + @Test + public void testGetGcsFilename_overridepermitEmptyPathComponents() { + try (CloudStorageFileSystem fs = forBucket("doodle", permitEmptyPathComponents(true))) { + assertThat(fs.getPath("a//b").getGcsFilename().getObjectName()).isEqualTo("a//b"); + } + } + + @Test + public void testGetGcsFilename_freaksOutOnExtraSlashesAndDotDirs() { + try (CloudStorageFileSystem fs = forBucket("doodle")) { + thrown.expect(IllegalArgumentException.class); + fs.getPath("a//b/..").getGcsFilename(); + } + } + + @Test + public void testNameCount() { + try (CloudStorageFileSystem fs = forBucket("doodle")) { + assertThat(fs.getPath("").getNameCount()).isEqualTo(1); + assertThat(fs.getPath("/").getNameCount()).isEqualTo(0); + assertThat(fs.getPath("/hi/").getNameCount()).isEqualTo(1); + assertThat(fs.getPath("/hi/yo").getNameCount()).isEqualTo(2); + assertThat(fs.getPath("hi/yo").getNameCount()).isEqualTo(2); + } + } + + @Test + public void testGetName() { + try (CloudStorageFileSystem fs = forBucket("doodle")) { + assertThat(fs.getPath("").getName(0).toString()).isEqualTo(""); + assertThat(fs.getPath("/hi").getName(0).toString()).isEqualTo("hi"); + assertThat(fs.getPath("hi/there").getName(1).toString()).isEqualTo("there"); + } + } + + @Test + public void testGetName_negative_throwsIae() { + try (CloudStorageFileSystem fs = forBucket("doodle")) { + thrown.expect(IllegalArgumentException.class); + fs.getPath("angel").getName(-1); + } + } + + @Test + public void testGetName_overflow_throwsIae() { + try (CloudStorageFileSystem fs = forBucket("doodle")) { + thrown.expect(IllegalArgumentException.class); + fs.getPath("angel").getName(1); + } + } + + @Test + public void testIterator() { + try (CloudStorageFileSystem fs = forBucket("doodle")) { + assertThat(Iterables.get(fs.getPath("/dog/mog"), 0).toString()).isEqualTo("dog"); + assertThat(Iterables.get(fs.getPath("/dog/mog"), 1).toString()).isEqualTo("mog"); + assertThat(Iterables.size(fs.getPath("/"))).isEqualTo(0); + assertThat(Iterables.size(fs.getPath(""))).isEqualTo(1); + assertThat(Iterables.get(fs.getPath(""), 0).toString()).isEqualTo(""); + } + } + + @Test + public void testNormalize() { + try (CloudStorageFileSystem fs = forBucket("doodle")) { + assertThat(fs.getPath("/").normalize().toString()).isEqualTo("/"); + assertThat(fs.getPath("a/x/../b/x/..").normalize().toString()).isEqualTo("a/b/"); + assertThat(fs.getPath("/x/x/../../♡").normalize().toString()).isEqualTo("/♡"); + assertThat(fs.getPath("/x/x/./.././.././♡").normalize().toString()).isEqualTo("/♡"); + } + } + + @Test + public void testNormalize_dot_becomesBlank() { + try (CloudStorageFileSystem fs = forBucket("doodle")) { + assertThat(fs.getPath("").normalize().toString()).isEqualTo(""); + assertThat(fs.getPath(".").normalize().toString()).isEqualTo(""); + } + } + + @Test + public void testNormalize_trailingSlash_isPreserved() { + try (CloudStorageFileSystem fs = forBucket("doodle")) { + assertThat(fs.getPath("o/").normalize().toString()).isEqualTo("o/"); + } + } + + @Test + public void testNormalize_doubleDot_becomesBlank() { + try (CloudStorageFileSystem fs = forBucket("doodle")) { + assertThat(fs.getPath("..").normalize().toString()).isEqualTo(""); + assertThat(fs.getPath("../..").normalize().toString()).isEqualTo(""); + } + } + + @Test + public void testNormalize_extraSlashes_getRemoved() { + try (CloudStorageFileSystem fs = forBucket("doodle")) { + assertThat(fs.getPath("//life///b/good//").normalize().toString()).isEqualTo("/life/b/good/"); + } + } + + @Test + public void testToRealPath_hasDotDir_throwsIae() { + try (CloudStorageFileSystem fs = forBucket("doodle")) { + fs.getPath("a/hi./b").toRealPath(); + fs.getPath("a/.hi/b").toRealPath(); + thrown.expect(IllegalArgumentException.class); + thrown.expectMessage("dot-dir"); + fs.getPath("a/./b").toRealPath(); + } + } + + @Test + public void testToRealPath_hasDotDotDir_throwsIae() { + try (CloudStorageFileSystem fs = forBucket("doodle")) { + fs.getPath("a/hi../b").toRealPath(); + fs.getPath("a/..hi/b").toRealPath(); + thrown.expect(IllegalArgumentException.class); + thrown.expectMessage("dot-dir"); + fs.getPath("a/../b").toRealPath(); + } + } + + @Test + public void testToRealPath_extraSlashes_throwsIae() { + try (CloudStorageFileSystem fs = forBucket("doodle")) { + thrown.expect(IllegalArgumentException.class); + thrown.expectMessage("extra slashes"); + fs.getPath("a//b").toRealPath(); + } + } + + @Test + public void testToRealPath_overridePermitEmptyPathComponents_extraSlashes_slashesRemain() { + try (CloudStorageFileSystem fs = forBucket("doodle", permitEmptyPathComponents(true))) { + assertThat(fs.getPath("/life///b/./good/").toRealPath().toString()).isEqualTo("life///b/./good/"); + } + } + + @Test + public void testToRealPath_permitEmptyPathComponents_doesNotNormalize() { + try (CloudStorageFileSystem fs = forBucket("doodle", permitEmptyPathComponents(true))) { + assertThat(fs.getPath("a").toRealPath().toString()).isEqualTo("a"); + assertThat(fs.getPath("a//b").toRealPath().toString()).isEqualTo("a//b"); + assertThat(fs.getPath("a//./b//..").toRealPath().toString()).isEqualTo("a//./b//.."); + } + } + + @Test + public void testToRealPath_withWorkingDirectory_makesAbsolute() { + try (CloudStorageFileSystem fs = forBucket("doodle", workingDirectory("/lol"))) { + assertThat(fs.getPath("a").toRealPath().toString()).isEqualTo("lol/a"); + } + } + + @Test + public void testToRealPath_disableStripPrefixSlash_makesPathAbsolute() { + try (CloudStorageFileSystem fs = forBucket("doodle", stripPrefixSlash(false))) { + assertThat(fs.getPath("a").toRealPath().toString()).isEqualTo("/a"); + assertThat(fs.getPath("/a").toRealPath().toString()).isEqualTo("/a"); + } + } + + @Test + public void testToRealPath_trailingSlash_getsPreserved() { + try (CloudStorageFileSystem fs = forBucket("doodle")) { + assertThat(fs.getPath("a/b/").toRealPath().toString()).isEqualTo("a/b/"); + } + } + + @Test + public void testNormalize_empty_returnsEmpty() { + try (CloudStorageFileSystem fs = forBucket("doodle")) { + assertThat(fs.getPath("").normalize().toString()).isEqualTo(""); + } + } + + @Test + public void testNormalize_preserveTrailingSlash() { + try (CloudStorageFileSystem fs = forBucket("doodle")) { + assertThat(fs.getPath("a/b/../c/").normalize().toString()).isEqualTo("a/c/"); + assertThat(fs.getPath("a/b/./c/").normalize().toString()).isEqualTo("a/b/c/"); + } + } + + @Test + @SuppressWarnings("null") + public void testGetParent_preserveTrailingSlash() { + try (CloudStorageFileSystem fs = forBucket("doodle")) { + assertThat(fs.getPath("a/b/c").getParent().toString()).isEqualTo("a/b/"); + assertThat(fs.getPath("a/b/c/").getParent().toString()).isEqualTo("a/b/"); + assertThat((Object) fs.getPath("").getParent()).isNull(); + assertThat((Object) fs.getPath("/").getParent()).isNull(); + assertThat((Object) fs.getPath("aaa").getParent()).isNull(); + assertThat((Object) (fs.getPath("aaa/").getParent())).isNull(); + } + } + + @Test + @SuppressWarnings("null") + public void testGetRoot() { + try (CloudStorageFileSystem fs = forBucket("doodle")) { + assertThat(fs.getPath("/hello").getRoot().toString()).isEqualTo("/"); + assertThat((Object) fs.getPath("hello").getRoot()).isNull(); + } + } + + @Test + public void testRelativize() { + try (CloudStorageFileSystem fs = forBucket("doodle")) { + assertThat(fs.getPath("/foo/bar/lol/cat") + .relativize(fs.getPath("/foo/a/b/../../c")).toString()) + .isEqualTo("../../../a/b/../../c"); + } + } + + @Test + public void testRelativize_providerMismatch() { + try (CloudStorageFileSystem fs = forBucket("doodle")) { + thrown.expect(ProviderMismatchException.class); + fs.getPath("/etc").relativize(Paths.get("/dog")); + } + } + + @Test + @SuppressWarnings("ReturnValueIgnored") // testing that an Exception is thrown + public void testRelativize_providerMismatch2() { + try (CloudStorageFileSystem fs = forBucket("doodle")) { + thrown.expect(ProviderMismatchException.class); + Paths.get("/dog").relativize(fs.getPath("/etc")); + } + } + + @Test + public void testResolve() { + try (CloudStorageFileSystem fs = forBucket("doodle")) { + assertThat(fs.getPath("/hi").resolve("there").toString()).isEqualTo("/hi/there"); + assertThat(fs.getPath("hi").resolve("there").toString()).isEqualTo("hi/there"); + } + } + + @Test + public void testResolve_providerMismatch() { + try (CloudStorageFileSystem fs = forBucket("doodle")) { + thrown.expect(ProviderMismatchException.class); + fs.getPath("etc").resolve(Paths.get("/dog")); + } + } + + @Test + public void testIsAbsolute() { + try (CloudStorageFileSystem fs = forBucket("doodle")) { + assertThat(fs.getPath("/hi").isAbsolute()).isTrue(); + assertThat(fs.getPath("hi").isAbsolute()).isFalse(); + } + } + + @Test + public void testToAbsolutePath() { + try (CloudStorageFileSystem fs = forBucket("doodle")) { + assertThat((Object) fs.getPath("/hi").toAbsolutePath()).isEqualTo(fs.getPath("/hi")); + assertThat((Object) fs.getPath("hi").toAbsolutePath()).isEqualTo(fs.getPath("/hi")); + } + } + + @Test + public void testToAbsolutePath_withWorkingDirectory() { + try (CloudStorageFileSystem fs = forBucket("doodle", workingDirectory("/lol"))) { + assertThat(fs.getPath("a").toAbsolutePath().toString()).isEqualTo("/lol/a"); + } + } + + @Test + @SuppressWarnings("null") + public void testGetFileName() { + try (CloudStorageFileSystem fs = forBucket("doodle")) { + assertThat(fs.getPath("/hi/there").getFileName().toString()).isEqualTo("there"); + assertThat(fs.getPath("military/fashion/show").getFileName().toString()).isEqualTo("show"); + } + } + + @Test + public void testCompareTo() { + try (CloudStorageFileSystem fs = forBucket("doodle")) { + assertThat(fs.getPath("/hi/there").compareTo(fs.getPath("/hi/there"))).isEqualTo(0); + assertThat(fs.getPath("/hi/there").compareTo(fs.getPath("/hi/therf"))).isEqualTo(-1); + assertThat(fs.getPath("/hi/there").compareTo(fs.getPath("/hi/therd"))).isEqualTo(1); + } + } + + @Test + public void testStartsWith() { + try (CloudStorageFileSystem fs = forBucket("doodle")) { + assertThat(fs.getPath("/hi/there").startsWith(fs.getPath("/hi/there"))).isTrue(); + assertThat(fs.getPath("/hi/there").startsWith(fs.getPath("/hi/therf"))).isFalse(); + assertThat(fs.getPath("/hi/there").startsWith(fs.getPath("/hi"))).isTrue(); + assertThat(fs.getPath("/hi/there").startsWith(fs.getPath("/hi/"))).isTrue(); + assertThat(fs.getPath("/hi/there").startsWith(fs.getPath("hi"))).isFalse(); + assertThat(fs.getPath("/hi/there").startsWith(fs.getPath("/"))).isTrue(); + assertThat(fs.getPath("/hi/there").startsWith(fs.getPath(""))).isFalse(); + } + } + + @Test + public void testEndsWith() { + try (CloudStorageFileSystem fs = forBucket("doodle")) { + assertThat(fs.getPath("/hi/there").endsWith(fs.getPath("there"))).isTrue(); + assertThat(fs.getPath("/hi/there").endsWith(fs.getPath("therf"))).isFalse(); + assertThat(fs.getPath("/hi/there").endsWith(fs.getPath("/blag/therf"))).isFalse(); + assertThat(fs.getPath("/hi/there").endsWith(fs.getPath("/hi/there"))).isTrue(); + assertThat(fs.getPath("/hi/there").endsWith(fs.getPath("/there"))).isFalse(); + assertThat(fs.getPath("/human/that/you/cry").endsWith(fs.getPath("that/you/cry"))).isTrue(); + assertThat(fs.getPath("/human/that/you/cry").endsWith(fs.getPath("that/you/cry/"))).isTrue(); + assertThat(fs.getPath("/hi/there/").endsWith(fs.getPath("/"))).isFalse(); + assertThat(fs.getPath("/hi/there").endsWith(fs.getPath(""))).isFalse(); + assertThat(fs.getPath("").endsWith(fs.getPath(""))).isTrue(); + } + } + + /** @see "http://stackoverflow.com/a/10068306" */ + @Test + public void testResolve_willWorkWithRecursiveCopy() throws Exception { + try (FileSystem fsSource = FileSystems.getFileSystem(URI.create("gs://hello")); + FileSystem fsTarget = FileSystems.getFileSystem(URI.create("gs://cat"))) { + Path targetPath = fsTarget.getPath("/some/folder/"); + Path relativeSourcePath = fsSource.getPath("file.txt"); + assertThat((Object) targetPath.resolve(relativeSourcePath)) + .isEqualTo(fsTarget.getPath("/some/folder/file.txt")); + } + } + + /** @see "http://stackoverflow.com/a/10068306" */ + @Test + public void testRelativize_willWorkWithRecursiveCopy() throws Exception { + try (FileSystem fsSource = FileSystems.getFileSystem(URI.create("gs://hello")); + FileSystem fsTarget = FileSystems.getFileSystem(URI.create("gs://cat"))) { + Path targetPath = fsTarget.getPath("/some/folder/"); + Path sourcePath = fsSource.getPath("/sloth/"); + Path file = fsSource.getPath("/sloth/file.txt"); + assertThat((Object) targetPath.resolve(sourcePath.relativize(file))) + .isEqualTo(fsTarget.getPath("/some/folder/file.txt")); + } + } + + @Test + public void testToFile_unsupported() { + try (CloudStorageFileSystem fs = forBucket("doodle")) { + Path path = fs.getPath("/lol"); + thrown.expect(UnsupportedOperationException.class); + path.toFile(); + } + } + + @Test + public void testEquals() { + try (CloudStorageFileSystem fs = forBucket("doodle")) { + new EqualsTester() + // These are obviously equal. + .addEqualityGroup(fs.getPath("/hello/cat"), fs.getPath("/hello/cat")) + // These are equal because equals() runs things through toRealPath() + .addEqualityGroup(fs.getPath("great/commandment"), fs.getPath("/great/commandment")) + .addEqualityGroup(fs.getPath("great/commandment/"), fs.getPath("/great/commandment/")) + // Equals shouldn't do error checking or normalization. + .addEqualityGroup(fs.getPath("foo/../bar"), fs.getPath("foo/../bar")) + .addEqualityGroup(fs.getPath("bar")) + .testEquals(); + } + } + + @Test + public void testEquals_currentDirectoryIsTakenIntoConsideration() { + try (CloudStorageFileSystem fs = forBucket("doodle", workingDirectory("/hello"))) { + new EqualsTester() + .addEqualityGroup(fs.getPath("cat"), fs.getPath("/hello/cat")) + .addEqualityGroup(fs.getPath(""), fs.getPath("/hello")) + .testEquals(); + } + } + + @Test + public void testNullness() { + try (CloudStorageFileSystem fs = forBucket("doodle")) { + NullPointerTester tester = new NullPointerTester() + .setDefault(Path.class, fs.getPath("sup")); + tester.testAllPublicStaticMethods(CloudStoragePath.class); + tester.testAllPublicInstanceMethods(fs.getPath("sup")); + } + } + + private static CloudStorageConfiguration stripPrefixSlash(boolean value) { + return CloudStorageConfiguration.builder() + .stripPrefixSlash(value) + .build(); + } + + private static CloudStorageConfiguration permitEmptyPathComponents(boolean value) { + return CloudStorageConfiguration.builder() + .permitEmptyPathComponents(value) + .build(); + } + + private static CloudStorageConfiguration workingDirectory(String value) { + return CloudStorageConfiguration.builder() + .workingDirectory(value) + .build(); + } +} diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageReadChannelTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageReadChannelTest.java new file mode 100644 index 000000000000..c46ec0ac7981 --- /dev/null +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageReadChannelTest.java @@ -0,0 +1,146 @@ +package com.google.gcloud.storage.contrib.nio; + +import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; + +import com.google.appengine.tools.cloudstorage.GcsFileMetadata; +import com.google.appengine.tools.cloudstorage.GcsFileOptions; +import com.google.appengine.tools.cloudstorage.GcsFilename; +import com.google.appengine.tools.cloudstorage.GcsInputChannel; +import com.google.appengine.tools.cloudstorage.GcsService; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.nio.ByteBuffer; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.NonWritableChannelException; + +/** Unit tests for {@link CloudStorageReadChannel}. */ +@RunWith(JUnit4.class) +public class CloudStorageReadChannelTest { + + @Rule + public final ExpectedException thrown = ExpectedException.none(); + + private final GcsService gcsService = mock(GcsService.class); + private final GcsInputChannel gcsChannel = mock(GcsInputChannel.class); + private final GcsFilename file = new GcsFilename("enya", "rocks"); + private final GcsFileOptions options = GcsFileOptions.getDefaultInstance(); + private final GcsFileMetadata metadata = new GcsFileMetadata(file, options, null, 42, null); + private CloudStorageReadChannel chan; + + @Before + public void before() throws Exception { + when(gcsService.getMetadata(eq(file))).thenReturn(metadata); + when(gcsService.openReadChannel(eq(file), anyInt())).thenReturn(gcsChannel); + when(gcsChannel.isOpen()).thenReturn(true); + chan = CloudStorageReadChannel.create(gcsService, file, 0); + verify(gcsService).getMetadata(eq(file)); + verify(gcsService).openReadChannel(eq(file), eq(0L)); + } + + @Test + public void testRead() throws Exception { + ByteBuffer buffer = ByteBuffer.allocate(1); + when(gcsChannel.read(eq(buffer))).thenReturn(1); + assertThat(chan.position()).isEqualTo(0L); + assertThat(chan.read(buffer)).isEqualTo(1); + assertThat(chan.position()).isEqualTo(1L); + verify(gcsChannel).read(any(ByteBuffer.class)); + verify(gcsChannel, times(3)).isOpen(); + verifyNoMoreInteractions(gcsService, gcsChannel); + } + + @Test + public void testRead_whenClosed_throwsCce() throws Exception { + when(gcsChannel.isOpen()).thenReturn(false); + thrown.expect(ClosedChannelException.class); + chan.read(ByteBuffer.allocate(1)); + } + + @Test + public void testWrite_throwsNonWritableChannelException() throws Exception { + thrown.expect(NonWritableChannelException.class); + chan.write(ByteBuffer.allocate(1)); + } + + @Test + public void testTruncate_throwsNonWritableChannelException() throws Exception { + thrown.expect(NonWritableChannelException.class); + chan.truncate(0); + } + + @Test + public void testIsOpen() throws Exception { + when(gcsChannel.isOpen()).thenReturn(true).thenReturn(false); + assertThat(chan.isOpen()).isTrue(); + chan.close(); + assertThat(chan.isOpen()).isFalse(); + verify(gcsChannel, times(2)).isOpen(); + verify(gcsChannel).close(); + verifyNoMoreInteractions(gcsService, gcsChannel); + } + + @Test + public void testSize() throws Exception { + assertThat(chan.size()).isEqualTo(42L); + verify(gcsChannel).isOpen(); + verifyZeroInteractions(gcsChannel); + verifyNoMoreInteractions(gcsService); + } + + @Test + public void testSize_whenClosed_throwsCce() throws Exception { + when(gcsChannel.isOpen()).thenReturn(false); + thrown.expect(ClosedChannelException.class); + chan.size(); + } + + @Test + public void testPosition_whenClosed_throwsCce() throws Exception { + when(gcsChannel.isOpen()).thenReturn(false); + thrown.expect(ClosedChannelException.class); + chan.position(); + } + + @Test + public void testSetPosition_whenClosed_throwsCce() throws Exception { + when(gcsChannel.isOpen()).thenReturn(false); + thrown.expect(ClosedChannelException.class); + chan.position(0); + } + + @Test + public void testClose_calledMultipleTimes_doesntThrowAnError() throws Exception { + chan.close(); + chan.close(); + chan.close(); + } + + @Test + public void testSetPosition() throws Exception { + assertThat(chan.position()).isEqualTo(0L); + assertThat(chan.size()).isEqualTo(42L); + chan.position(1L); + assertThat(chan.position()).isEqualTo(1L); + assertThat(chan.size()).isEqualTo(42L); + verify(gcsChannel).close(); + verify(gcsChannel, times(5)).isOpen(); + verify(gcsService, times(2)).getMetadata(eq(file)); + verify(gcsService).openReadChannel(eq(file), eq(1L)); + verifyNoMoreInteractions(gcsService, gcsChannel); + } +} diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageWriteChannelTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageWriteChannelTest.java new file mode 100644 index 000000000000..7b0e298b56c0 --- /dev/null +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageWriteChannelTest.java @@ -0,0 +1,107 @@ +package com.google.gcloud.storage.contrib.nio; + +import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; + +import com.google.appengine.tools.cloudstorage.GcsOutputChannel; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.nio.ByteBuffer; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.NonReadableChannelException; + +/** Unit tests for {@link CloudStorageWriteChannel}. */ +@RunWith(JUnit4.class) +public class CloudStorageWriteChannelTest { + + @Rule + public final ExpectedException thrown = ExpectedException.none(); + + private final GcsOutputChannel gcsChannel = mock(GcsOutputChannel.class); + private CloudStorageWriteChannel chan = new CloudStorageWriteChannel(gcsChannel); + + @Before + public void before() throws Exception { + when(gcsChannel.isOpen()).thenReturn(true); + } + + @Test + public void testRead_throwsNonReadableChannelException() throws Exception { + thrown.expect(NonReadableChannelException.class); + chan.read(ByteBuffer.allocate(1)); + } + + @Test + public void testWrite() throws Exception { + ByteBuffer buffer = ByteBuffer.allocate(1); + buffer.put((byte) 'B'); + assertThat(chan.position()).isEqualTo(0L); + assertThat(chan.size()).isEqualTo(0L); + when(gcsChannel.write(eq(buffer))).thenReturn(1); + assertThat(chan.write(buffer)).isEqualTo(1); + assertThat(chan.position()).isEqualTo(1L); + assertThat(chan.size()).isEqualTo(1L); + verify(gcsChannel).write(any(ByteBuffer.class)); + verify(gcsChannel, times(5)).isOpen(); + verifyNoMoreInteractions(gcsChannel); + } + + @Test + public void testWrite_whenClosed_throwsCce() throws Exception { + when(gcsChannel.isOpen()).thenReturn(false); + thrown.expect(ClosedChannelException.class); + chan.write(ByteBuffer.allocate(1)); + } + + @Test + public void testIsOpen() throws Exception { + when(gcsChannel.isOpen()).thenReturn(true).thenReturn(false); + assertThat(chan.isOpen()).isTrue(); + chan.close(); + assertThat(chan.isOpen()).isFalse(); + verify(gcsChannel, times(2)).isOpen(); + verify(gcsChannel).close(); + verifyNoMoreInteractions(gcsChannel); + } + + @Test + public void testSize() throws Exception { + assertThat(chan.size()).isEqualTo(0L); + verify(gcsChannel).isOpen(); + verifyZeroInteractions(gcsChannel); + } + + @Test + public void testSize_whenClosed_throwsCce() throws Exception { + when(gcsChannel.isOpen()).thenReturn(false); + thrown.expect(ClosedChannelException.class); + chan.size(); + } + + @Test + public void testPosition_whenClosed_throwsCce() throws Exception { + when(gcsChannel.isOpen()).thenReturn(false); + thrown.expect(ClosedChannelException.class); + chan.position(); + } + + @Test + public void testClose_calledMultipleTimes_doesntThrowAnError() throws Exception { + chan.close(); + chan.close(); + chan.close(); + } +} diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/UnixPathTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/UnixPathTest.java new file mode 100644 index 000000000000..e6417fc75c8c --- /dev/null +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/UnixPathTest.java @@ -0,0 +1,387 @@ +package com.google.gcloud.storage.contrib.nio; + +import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assume.assumeTrue; + +import com.google.common.testing.EqualsTester; +import com.google.common.testing.NullPointerTester; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Unit tests for {@link UnixPath}. */ +@RunWith(JUnit4.class) +public class UnixPathTest { + + @Rule + public final ExpectedException thrown = ExpectedException.none(); + + @Test + public void testNormalize() { + assertThat(p(".").normalize()).isEqualTo(p("")); + assertThat(p("/").normalize()).isEqualTo(p("/")); + assertThat(p("/.").normalize()).isEqualTo(p("/")); + assertThat(p("/a/b/../c").normalize()).isEqualTo(p("/a/c")); + assertThat(p("/a/b/./c").normalize()).isEqualTo(p("/a/b/c")); + assertThat(p("a/b/../c").normalize()).isEqualTo(p("a/c")); + assertThat(p("a/b/./c").normalize()).isEqualTo(p("a/b/c")); + assertThat(p("/a/b/../../c").normalize()).isEqualTo(p("/c")); + assertThat(p("/a/b/./.././.././c").normalize()).isEqualTo(p("/c")); + } + + @Test + public void testNormalize_empty_returnsEmpty() { + assertThat(p("").normalize()).isEqualTo(p("")); + } + + @Test + public void testNormalize_underflow_isAllowed() { + assertThat(p("../").normalize()).isEqualTo(p("")); + } + + @Test + public void testNormalize_extraSlashes_getRemoved() { + assertThat(p("///").normalize()).isEqualTo(p("/")); + assertThat(p("/hi//there").normalize()).isEqualTo(p("/hi/there")); + assertThat(p("/hi////.///there").normalize()).isEqualTo(p("/hi/there")); + } + + @Test + public void testNormalize_trailingSlash() { + assertThat(p("/hi/there/").normalize()).isEqualTo(p("/hi/there/")); + assertThat(p("/hi/there/../").normalize()).isEqualTo(p("/hi/")); + assertThat(p("/hi/there/..").normalize()).isEqualTo(p("/hi/")); + assertThat(p("hi/../").normalize()).isEqualTo(p("")); + assertThat(p("/hi/../").normalize()).isEqualTo(p("/")); + assertThat(p("hi/..").normalize()).isEqualTo(p("")); + assertThat(p("/hi/..").normalize()).isEqualTo(p("/")); + } + + @Test + public void testNormalize_sameObjectOptimization() { + UnixPath path = p("/hi/there"); + assertThat(path.normalize()).isSameAs(path); + path = p("/hi/there/"); + assertThat(path.normalize()).isSameAs(path); + } + + @Test + public void testResolve() { + assertThat(p("/hello").resolve(p("cat"))).isEqualTo(p("/hello/cat")); + assertThat(p("/hello/").resolve(p("cat"))).isEqualTo(p("/hello/cat")); + assertThat(p("hello/").resolve(p("cat"))).isEqualTo(p("hello/cat")); + assertThat(p("hello/").resolve(p("cat/"))).isEqualTo(p("hello/cat/")); + assertThat(p("hello/").resolve(p(""))).isEqualTo(p("hello/")); + assertThat(p("hello/").resolve(p("/hi/there"))).isEqualTo(p("/hi/there")); + } + + @Test + public void testResolve_sameObjectOptimization() { + UnixPath path = p("/hi/there"); + assertThat(path.resolve(p(""))).isSameAs(path); + assertThat(p("hello").resolve(path)).isSameAs(path); + } + + @Test + public void testGetPath() { + assertThat(UnixPath.getPath(false, "hello")).isEqualTo(p("hello")); + assertThat(UnixPath.getPath(false, "hello", "cat")).isEqualTo(p("hello/cat")); + assertThat(UnixPath.getPath(false, "/hello", "cat")).isEqualTo(p("/hello/cat")); + assertThat(UnixPath.getPath(false, "/hello", "cat", "inc.")) + .isEqualTo(p("/hello/cat/inc.")); + assertThat(UnixPath.getPath(false, "hello/", "/hi/there")).isEqualTo(p("/hi/there")); + } + + @Test + public void testResolveSibling() { + assertThat(p("/hello/cat").resolveSibling(p("dog"))).isEqualTo(p("/hello/dog")); + assertThat(p("/").resolveSibling(p("dog"))).isEqualTo(p("dog")); + } + + @Test + public void testResolveSibling_preservesTrailingSlash() { + assertThat(p("/hello/cat").resolveSibling(p("dog/"))).isEqualTo(p("/hello/dog/")); + assertThat(p("/").resolveSibling(p("dog/"))).isEqualTo(p("dog/")); + } + + @Test + public void testRelativize() { + assertThat(p("/foo/bar/hop/dog").relativize(p("/foo/mop/top"))) + .isEqualTo(p("../../../mop/top")); + assertThat(p("/foo/bar/dog").relativize(p("/foo/mop/top"))) + .isEqualTo(p("../../mop/top")); + assertThat(p("/foo/bar/hop/dog").relativize(p("/foo/mop/top/../../mog"))) + .isEqualTo(p("../../../mop/top/../../mog")); + assertThat(p("/foo/bar/hop/dog").relativize(p("/foo/../mog"))) + .isEqualTo(p("../../../../mog")); + assertThat(p("").relativize(p("foo/mop/top/"))).isEqualTo(p("foo/mop/top/")); + } + + @Test + public void testRelativize_absoluteMismatch_notAllowed() { + thrown.expect(IllegalArgumentException.class); + p("/a/b/").relativize(p("")); + } + + @Test + public void testRelativize_preservesTrailingSlash() { + // This behavior actually diverges from sun.nio.fs.UnixPath: + // bsh % print(Paths.get("/a/b/").relativize(Paths.get("/etc/"))); + // ../../etc + assertThat(p("/foo/bar/hop/dog").relativize(p("/foo/../mog/"))) + .isEqualTo(p("../../../../mog/")); + assertThat(p("/a/b/").relativize(p("/etc/"))).isEqualTo(p("../../etc/")); + } + + @Test + public void testStartsWith() { + assertThat(p("/hi/there").startsWith(p("/hi/there"))).isTrue(); + assertThat(p("/hi/there").startsWith(p("/hi/therf"))).isFalse(); + assertThat(p("/hi/there").startsWith(p("/hi"))).isTrue(); + assertThat(p("/hi/there").startsWith(p("/hi/"))).isTrue(); + assertThat(p("/hi/there").startsWith(p("hi"))).isFalse(); + assertThat(p("/hi/there").startsWith(p("/"))).isTrue(); + assertThat(p("/hi/there").startsWith(p(""))).isFalse(); + assertThat(p("/a/b").startsWith(p("a/b/"))).isFalse(); + assertThat(p("/a/b/").startsWith(p("a/b/"))).isFalse(); + assertThat(p("/hi/there").startsWith(p(""))).isFalse(); + assertThat(p("").startsWith(p(""))).isTrue(); + } + + @Test + public void testStartsWith_comparesComponentsIndividually() { + assertThat(p("/hello").startsWith(p("/hell"))).isFalse(); + assertThat(p("/hello").startsWith(p("/hello"))).isTrue(); + } + + @Test + public void testEndsWith() { + assertThat(p("/hi/there").endsWith(p("there"))).isTrue(); + assertThat(p("/hi/there").endsWith(p("therf"))).isFalse(); + assertThat(p("/hi/there").endsWith(p("/blag/therf"))).isFalse(); + assertThat(p("/hi/there").endsWith(p("/hi/there"))).isTrue(); + assertThat(p("/hi/there").endsWith(p("/there"))).isFalse(); + assertThat(p("/human/that/you/cry").endsWith(p("that/you/cry"))).isTrue(); + assertThat(p("/human/that/you/cry").endsWith(p("that/you/cry/"))).isTrue(); + assertThat(p("/hi/there/").endsWith(p("/"))).isFalse(); + assertThat(p("/hi/there").endsWith(p(""))).isFalse(); + assertThat(p("").endsWith(p(""))).isTrue(); + } + + @Test + public void testEndsWith_comparesComponentsIndividually() { + assertThat(p("/hello").endsWith(p("lo"))).isFalse(); + assertThat(p("/hello").endsWith(p("hello"))).isTrue(); + } + + @Test + public void testGetParent() { + assertThat(p("").getParent()).isNull(); + assertThat(p("/").getParent()).isNull(); + assertThat(p("aaa/").getParent()).isNull(); + assertThat(p("aaa").getParent()).isNull(); + assertThat(p("/aaa/").getParent()).isEqualTo(p("/")); + assertThat(p("a/b/c").getParent()).isEqualTo(p("a/b/")); + assertThat(p("a/b/c/").getParent()).isEqualTo(p("a/b/")); + assertThat(p("a/b/").getParent()).isEqualTo(p("a/")); + } + + @Test + public void testGetRoot() { + assertThat(p("/hello").getRoot()).isEqualTo(p("/")); + assertThat(p("hello").getRoot()).isNull(); + } + + @Test + public void testGetFileName() { + assertThat(p("").getFileName()).isEqualTo(p("")); + assertThat(p("/").getFileName()).isNull(); + assertThat(p("/dark").getFileName()).isEqualTo(p("dark")); + assertThat(p("/angels/").getFileName()).isEqualTo(p("angels")); + } + + @Test + public void testEquals() { + assertThat(p("/a/").equals(p("/a/"))).isTrue(); + assertThat(p("/a/").equals(p("/b/"))).isFalse(); + assertThat(p("/b/").equals(p("/b"))).isFalse(); + assertThat(p("/b").equals(p("/b/"))).isFalse(); + assertThat(p("b").equals(p("/b"))).isFalse(); + assertThat(p("b").equals(p("b"))).isTrue(); + } + + @Test + public void testSplit() { + assertThat(p("").split().hasNext()).isFalse(); + assertThat(p("hi/there").split().hasNext()).isTrue(); + assertThat(p(p("hi/there").split().next())).isEqualTo(p("hi")); + } + + @Test + public void testToAbsolute() { + assertThat(p("lol").toAbsolutePath(UnixPath.ROOT_PATH)).isEqualTo(p("/lol")); + assertThat(p("lol/cat").toAbsolutePath(UnixPath.ROOT_PATH)).isEqualTo(p("/lol/cat")); + } + + @Test + public void testToAbsolute_withCurrentDirectory() { + assertThat(p("cat").toAbsolutePath(p("/lol"))).isEqualTo(p("/lol/cat")); + assertThat(p("cat").toAbsolutePath(p("/lol/"))).isEqualTo(p("/lol/cat")); + assertThat(p("/hi/there").toAbsolutePath(p("/lol"))).isEqualTo(p("/hi/there")); + } + + @Test + public void testToAbsolute_preservesTrailingSlash() { + assertThat(p("cat/").toAbsolutePath(p("/lol"))).isEqualTo(p("/lol/cat/")); + } + + @Test + public void testSubpath() { + assertThat(p("/eins/zwei/drei/vier").subpath(0, 1)).isEqualTo(p("eins")); + assertThat(p("/eins/zwei/drei/vier").subpath(0, 2)).isEqualTo(p("eins/zwei")); + assertThat(p("eins/zwei/drei/vier/").subpath(1, 4)).isEqualTo(p("zwei/drei/vier")); + assertThat(p("eins/zwei/drei/vier/").subpath(2, 4)).isEqualTo(p("drei/vier")); + } + + @Test + public void testSubpath_empty_returnsEmpty() { + assertThat(p("").subpath(0, 1)).isEqualTo(p("")); + } + + @Test + public void testSubpath_root_throwsIae() { + thrown.expect(IllegalArgumentException.class); + p("/").subpath(0, 1); + } + + @Test + public void testSubpath_negativeIndex_throwsIae() { + thrown.expect(IllegalArgumentException.class); + p("/eins/zwei/drei/vier").subpath(-1, 1); + } + + @Test + public void testSubpath_notEnoughElements_throwsIae() { + thrown.expect(IllegalArgumentException.class); + p("/eins/zwei/drei/vier").subpath(0, 5); + } + + @Test + public void testSubpath_beginAboveEnd_throwsIae() { + thrown.expect(IllegalArgumentException.class); + p("/eins/zwei/drei/vier").subpath(1, 0); + } + + @Test + public void testSubpath_beginAndEndEqual_throwsIae() { + thrown.expect(IllegalArgumentException.class); + p("/eins/zwei/drei/vier").subpath(0, 0); + } + + @Test + public void testNameCount() { + assertThat(p("").getNameCount()).isEqualTo(1); + assertThat(p("/").getNameCount()).isEqualTo(0); + assertThat(p("/hi/").getNameCount()).isEqualTo(1); + assertThat(p("/hi/yo").getNameCount()).isEqualTo(2); + assertThat(p("hi/yo").getNameCount()).isEqualTo(2); + } + + @Test + public void testNameCount_dontPermitEmptyComponents_emptiesGetIgnored() { + assertThat(p("hi//yo").getNameCount()).isEqualTo(2); + assertThat(p("//hi//yo//").getNameCount()).isEqualTo(2); + } + + @Test + public void testNameCount_permitEmptyComponents_emptiesGetCounted() { + assertThat(pp("hi//yo").getNameCount()).isEqualTo(3); + assertThat(pp("hi//yo/").getNameCount()).isEqualTo(4); + assertThat(pp("hi//yo//").getNameCount()).isEqualTo(5); + } + + @Test + public void testNameCount_permitEmptyComponents_rootComponentDoesntCount() { + assertThat(pp("hi/yo").getNameCount()).isEqualTo(2); + assertThat(pp("/hi/yo").getNameCount()).isEqualTo(2); + assertThat(pp("//hi/yo").getNameCount()).isEqualTo(3); + } + + @Test + public void testGetName() { + assertThat(p("").getName(0)).isEqualTo(p("")); + assertThat(p("/hi").getName(0)).isEqualTo(p("hi")); + assertThat(p("hi/there").getName(1)).isEqualTo(p("there")); + } + + @Test + public void testCompareTo() { + assertThat(p("/hi/there").compareTo(p("/hi/there"))).isEqualTo(0); + assertThat(p("/hi/there").compareTo(p("/hi/therf"))).isEqualTo(-1); + assertThat(p("/hi/there").compareTo(p("/hi/therd"))).isEqualTo(1); + } + + @Test + public void testCompareTo_dontPermitEmptyComponents_emptiesGetIgnored() { + assertThat(p("a/b").compareTo(p("a//b"))).isEqualTo(0); + } + + @Test + public void testCompareTo_permitEmptyComponents_behaviorChanges() { + assertThat(p("a/b").compareTo(pp("a//b"))).isEqualTo(1); + assertThat(pp("a/b").compareTo(pp("a//b"))).isEqualTo(1); + } + + @Test + public void testCompareTo_comparesComponentsIndividually() { + assumeTrue('.' < '/'); + assertThat("hi./there".compareTo("hi/there")).isEqualTo(-1); + assertThat("hi.".compareTo("hi")).isEqualTo(1); + assertThat(p("hi./there").compareTo(p("hi/there"))).isEqualTo(1); + assertThat(p("hi./there").compareTo(p("hi/there"))).isEqualTo(1); + assumeTrue('0' > '/'); + assertThat("hi0/there".compareTo("hi/there")).isEqualTo(1); + assertThat("hi0".compareTo("hi")).isEqualTo(1); + assertThat(p("hi0/there").compareTo(p("hi/there"))).isEqualTo(1); + } + + @Test + public void testSeemsLikeADirectory() { + assertThat(p("a").seemsLikeADirectory()).isFalse(); + assertThat(p("a.").seemsLikeADirectory()).isFalse(); + assertThat(p("a..").seemsLikeADirectory()).isFalse(); + assertThat(p("").seemsLikeADirectory()).isTrue(); + assertThat(p("/").seemsLikeADirectory()).isTrue(); + assertThat(p(".").seemsLikeADirectory()).isTrue(); + assertThat(p("/.").seemsLikeADirectory()).isTrue(); + assertThat(p("..").seemsLikeADirectory()).isTrue(); + assertThat(p("/..").seemsLikeADirectory()).isTrue(); + } + + @Test + public void testEquals_equalsTester() throws Exception { + new EqualsTester() + .addEqualityGroup(p("/lol"), p("/lol")) + .addEqualityGroup(p("/lol//"), p("/lol//")) + .addEqualityGroup(p("dust")) + .testEquals(); + } + + @Test + public void testNullness() { + NullPointerTester tester = new NullPointerTester(); + tester.testAllPublicStaticMethods(UnixPath.class); + tester.testAllPublicInstanceMethods(p("solo")); + } + + private static UnixPath p(String path) { + return UnixPath.getPath(false, path); + } + + private static UnixPath pp(String path) { + return UnixPath.getPath(true, path); + } +} diff --git a/gcloud-java-contrib/pom.xml b/gcloud-java-contrib/pom.xml index 61a43bd070c3..e24787969ace 100644 --- a/gcloud-java-contrib/pom.xml +++ b/gcloud-java-contrib/pom.xml @@ -2,7 +2,7 @@ 4.0.0 gcloud-java-contrib - jar + pom GCloud Java contributions https://github.com/GoogleCloudPlatform/gcloud-java/tree/master/gcloud-java-contrib @@ -16,6 +16,9 @@ gcloud-java-contrib + + gcloud-java-nio + ${project.groupId} From 045dce208a88b7df5fd0a97d3f12b420d8e0f1d3 Mon Sep 17 00:00:00 2001 From: JP Martin Date: Wed, 2 Mar 2016 14:00:47 -0800 Subject: [PATCH 02/38] Migrated GCS NIO from App Engine to gcloud-storage Also implemented FakeStorageRPC to provide an in-memory GCS implementation for testing (it's not complete yet, but good enough for simple tests). --- gcloud-java-contrib/gcloud-java-nio/pom.xml | 28 +- .../nio/CloudStorageConfiguration.java | 20 +- .../nio/CloudStorageFileAttributeView.java | 35 +- .../nio/CloudStorageFileAttributes.java | 6 +- .../contrib/nio/CloudStorageFileSystem.java | 62 +++- .../nio/CloudStorageFileSystemProvider.java | 320 ++++++++++------ .../nio/CloudStorageObjectAttributes.java | 64 ++-- .../CloudStorageObjectImmutableException.java | 2 +- .../contrib/nio/CloudStorageOption.java | 8 +- .../contrib/nio/CloudStorageOptions.java | 6 +- .../storage/contrib/nio/CloudStoragePath.java | 38 +- ...CloudStoragePseudoDirectoryAttributes.java | 19 +- .../CloudStoragePseudoDirectoryException.java | 2 +- .../contrib/nio/CloudStorageReadChannel.java | 46 ++- .../storage/contrib/nio/CloudStorageUtil.java | 57 +++ .../contrib/nio/CloudStorageWriteChannel.java | 12 +- .../storage/contrib/nio/OptionAcl.java | 7 +- .../storage/contrib/nio/OptionBlockSize.java | 2 +- .../contrib/nio/OptionCacheControl.java | 2 +- .../contrib/nio/OptionContentDisposition.java | 2 +- .../contrib/nio/OptionContentEncoding.java | 2 +- .../storage/contrib/nio/OptionMimeType.java | 2 +- .../contrib/nio/OptionUserMetadata.java | 3 +- .../storage/contrib/nio/UnixPath.java | 4 +- .../storage/contrib/nio/package-info.java | 6 +- .../storage/contrib/nio/CloudStorageUtil.java | 110 ------ .../nio/CloudStorageConfigurationTest.java | 2 +- .../CloudStorageFileAttributeViewTest.java | 14 +- .../nio/CloudStorageFileAttributesTest.java | 49 ++- .../CloudStorageFileSystemProviderTest.java | 156 +++++--- .../nio/CloudStorageFileSystemTest.java | 12 +- .../contrib/nio/CloudStorageOptionsTest.java | 66 ++-- .../contrib/nio/CloudStoragePathTest.java | 37 +- .../nio/CloudStorageReadChannelTest.java | 90 +++-- .../nio/CloudStorageWriteChannelTest.java | 8 +- .../storage/contrib/nio/UnixPathTest.java | 2 +- .../storage/contrib/nio/it/ITGcsNio.java | 351 ++++++++++++++++++ .../storage/contrib/nio/AppEngineRule.java | 25 -- .../com/google/cloud/examples/nio/Stat.java | 104 ++++++ .../cloud/storage/testing/FakeStorageRpc.java | 279 ++++++++++++++ .../cloud/storage/testing/LocalGcsHelper.java | 68 ++++ .../cloud/storage/testing/package-info.java | 19 +- 42 files changed, 1553 insertions(+), 594 deletions(-) rename gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/{gcloud => cloud}/storage/contrib/nio/CloudStorageConfiguration.java (92%) rename gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/{gcloud => cloud}/storage/contrib/nio/CloudStorageFileAttributeView.java (64%) rename gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/{gcloud => cloud}/storage/contrib/nio/CloudStorageFileAttributes.java (92%) rename gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/{gcloud => cloud}/storage/contrib/nio/CloudStorageFileSystem.java (75%) rename gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/{gcloud => cloud}/storage/contrib/nio/CloudStorageFileSystemProvider.java (58%) rename gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/{gcloud => cloud}/storage/contrib/nio/CloudStorageObjectAttributes.java (63%) rename gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/{gcloud => cloud}/storage/contrib/nio/CloudStorageObjectImmutableException.java (85%) rename gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/{gcloud => cloud}/storage/contrib/nio/CloudStorageOption.java (65%) rename gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/{gcloud => cloud}/storage/contrib/nio/CloudStorageOptions.java (94%) rename gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/{gcloud => cloud}/storage/contrib/nio/CloudStoragePath.java (90%) rename gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/{gcloud => cloud}/storage/contrib/nio/CloudStoragePseudoDirectoryAttributes.java (79%) rename gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/{gcloud => cloud}/storage/contrib/nio/CloudStoragePseudoDirectoryException.java (89%) rename gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/{gcloud => cloud}/storage/contrib/nio/CloudStorageReadChannel.java (64%) create mode 100644 gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageUtil.java rename gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/{gcloud => cloud}/storage/contrib/nio/CloudStorageWriteChannel.java (82%) rename gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/{gcloud => cloud}/storage/contrib/nio/OptionAcl.java (54%) rename gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/{gcloud => cloud}/storage/contrib/nio/OptionBlockSize.java (84%) rename gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/{gcloud => cloud}/storage/contrib/nio/OptionCacheControl.java (85%) rename gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/{gcloud => cloud}/storage/contrib/nio/OptionContentDisposition.java (87%) rename gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/{gcloud => cloud}/storage/contrib/nio/OptionContentEncoding.java (86%) rename gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/{gcloud => cloud}/storage/contrib/nio/OptionMimeType.java (84%) rename gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/{gcloud => cloud}/storage/contrib/nio/OptionUserMetadata.java (86%) rename gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/{gcloud => cloud}/storage/contrib/nio/UnixPath.java (99%) rename gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/{gcloud => cloud}/storage/contrib/nio/package-info.java (92%) delete mode 100644 gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageUtil.java rename gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/{gcloud => cloud}/storage/contrib/nio/CloudStorageConfigurationTest.java (97%) rename gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/{gcloud => cloud}/storage/contrib/nio/CloudStorageFileAttributeViewTest.java (89%) rename gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/{gcloud => cloud}/storage/contrib/nio/CloudStorageFileAttributesTest.java (70%) rename gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/{gcloud => cloud}/storage/contrib/nio/CloudStorageFileSystemProviderTest.java (79%) rename gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/{gcloud => cloud}/storage/contrib/nio/CloudStorageFileSystemTest.java (94%) rename gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/{gcloud => cloud}/storage/contrib/nio/CloudStorageOptionsTest.java (52%) rename gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/{gcloud => cloud}/storage/contrib/nio/CloudStoragePathTest.java (94%) rename gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/{gcloud => cloud}/storage/contrib/nio/CloudStorageReadChannelTest.java (56%) rename gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/{gcloud => cloud}/storage/contrib/nio/CloudStorageWriteChannelTest.java (93%) rename gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/{gcloud => cloud}/storage/contrib/nio/UnixPathTest.java (99%) create mode 100644 gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/it/ITGcsNio.java delete mode 100644 gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/AppEngineRule.java create mode 100644 gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/Stat.java create mode 100644 gcloud-java-storage/src/main/java/com/google/cloud/storage/testing/FakeStorageRpc.java create mode 100644 gcloud-java-storage/src/main/java/com/google/cloud/storage/testing/LocalGcsHelper.java diff --git a/gcloud-java-contrib/gcloud-java-nio/pom.xml b/gcloud-java-contrib/gcloud-java-nio/pom.xml index 69d3c5157c96..d8940a720418 100644 --- a/gcloud-java-contrib/gcloud-java-nio/pom.xml +++ b/gcloud-java-contrib/gcloud-java-nio/pom.xml @@ -1,7 +1,6 @@ 4.0.0 - com.google.gcloud gcloud-java-nio jar GCloud Java NIO @@ -9,9 +8,9 @@ FileSystemProvider for Java NIO to access GCS transparently. - com.google.gcloud + com.google.cloud gcloud-java-contrib - 0.1.4-SNAPSHOT + 0.2.5-SNAPSHOT nio @@ -49,11 +48,6 @@ 1.1 provided - - com.google.appengine.tools - appengine-gcs-client - 0.5 - junit junit @@ -77,24 +71,6 @@ mockito-core 1.9.5 - - com.google.appengine - appengine-testing - 1.9.30 - test - - - com.google.appengine - appengine-api-stubs - 1.9.30 - test - - - com.google.appengine - appengine-local-endpoints - 1.9.30 - test - diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageConfiguration.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageConfiguration.java similarity index 92% rename from gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageConfiguration.java rename to gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageConfiguration.java index e939edfb5a19..0fbe7dff4c93 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageConfiguration.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageConfiguration.java @@ -1,4 +1,4 @@ -package com.google.gcloud.storage.contrib.nio; +package com.google.cloud.storage.contrib.nio; import static com.google.common.base.Preconditions.checkArgument; @@ -6,11 +6,13 @@ import java.util.Map; -/** Configuration class for {@link CloudStorageFileSystem#forBucket} */ +/** CloudStorageConfiguration is the configuration class for + * {@link CloudStorageFileSystem#forBucket}. */ @AutoValue public abstract class CloudStorageConfiguration { - /** Returns the path of the current working directory. Defaults to the root directory. */ + /** Returns the path of the current working directory. Defaults to the root directory. + */ public abstract String workingDirectory(); /** @@ -46,7 +48,8 @@ public static Builder builder() { return new Builder(); } - /** Builder for {@link CloudStorageConfiguration}. */ + /** Builder for {@link CloudStorageConfiguration}. + */ public static final class Builder { private String workingDirectory = UnixPath.ROOT; @@ -87,7 +90,8 @@ public Builder stripPrefixSlash(boolean value) { return this; } - /** Configures if paths with a trailing slash should be treated as fake directories. */ + /** Configures if paths with a trailing slash should be treated as fake directories. + */ public Builder usePseudoDirectories(boolean value) { usePseudoDirectories = value; return this; @@ -103,7 +107,9 @@ public Builder blockSize(int value) { return this; } - /** Creates a new instance, but does not destroy the builder. */ + + /** Creates a new instance, but does not destroy the builder. + */ public CloudStorageConfiguration build() { return new AutoValue_CloudStorageConfiguration( workingDirectory, @@ -116,7 +122,7 @@ public CloudStorageConfiguration build() { Builder() {} } - static final CloudStorageConfiguration DEFAULT = builder().build(); + public static final CloudStorageConfiguration DEFAULT = builder().build(); static CloudStorageConfiguration fromMap(Map env) { Builder builder = builder(); diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileAttributeView.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributeView.java similarity index 64% rename from gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileAttributeView.java rename to gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributeView.java index 5a4d4bc7c28c..526122e45ef6 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileAttributeView.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributeView.java @@ -1,8 +1,9 @@ -package com.google.gcloud.storage.contrib.nio; +package com.google.cloud.storage.contrib.nio; import static com.google.common.base.Verify.verifyNotNull; -import com.google.appengine.tools.cloudstorage.GcsFileMetadata; +import com.google.cloud.storage.BlobInfo; +import com.google.cloud.storage.Storage; import com.google.common.base.MoreObjects; import java.io.IOException; @@ -14,19 +15,22 @@ import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; -/** Metadata view for a Google Cloud Storage object. */ +/** Metadata view for a Google Cloud Storage object. + */ @Immutable public final class CloudStorageFileAttributeView implements BasicFileAttributeView { - private final CloudStorageFileSystemProvider provider; + //private final CloudStorageFileSystemProvider provider; + private final Storage storage; private final CloudStoragePath path; - CloudStorageFileAttributeView(CloudStorageFileSystemProvider provider, CloudStoragePath path) { - this.provider = verifyNotNull(provider); + CloudStorageFileAttributeView(Storage storage, CloudStoragePath path) { + this.storage = verifyNotNull(storage); this.path = verifyNotNull(path); } - /** Returns {@value CloudStorageFileSystem#GCS_VIEW} */ + /** Returns {@value CloudStorageFileSystem#GCS_VIEW}. + */ @Override public String name() { return CloudStorageFileSystem.GCS_VIEW; @@ -36,19 +40,18 @@ public String name() { public CloudStorageFileAttributes readAttributes() throws IOException { if (path.seemsLikeADirectory() && path.getFileSystem().config().usePseudoDirectories()) { - return CloudStoragePseudoDirectoryAttributes.SINGLETON_INSTANCE; + return new CloudStoragePseudoDirectoryAttributes(path); } - GcsFileMetadata metadata = provider.getGcsService().getMetadata(path.getGcsFilename()); - if (metadata == null) { + BlobInfo blobInfo = storage.get(path.getBlobId()); + if (blobInfo == null) { throw new NoSuchFileException(path.toUri().toString()); } - return new CloudStorageObjectAttributes(metadata); + + return new CloudStorageObjectAttributes(blobInfo); } /** * This feature is not supported, since Cloud Storage objects are immutable. - * - * @throws UnsupportedOperationException */ @Override public void setTimes(FileTime lastModifiedTime, FileTime lastAccessTime, FileTime createTime) { @@ -59,19 +62,19 @@ public void setTimes(FileTime lastModifiedTime, FileTime lastAccessTime, FileTim public boolean equals(@Nullable Object other) { return this == other || other instanceof CloudStorageFileAttributeView - && Objects.equals(provider, ((CloudStorageFileAttributeView) other).provider) + && Objects.equals(storage, ((CloudStorageFileAttributeView) other).storage) && Objects.equals(path, ((CloudStorageFileAttributeView) other).path); } @Override public int hashCode() { - return Objects.hash(provider, path); + return Objects.hash(storage, path); } @Override public String toString() { return MoreObjects.toStringHelper(this) - .add("provider", provider) + .add("storage", storage) .add("path", path) .toString(); } diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileAttributes.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributes.java similarity index 92% rename from gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileAttributes.java rename to gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributes.java index 774a5499431d..a4af002f9223 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileAttributes.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributes.java @@ -1,9 +1,11 @@ -package com.google.gcloud.storage.contrib.nio; +package com.google.cloud.storage.contrib.nio; +import com.google.cloud.storage.Acl; import com.google.common.base.Optional; import com.google.common.collect.ImmutableMap; import java.nio.file.attribute.BasicFileAttributes; +import java.util.List; /** Interface for attributes on a cloud storage file or pseudo-directory. */ public interface CloudStorageFileAttributes extends BasicFileAttributes { @@ -27,7 +29,7 @@ public interface CloudStorageFileAttributes extends BasicFileAttributes { * * @see "https://developers.google.com/storage/docs/reference-headers#acl" */ - Optional acl(); + Optional> acl(); /** * Returns the {@code Cache-Control} HTTP header value, if set on this object. diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystem.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystem.java similarity index 75% rename from gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystem.java rename to gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystem.java index b4318be63f31..cceab822d7f9 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystem.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystem.java @@ -1,8 +1,9 @@ -package com.google.gcloud.storage.contrib.nio; +package com.google.cloud.storage.contrib.nio; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; +import com.google.cloud.storage.StorageOptions; import com.google.common.collect.ImmutableSet; import java.io.IOException; @@ -18,6 +19,7 @@ import java.util.Objects; import java.util.Set; +import javax.annotation.CheckReturnValue; import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; @@ -63,6 +65,29 @@ public static CloudStorageFileSystem forBucket(String bucket, CloudStorageConfig new CloudStorageFileSystemProvider(), bucket, checkNotNull(config)); } + /** + * Returns Google Cloud Storage {@link FileSystem} object for {@code bucket}. + * + *

GCS file system objects are basically free. You can create as many as you want, even if you + * have multiple instances for the same bucket. There's no actual system resources associated + * with this object. Therefore calling {@link #close()} on the returned value is optional. + * + *

Note: It is also possible to instantiate this class via Java's + * {@code FileSystems.getFileSystem(URI.create("gs://bucket"))}. We discourage you + * from using that if possible, for the reasons documented in + * {@link CloudStorageFileSystemProvider#newFileSystem(URI, java.util.Map)} + * + * @see java.nio.file.FileSystems#getFileSystem(URI) + */ + @CheckReturnValue + public static CloudStorageFileSystem forBucket(String bucket, CloudStorageConfiguration config, + @Nullable StorageOptions storageOptions) { + checkArgument(!bucket.startsWith(URI_SCHEME + ":"), + "Bucket name must not have schema: %s", bucket); + return new CloudStorageFileSystem(new CloudStorageFileSystemProvider(storageOptions), + bucket, checkNotNull(config)); + } + public static final String URI_SCHEME = "gs"; public static final String GCS_VIEW = "gcs"; public static final String BASIC_VIEW = "basic"; @@ -89,17 +114,20 @@ public CloudStorageFileSystemProvider provider() { return provider; } - /** Returns the Cloud Storage bucket name being served by this file system. */ + /** Returns the Cloud Storage bucket name being served by this file system. + */ public String bucket() { return bucket; } - /** Returns the configuration object for this filesystem instance. */ + /** Returns the configuration object for this filesystem instance. + */ public CloudStorageConfiguration config() { return config; } - /** Converts a cloud storage object name to a {@link Path} object. */ + /** Converts a cloud storage object name to a {@link Path} object. + */ @Override public CloudStoragePath getPath(String first, String... more) { checkArgument(!first.startsWith(URI_SCHEME + ":"), @@ -107,23 +135,27 @@ public CloudStoragePath getPath(String first, String... more) { return CloudStoragePath.getPath(this, first, more); } - /** Does nothing. */ + /** Does nothing. + */ @Override public void close() {} - /** Returns {@code true} */ + /** Returns {@code true}. + */ @Override public boolean isOpen() { return true; } - /** Returns {@code false} */ + /** Returns {@code false}. + */ @Override public boolean isReadOnly() { return false; } - /** Returns {@value UnixPath#SEPARATOR} */ + /** Returns {@value UnixPath#SEPARATOR}. + */ @Override public String getSeparator() { return "" + UnixPath.SEPARATOR; @@ -144,24 +176,26 @@ public Set supportedFileAttributeViews() { return SUPPORTED_VIEWS; } - /** @throws UnsupportedOperationException */ + /** Always throws {@link UnsupportedOperationException}. */ @Override public PathMatcher getPathMatcher(String syntaxAndPattern) { - // TODO(b/18997520): Implement me. + // TODO: Implement me. throw new UnsupportedOperationException(); } - /** @throws UnsupportedOperationException */ + /** Always throws {@link UnsupportedOperationException}. + */ @Override public UserPrincipalLookupService getUserPrincipalLookupService() { - // TODO(b/18997520): Implement me. + // TODO: Implement me. throw new UnsupportedOperationException(); } - /** @throws UnsupportedOperationException */ + /** Always throws {@link UnsupportedOperationException}. + */ @Override public WatchService newWatchService() throws IOException { - // TODO(b/18997520): Implement me. + // TODO: Implement me. throw new UnsupportedOperationException(); } diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystemProvider.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProvider.java similarity index 58% rename from gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystemProvider.java rename to gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProvider.java index f1964b231ed1..bf6b1e2657a0 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystemProvider.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProvider.java @@ -1,34 +1,30 @@ -package com.google.gcloud.storage.contrib.nio; +package com.google.cloud.storage.contrib.nio; +import static com.google.cloud.storage.contrib.nio.CloudStorageUtil.checkBucket; +import static com.google.cloud.storage.contrib.nio.CloudStorageUtil.checkNotNullArray; +import static com.google.cloud.storage.contrib.nio.CloudStorageUtil.checkPath; +import static com.google.cloud.storage.contrib.nio.CloudStorageUtil.stripPathFromUri; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Strings.isNullOrEmpty; -import static com.google.common.base.Suppliers.memoize; -import static com.google.gcloud.storage.contrib.nio.CloudStorageFileSystem.URI_SCHEME; -import static com.google.gcloud.storage.contrib.nio.CloudStorageUtil.buildFileOptions; -import static com.google.gcloud.storage.contrib.nio.CloudStorageUtil.checkBucket; -import static com.google.gcloud.storage.contrib.nio.CloudStorageUtil.checkNotNullArray; -import static com.google.gcloud.storage.contrib.nio.CloudStorageUtil.checkPath; -import static com.google.gcloud.storage.contrib.nio.CloudStorageUtil.copyFileOptions; -import static com.google.gcloud.storage.contrib.nio.CloudStorageUtil.stripPathFromUri; - -import com.google.appengine.tools.cloudstorage.GcsFileMetadata; -import com.google.appengine.tools.cloudstorage.GcsFileOptions; -import com.google.appengine.tools.cloudstorage.GcsFilename; -import com.google.appengine.tools.cloudstorage.GcsInputChannel; -import com.google.appengine.tools.cloudstorage.GcsOutputChannel; -import com.google.appengine.tools.cloudstorage.GcsService; -import com.google.appengine.tools.cloudstorage.GcsServiceFactory; + import com.google.auto.service.AutoService; +import com.google.cloud.storage.Acl; +import com.google.cloud.storage.BlobId; +import com.google.cloud.storage.BlobInfo; +import com.google.cloud.storage.CopyWriter; +import com.google.cloud.storage.Storage; +import com.google.cloud.storage.StorageException; +import com.google.cloud.storage.StorageOptions; +import com.google.common.annotations.VisibleForTesting; import com.google.common.base.MoreObjects; -import com.google.common.base.Supplier; +import com.google.common.base.Throwables; import com.google.common.primitives.Ints; import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; import java.net.URI; -import java.nio.ByteBuffer; import java.nio.channels.SeekableByteChannel; import java.nio.file.AccessMode; import java.nio.file.AtomicMoveNotSupportedException; @@ -48,7 +44,10 @@ import java.nio.file.attribute.FileAttribute; import java.nio.file.attribute.FileAttributeView; import java.nio.file.spi.FileSystemProvider; +import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; @@ -56,19 +55,23 @@ import javax.annotation.Nullable; import javax.annotation.concurrent.ThreadSafe; -/** Google Cloud Storage {@link FileSystemProvider} */ +/** + * Google Cloud Storage {@link FileSystemProvider}. + */ @ThreadSafe @AutoService(FileSystemProvider.class) public final class CloudStorageFileSystemProvider extends FileSystemProvider { - private static final Supplier gcsServiceSupplier = - memoize(new Supplier() { - @Override - public GcsService get() { - return GcsServiceFactory.createGcsService(); - }}); + private final Storage storage; + + // used only when we create a new instance of CloudStorageFileSystemProvider. + private static StorageOptions storageOptions; - private final GcsService gcsService; + /** Those options are only used by the CloudStorageFileSystemProvider ctor. */ + @VisibleForTesting + public static void setGCloudOptions(StorageOptions newStorageOptions) { + storageOptions = newStorageOptions; + } /** * Default constructor which should only be called by Java SPI. @@ -77,20 +80,20 @@ public GcsService get() { * @see CloudStorageFileSystem#forBucket(String) */ public CloudStorageFileSystemProvider() { - this(gcsServiceSupplier.get()); - } - - private CloudStorageFileSystemProvider(GcsService gcsService) { - this.gcsService = checkNotNull(gcsService); + this(storageOptions); } - GcsService getGcsService() { - return gcsService; + CloudStorageFileSystemProvider(@Nullable StorageOptions gcsStorageOptions) { + if (gcsStorageOptions == null) { + this.storage = StorageOptions.defaultInstance().service(); + } else { + this.storage = gcsStorageOptions.service(); + } } @Override public String getScheme() { - return URI_SCHEME; + return CloudStorageFileSystem.URI_SCHEME; } /** Returns cloud storage file system, provided a URI with no path, e.g. {@code gs://bucket} */ @@ -102,10 +105,10 @@ public CloudStorageFileSystem getFileSystem(URI uri) { /** Returns cloud storage file system, provided a URI with no path, e.g. {@code gs://bucket} */ @Override public CloudStorageFileSystem newFileSystem(URI uri, Map env) { - checkArgument(uri.getScheme().equalsIgnoreCase(URI_SCHEME), - "Cloud Storage URIs must have '%s' scheme: %s", URI_SCHEME, uri); + checkArgument(uri.getScheme().equalsIgnoreCase(CloudStorageFileSystem.URI_SCHEME), + "Cloud Storage URIs must have '%s' scheme: %s", CloudStorageFileSystem.URI_SCHEME, uri); checkArgument(!isNullOrEmpty(uri.getHost()), - "%s:// URIs must have a host: %s", URI_SCHEME, uri); + "%s:// URIs must have a host: %s", CloudStorageFileSystem.URI_SCHEME, uri); checkArgument(uri.getPort() == -1 && isNullOrEmpty(uri.getPath()) && isNullOrEmpty(uri.getQuery()) @@ -127,7 +130,7 @@ public SeekableByteChannel newByteChannel( checkNotNull(path); checkNotNullArray(attrs); if (options.contains(StandardOpenOption.WRITE)) { - // TODO(b/18997618): Make our OpenOptions implement FileAttribute. Also remove buffer option. + // TODO: Make our OpenOptions implement FileAttribute. Also remove buffer option. return newWriteChannel(path, options); } else { return newReadChannel(path, options); @@ -165,14 +168,40 @@ private SeekableByteChannel newReadChannel( if (cloudPath.seemsLikeADirectoryAndUsePseudoDirectories()) { throw new CloudStoragePseudoDirectoryException(cloudPath); } - return CloudStorageReadChannel.create(gcsService, cloudPath.getGcsFilename(), 0); + return CloudStorageReadChannel.create(storage, cloudPath.getBlobId(), 0); } private SeekableByteChannel newWriteChannel( Path path, Set options) throws IOException { - boolean wantCreateNew = false; + + CloudStoragePath cloudPath = checkPath(path); + if (cloudPath.seemsLikeADirectoryAndUsePseudoDirectories()) { + throw new CloudStoragePseudoDirectoryException(cloudPath); + } + BlobId file = cloudPath.getBlobId(); + BlobInfo.Builder infoBuilder = BlobInfo.builder(file); + List writeOptions = new ArrayList<>(); + List acls = new ArrayList<>(); + + + HashMap metas = new HashMap<>(); for (OpenOption option : options) { - if (option instanceof StandardOpenOption) { + if (option instanceof OptionMimeType) { + infoBuilder.contentType(((OptionMimeType) option).mimeType()); + } else if (option instanceof OptionCacheControl) { + infoBuilder.cacheControl(((OptionCacheControl) option).cacheControl()); + } else if (option instanceof OptionContentDisposition) { + infoBuilder.contentDisposition(((OptionContentDisposition) option).contentDisposition()); + } else if (option instanceof OptionContentEncoding) { + infoBuilder.contentEncoding(((OptionContentEncoding) option).contentEncoding()); + } else if (option instanceof OptionUserMetadata) { + OptionUserMetadata opMeta = (OptionUserMetadata) option; + metas.put(opMeta.key(), opMeta.value()); + } else if (option instanceof OptionAcl) { + acls.add(((OptionAcl) option).acl()); + } else if (option instanceof OptionBlockSize) { + // TODO: figure out how to plumb in block size. + } else if (option instanceof StandardOpenOption) { switch ((StandardOpenOption) option) { case CREATE: case TRUNCATE_EXISTING: @@ -183,7 +212,7 @@ private SeekableByteChannel newWriteChannel( // Ignored by specification. break; case CREATE_NEW: - wantCreateNew = true; + writeOptions.add(Storage.BlobWriteOption.doesNotExist()); break; case READ: throw new IllegalArgumentException("READ+WRITE not supported yet"); @@ -195,24 +224,25 @@ private SeekableByteChannel newWriteChannel( throw new UnsupportedOperationException(option.toString()); } } else if (option instanceof CloudStorageOption) { - // These will be interpreted later. + // XXX: We need to interpret these later } else { throw new UnsupportedOperationException(option.toString()); } } - CloudStoragePath cloudPath = checkPath(path); - if (cloudPath.seemsLikeADirectoryAndUsePseudoDirectories()) { - throw new CloudStoragePseudoDirectoryException(cloudPath); + + if (!metas.isEmpty()) { + infoBuilder.metadata(metas); } - if (wantCreateNew) { - // XXX: Java's documentation says this should be atomic. - if (gcsService.getMetadata(cloudPath.getGcsFilename()) != null) { - throw new FileAlreadyExistsException(cloudPath.toString()); - } + if (!acls.isEmpty()) { + infoBuilder.acl(acls); + } + + try { + return new CloudStorageWriteChannel(storage.writer(infoBuilder.build(), + writeOptions.toArray(new Storage.BlobWriteOption[0]))); + } catch (StorageException oops) { + throw asIOException(oops); } - GcsFilename file = cloudPath.getGcsFilename(); - GcsFileOptions fileOptions = buildFileOptions(new GcsFileOptions.Builder(), options.toArray()); - return new CloudStorageWriteChannel(gcsService.createOrReplace(file, fileOptions)); } @Override @@ -229,12 +259,12 @@ public InputStream newInputStream(Path path, OpenOption... options) throws IOExc } @Override - public final boolean deleteIfExists(Path path) throws IOException { + public boolean deleteIfExists(Path path) throws IOException { CloudStoragePath cloudPath = checkPath(path); if (cloudPath.seemsLikeADirectoryAndUsePseudoDirectories()) { throw new CloudStoragePseudoDirectoryException(cloudPath); } - return gcsService.delete(cloudPath.getGcsFilename()); + return storage.delete(cloudPath.getBlobId()); } @Override @@ -261,6 +291,14 @@ public void move(Path source, Path target, CopyOption... options) throws IOExcep public void copy(Path source, Path target, CopyOption... options) throws IOException { boolean wantCopyAttributes = false; boolean wantReplaceExisting = false; + boolean setContentType = false; + boolean setCacheControl = false; + boolean setContentEncoding = false; + boolean setContentDisposition = false; + + CloudStoragePath toPath = checkPath(target); + BlobInfo.Builder tgtInfoBuilder = BlobInfo.builder(toPath.getBlobId()).contentType(""); + int blockSize = -1; for (CopyOption option : options) { if (option instanceof StandardCopyOption) { @@ -278,18 +316,34 @@ public void copy(Path source, Path target, CopyOption... options) throws IOExcep } else if (option instanceof CloudStorageOption) { if (option instanceof OptionBlockSize) { blockSize = ((OptionBlockSize) option).size(); + } else if (option instanceof OptionMimeType) { + tgtInfoBuilder.contentType(((OptionMimeType) option).mimeType()); + setContentType = true; + } else if (option instanceof OptionCacheControl) { + tgtInfoBuilder.cacheControl(((OptionCacheControl) option).cacheControl()); + setCacheControl = true; + } else if (option instanceof OptionContentEncoding) { + tgtInfoBuilder.contentEncoding(((OptionContentEncoding) option).contentEncoding()); + setContentEncoding = true; + } else if (option instanceof OptionContentDisposition) { + tgtInfoBuilder.contentDisposition(((OptionContentDisposition) option) + .contentDisposition()); + setContentDisposition = true; + } else { + throw new UnsupportedOperationException(option.toString()); } - // The rest will be interpreted later. } else { throw new UnsupportedOperationException(option.toString()); } } + CloudStoragePath fromPath = checkPath(source); - CloudStoragePath toPath = checkPath(target); blockSize = blockSize != -1 ? blockSize : Ints.max(fromPath.getFileSystem().config().blockSize(), toPath.getFileSystem().config().blockSize()); + // TODO: actually use blockSize + if (fromPath.seemsLikeADirectory() && toPath.seemsLikeADirectory()) { if (fromPath.getFileSystem().config().usePseudoDirectories() && toPath.getFileSystem().config().usePseudoDirectories()) { @@ -307,41 +361,54 @@ public void copy(Path source, Path target, CopyOption... options) throws IOExcep if (toPath.seemsLikeADirectoryAndUsePseudoDirectories()) { throw new CloudStoragePseudoDirectoryException(toPath); } - GcsFilename from = fromPath.getGcsFilename(); - GcsFilename to = toPath.getGcsFilename(); - GcsFileMetadata metadata = gcsService.getMetadata(from); - if (metadata == null) { - throw new NoSuchFileException(source.toString()); - } - if (fromPath.equals(toPath)) { - return; - } - if (!wantReplaceExisting && gcsService.getMetadata(to) != null) { - throw new FileAlreadyExistsException(target.toString()); - } - GcsFileOptions.Builder builder = wantCopyAttributes - ? copyFileOptions(metadata.getOptions()) - : new GcsFileOptions.Builder(); - GcsFileOptions fileOptions = buildFileOptions(builder, options); - try (GcsInputChannel input = gcsService.openReadChannel(from, 0); - GcsOutputChannel output = gcsService.createOrReplace(to, fileOptions)) { - ByteBuffer block = ByteBuffer.allocate(blockSize); - while (input.read(block) != -1) { - block.flip(); - while (block.hasRemaining()) { - output.write(block); + + try { + + + if (wantCopyAttributes) { + BlobInfo blobInfo = storage.get(fromPath.getBlobId()); + if (null == blobInfo) { + throw new NoSuchFileException(fromPath.toString()); + } + if (!setCacheControl) { + tgtInfoBuilder.cacheControl(blobInfo.cacheControl()); + } + if (!setContentType) { + tgtInfoBuilder.contentType(blobInfo.contentType()); } - block.clear(); + if (!setContentEncoding) { + tgtInfoBuilder.contentEncoding(blobInfo.contentEncoding()); + } + if (!setContentDisposition) { + tgtInfoBuilder.contentDisposition(blobInfo.contentDisposition()); + } + tgtInfoBuilder.acl(blobInfo.acl()); + tgtInfoBuilder.metadata(blobInfo.metadata()); + } + + BlobInfo tgtInfo = tgtInfoBuilder.build(); + Storage.CopyRequest.Builder copyReqBuilder = Storage.CopyRequest.builder() + .source(fromPath.getBlobId()); + if (wantReplaceExisting) { + copyReqBuilder = copyReqBuilder.target(tgtInfo); + } else { + copyReqBuilder = copyReqBuilder.target(tgtInfo, Storage.BlobTargetOption.doesNotExist()); } + CopyWriter copyWriter = storage.copy(copyReqBuilder.build()); + copyWriter.result(); + } catch (StorageException oops) { + throw asIOException(oops); } } + @Override public boolean isSameFile(Path path, Path path2) { return checkPath(path).equals(checkPath(path2)); } - /** Returns {@code false} */ + /** Returns {@code false}. + */ @Override public boolean isHidden(Path path) { checkPath(path); @@ -364,7 +431,8 @@ public void checkAccess(Path path, AccessMode... modes) throws IOException { if (cloudPath.seemsLikeADirectoryAndUsePseudoDirectories()) { return; } - if (gcsService.getMetadata(cloudPath.getGcsFilename()) == null) { + if (storage.get(cloudPath.getBlobId(), Storage.BlobGetOption.fields(Storage.BlobField.ID)) + == null) { throw new NoSuchFileException(path.toString()); } } @@ -372,68 +440,71 @@ public void checkAccess(Path path, AccessMode... modes) throws IOException { @Override public A readAttributes( Path path, Class type, LinkOption... options) throws IOException { - CloudStoragePath cloudPath = checkPath(path); checkNotNull(type); checkNotNullArray(options); if (type != CloudStorageFileAttributes.class && type != BasicFileAttributes.class) { throw new UnsupportedOperationException(type.getSimpleName()); } + CloudStoragePath cloudPath = checkPath(path); if (cloudPath.seemsLikeADirectoryAndUsePseudoDirectories()) { @SuppressWarnings("unchecked") - A result = (A) CloudStoragePseudoDirectoryAttributes.SINGLETON_INSTANCE; + A result = (A) new CloudStoragePseudoDirectoryAttributes(cloudPath); return result; } - GcsFileMetadata metadata = gcsService.getMetadata(cloudPath.getGcsFilename()); - if (metadata == null) { - throw new NoSuchFileException(path.toString()); + BlobInfo blobInfo = storage.get(cloudPath.getBlobId()); + // null size indicate a file that we haven't closed yet, so GCS treats it as not there yet. + if (null == blobInfo || blobInfo.size() == null) { + throw new NoSuchFileException( + cloudPath.getBlobId().bucket() + "/" + cloudPath.getBlobId().name()); } + CloudStorageObjectAttributes ret; + ret = new CloudStorageObjectAttributes(blobInfo); @SuppressWarnings("unchecked") - A result = (A) new CloudStorageObjectAttributes(metadata); + A result = (A) ret; return result; } + @Override + public Map readAttributes(Path path, String attributes, LinkOption... options) { + // Java 7 NIO defines at least eleven string attributes we'd want to support + // (eg. BasicFileAttributeView and PosixFileAttributeView), so rather than a partial + // implementation we rely on the other overload for now. + throw new UnsupportedOperationException(); + } + @Override public V getFileAttributeView( Path path, Class type, LinkOption... options) { - CloudStoragePath cloudPath = checkPath(path); checkNotNull(type); checkNotNullArray(options); if (type != CloudStorageFileAttributeView.class && type != BasicFileAttributeView.class) { throw new UnsupportedOperationException(type.getSimpleName()); } + CloudStoragePath cloudPath = checkPath(path); @SuppressWarnings("unchecked") - V result = (V) new CloudStorageFileAttributeView(this, cloudPath); + V result = (V) new CloudStorageFileAttributeView(storage, cloudPath); return result; } - /** Does nothing since GCS uses fake directories. */ + /** Does nothing since GCS uses fake directories. + */ @Override public void createDirectory(Path dir, FileAttribute... attrs) { checkPath(dir); checkNotNullArray(attrs); } - /** @throws UnsupportedOperationException */ - @Override - public DirectoryStream newDirectoryStream(Path dir, Filter filter) { - // TODO(b/18997618): Implement me. - throw new UnsupportedOperationException(); - } - - /** - * This feature is not supported. Please use {@link #readAttributes(Path, Class, LinkOption...)} - * - * @throws UnsupportedOperationException + /** Always @throws UnsupportedOperationException. */ @Override - public Map readAttributes(Path path, String attributes, LinkOption... options) { + public DirectoryStream newDirectoryStream(Path dir, Filter filter) { + // TODO: Implement me. throw new UnsupportedOperationException(); } /** * This feature is not supported, since Cloud Storage objects are immutable. - * - * @throws UnsupportedOperationException + * Always @throws UnsupportedOperationException. */ @Override public void setAttribute(Path path, String attribute, Object value, LinkOption... options) { @@ -442,8 +513,7 @@ public void setAttribute(Path path, String attribute, Object value, LinkOption.. /** * This feature is not supported. - * - * @throws UnsupportedOperationException + * Always @throws UnsupportedOperationException. */ @Override public FileStore getFileStore(Path path) { @@ -454,18 +524,42 @@ public FileStore getFileStore(Path path) { public boolean equals(@Nullable Object other) { return this == other || other instanceof CloudStorageFileSystemProvider - && Objects.equals(gcsService, ((CloudStorageFileSystemProvider) other).gcsService); + && Objects.equals(storage, ((CloudStorageFileSystemProvider) other).storage); } @Override public int hashCode() { - return Objects.hash(gcsService); + return Objects.hash(storage); } @Override public String toString() { return MoreObjects.toStringHelper(this) - .add("gcsService", gcsService) + .add("storage", storage) .toString(); } + + + + private IOException asIOException(StorageException oops) { + if (oops.code() == 404) { + return new NoSuchFileException(oops.reason()); + } + // TODO: research if other codes should be translated to IOException. + + // RPC API can only throw StorageException, but CloudStorageFileSystemProvider + // can only throw IOException. Square peg, round hole. + Throwable cause = oops.getCause(); + try { + if (cause instanceof FileAlreadyExistsException) { + throw new FileAlreadyExistsException(((FileAlreadyExistsException) cause).getReason()); + } + // fallback + Throwables.propagateIfInstanceOf(oops.getCause(), IOException.class); + } catch (IOException okEx) { + return okEx; + } + return new IOException(oops.getMessage(), oops); + } + } diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageObjectAttributes.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageObjectAttributes.java similarity index 63% rename from gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageObjectAttributes.java rename to gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageObjectAttributes.java index b3265cd6276b..651aa6a33d3b 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageObjectAttributes.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageObjectAttributes.java @@ -1,40 +1,45 @@ -package com.google.gcloud.storage.contrib.nio; +package com.google.cloud.storage.contrib.nio; import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.gcloud.storage.contrib.nio.CloudStorageFileSystem.FILE_TIME_UNKNOWN; -import com.google.appengine.tools.cloudstorage.GcsFileMetadata; +import com.google.cloud.storage.Acl; +import com.google.cloud.storage.BlobInfo; import com.google.common.base.MoreObjects; import com.google.common.base.Optional; import com.google.common.collect.ImmutableMap; import java.nio.file.attribute.FileTime; +import java.util.List; import java.util.Objects; +import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; -/** Metadata for a Google Cloud Storage object. */ +/** + * Metadata for a Google Cloud Storage object. + */ @Immutable final class CloudStorageObjectAttributes implements CloudStorageFileAttributes { - private final GcsFileMetadata metadata; + @Nonnull + private final BlobInfo info; - CloudStorageObjectAttributes(GcsFileMetadata metadata) { - this.metadata = checkNotNull(metadata); + CloudStorageObjectAttributes(BlobInfo info) { + this.info = checkNotNull(info); } @Override public long size() { - return metadata.getLength(); + return info.size(); } @Override public FileTime creationTime() { - if (metadata.getLastModified() == null) { - return FILE_TIME_UNKNOWN; + if (info.updateTime() == null) { + return CloudStorageFileSystem.FILE_TIME_UNKNOWN; } - return FileTime.fromMillis(metadata.getLastModified().getTime()); + return FileTime.fromMillis(info.updateTime()); } @Override @@ -42,16 +47,20 @@ public FileTime lastModifiedTime() { return creationTime(); } - /** Returns the HTTP etag hash for this object. */ + /** + * Returns the HTTP etag hash for this object. + */ @Override public Optional etag() { - return Optional.fromNullable(metadata.getEtag()); + return Optional.fromNullable(info.etag()); } - /** Returns the mime type (e.g. text/plain) if it was set for this object. */ + /** + * Returns the mime type (e.g. text/plain) if it was set for this object. + */ @Override public Optional mimeType() { - return Optional.fromNullable(metadata.getOptions().getMimeType()); + return Optional.fromNullable(info.contentType()); } /** @@ -60,8 +69,8 @@ public Optional mimeType() { * @see "https://developers.google.com/storage/docs/reference-headers#acl" */ @Override - public Optional acl() { - return Optional.fromNullable(metadata.getOptions().getAcl()); + public Optional> acl() { + return Optional.fromNullable(info.acl()); } /** @@ -71,7 +80,7 @@ public Optional acl() { */ @Override public Optional cacheControl() { - return Optional.fromNullable(metadata.getOptions().getCacheControl()); + return Optional.fromNullable(info.cacheControl()); } /** @@ -81,7 +90,7 @@ public Optional cacheControl() { */ @Override public Optional contentEncoding() { - return Optional.fromNullable(metadata.getOptions().getContentEncoding()); + return Optional.fromNullable(info.contentEncoding()); } /** @@ -91,7 +100,7 @@ public Optional contentEncoding() { */ @Override public Optional contentDisposition() { - return Optional.fromNullable(metadata.getOptions().getContentDisposition()); + return Optional.fromNullable(info.contentDisposition()); } /** @@ -101,7 +110,10 @@ public Optional contentDisposition() { */ @Override public ImmutableMap userMetadata() { - return ImmutableMap.copyOf(metadata.getOptions().getUserMetadata()); + if (null == info.metadata()) { + return ImmutableMap.of(); + } + return ImmutableMap.copyOf(info.metadata()); } @Override @@ -126,30 +138,30 @@ public boolean isSymbolicLink() { @Override public FileTime lastAccessTime() { - return FILE_TIME_UNKNOWN; + return CloudStorageFileSystem.FILE_TIME_UNKNOWN; } @Override public Object fileKey() { - return metadata.getFilename(); + return info.blobId().bucket() + info.blobId().name() + info.blobId().generation(); } @Override public boolean equals(@Nullable Object other) { return this == other || other instanceof CloudStorageObjectAttributes - && Objects.equals(metadata, ((CloudStorageObjectAttributes) other).metadata); + && Objects.equals(info, ((CloudStorageObjectAttributes) other).info); } @Override public int hashCode() { - return Objects.hash(metadata); + return info.hashCode(); } @Override public String toString() { return MoreObjects.toStringHelper(this) - .add("metadata", metadata) + .add("info", info) .toString(); } } diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageObjectImmutableException.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageObjectImmutableException.java similarity index 85% rename from gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageObjectImmutableException.java rename to gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageObjectImmutableException.java index 669f1c9ae91b..ebe816e2665b 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageObjectImmutableException.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageObjectImmutableException.java @@ -1,4 +1,4 @@ -package com.google.gcloud.storage.contrib.nio; +package com.google.cloud.storage.contrib.nio; /** Exception thrown to indicate we don't support a mutation of a cloud storage object. */ public final class CloudStorageObjectImmutableException extends UnsupportedOperationException { diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageOption.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageOption.java similarity index 65% rename from gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageOption.java rename to gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageOption.java index 4142cef2bb6a..8928c5ff4415 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageOption.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageOption.java @@ -1,4 +1,4 @@ -package com.google.gcloud.storage.contrib.nio; +package com.google.cloud.storage.contrib.nio; import java.nio.file.CopyOption; import java.nio.file.OpenOption; @@ -7,11 +7,11 @@ public interface CloudStorageOption { /** Interface for GCS options that can be specified when opening files. */ - public interface Open extends CloudStorageOption, OpenOption {} + interface Open extends CloudStorageOption, OpenOption {} /** Interface for GCS options that can be specified when copying files. */ - public interface Copy extends CloudStorageOption, CopyOption {} + interface Copy extends CloudStorageOption, CopyOption {} /** Interface for GCS options that can be specified when opening or copying files. */ - public interface OpenCopy extends Open, Copy {} + interface OpenCopy extends Open, Copy {} } diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageOptions.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageOptions.java similarity index 94% rename from gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageOptions.java rename to gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageOptions.java index c66842d44c03..a22b429ef31b 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageOptions.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageOptions.java @@ -1,4 +1,6 @@ -package com.google.gcloud.storage.contrib.nio; +package com.google.cloud.storage.contrib.nio; + +import com.google.cloud.storage.Acl; /** Helper class for specifying options when opening and copying Cloud Storage files. */ public final class CloudStorageOptions { @@ -45,7 +47,7 @@ public static CloudStorageOption.OpenCopy withContentEncoding(String contentEnco * * @see "https://developers.google.com/storage/docs/reference-headers#acl" */ - public static CloudStorageOption.OpenCopy withAcl(String acl) { + public static CloudStorageOption.OpenCopy withAcl(Acl acl) { return OptionAcl.create(acl); } diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStoragePath.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStoragePath.java similarity index 90% rename from gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStoragePath.java rename to gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStoragePath.java index 7d8e88753ce8..60b7ab0700cb 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStoragePath.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStoragePath.java @@ -1,12 +1,11 @@ -package com.google.gcloud.storage.contrib.nio; +package com.google.cloud.storage.contrib.nio; +import static com.google.cloud.storage.contrib.nio.CloudStorageUtil.checkNotNullArray; +import static com.google.cloud.storage.contrib.nio.CloudStorageUtil.checkPath; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.gcloud.storage.contrib.nio.CloudStorageFileSystem.URI_SCHEME; -import static com.google.gcloud.storage.contrib.nio.CloudStorageUtil.checkNotNullArray; -import static com.google.gcloud.storage.contrib.nio.CloudStorageUtil.checkPath; -import com.google.appengine.tools.cloudstorage.GcsFilename; +import com.google.cloud.storage.BlobId; import com.google.common.collect.UnmodifiableIterator; import java.io.File; @@ -27,7 +26,7 @@ import javax.annotation.concurrent.Immutable; /** - * Google Cloud Storage {@link Path} + * Google Cloud Storage {@link Path}. * * @see UnixPath */ @@ -51,14 +50,16 @@ static CloudStoragePath getPath( fileSystem, UnixPath.getPath(fileSystem.config().permitEmptyPathComponents(), path, more)); } - /** Returns the Cloud Storage bucket name being served by this file system. */ + /** Returns the Cloud Storage bucket name being served by this file system. + */ public String bucket() { return fileSystem.bucket(); } - /** Returns path converted to a {@link GcsFilename} so I/O can be performed. */ - GcsFilename getGcsFilename() { - return new GcsFilename(bucket(), toRealPath().path.toString()); + /** Returns path converted to a {@link BlobId} so I/O can be performed. + */ + BlobId getBlobId() { + return BlobId.of(bucket(), toRealPath().path.toString()); } boolean seemsLikeADirectory() { @@ -128,8 +129,6 @@ private UnixPath toRealPathInternal(boolean errorCheck) { /** * Returns path without extra slashes or {@code .} and {@code ..} and preserves trailing slash. - * - * @see java.nio.file.Path#normalize() */ @Override public CloudStoragePath normalize() { @@ -222,24 +221,25 @@ public boolean endsWith(String other) { return path.endsWith(getUnixPath(other)); } - /** @throws UnsupportedOperationException */ + /** Always @throws UnsupportedOperationException. + */ @Override public WatchKey register(WatchService watcher, Kind[] events, Modifier... modifiers) { - // TODO(b/18998105): Implement me. + // TODO: Implement me. throw new UnsupportedOperationException(); } - /** @throws UnsupportedOperationException */ + /** Always @throws UnsupportedOperationException. + */ @Override public WatchKey register(WatchService watcher, Kind... events) { - // TODO(b/18998105): Implement me. + // TODO: Implement me. throw new UnsupportedOperationException(); } /** * This operation is not supported, since GCS files aren't backed by the local file system. - * - * @throws UnsupportedOperationException + * Always @throws UnsupportedOperationException. */ @Override public File toFile() { @@ -290,7 +290,7 @@ public String toString() { @Override public URI toUri() { try { - return new URI(URI_SCHEME, bucket(), path.toAbsolutePath().toString(), null); + return new URI(CloudStorageFileSystem.URI_SCHEME, bucket(), path.toAbsolutePath().toString(), null); } catch (URISyntaxException e) { throw new AssertionError(e); } diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStoragePseudoDirectoryAttributes.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStoragePseudoDirectoryAttributes.java similarity index 79% rename from gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStoragePseudoDirectoryAttributes.java rename to gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStoragePseudoDirectoryAttributes.java index fc381de3244b..49f062f622b3 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStoragePseudoDirectoryAttributes.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStoragePseudoDirectoryAttributes.java @@ -1,17 +1,22 @@ -package com.google.gcloud.storage.contrib.nio; +package com.google.cloud.storage.contrib.nio; -import static com.google.gcloud.storage.contrib.nio.CloudStorageFileSystem.FILE_TIME_UNKNOWN; +import static com.google.cloud.storage.contrib.nio.CloudStorageFileSystem.FILE_TIME_UNKNOWN; +import com.google.cloud.storage.Acl; import com.google.common.base.Optional; import com.google.common.collect.ImmutableMap; import java.nio.file.attribute.FileTime; +import java.util.List; /** Metadata for a cloud storage pseudo-directory. */ final class CloudStoragePseudoDirectoryAttributes implements CloudStorageFileAttributes { - static final CloudStoragePseudoDirectoryAttributes SINGLETON_INSTANCE = - new CloudStoragePseudoDirectoryAttributes(); + private final String id; + + CloudStoragePseudoDirectoryAttributes(CloudStoragePath path) { + this.id = path.toUri().toString(); + } @Override public boolean isDirectory() { @@ -35,7 +40,7 @@ public boolean isSymbolicLink() { @Override public Object fileKey() { - return null; + return id; } @Override @@ -69,7 +74,7 @@ public Optional mimeType() { } @Override - public Optional acl() { + public Optional> acl() { return Optional.absent(); } @@ -92,6 +97,4 @@ public Optional contentDisposition() { public ImmutableMap userMetadata() { return ImmutableMap.of(); } - - private CloudStoragePseudoDirectoryAttributes() {} } diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStoragePseudoDirectoryException.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStoragePseudoDirectoryException.java similarity index 89% rename from gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStoragePseudoDirectoryException.java rename to gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStoragePseudoDirectoryException.java index d4ebbdad6e20..15cfffad0c69 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStoragePseudoDirectoryException.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStoragePseudoDirectoryException.java @@ -1,4 +1,4 @@ -package com.google.gcloud.storage.contrib.nio; +package com.google.cloud.storage.contrib.nio; import java.nio.file.InvalidPathException; diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageReadChannel.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageReadChannel.java similarity index 64% rename from gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageReadChannel.java rename to gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageReadChannel.java index f4a29dde941c..46629694e7cf 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageReadChannel.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageReadChannel.java @@ -1,11 +1,11 @@ -package com.google.gcloud.storage.contrib.nio; +package com.google.cloud.storage.contrib.nio; import static com.google.common.base.Preconditions.checkArgument; -import com.google.appengine.tools.cloudstorage.GcsFileMetadata; -import com.google.appengine.tools.cloudstorage.GcsFilename; -import com.google.appengine.tools.cloudstorage.GcsInputChannel; -import com.google.appengine.tools.cloudstorage.GcsService; +import com.google.cloud.ReadChannel; +import com.google.cloud.storage.BlobId; +import com.google.cloud.storage.BlobInfo; +import com.google.cloud.storage.Storage; import java.io.IOException; import java.nio.ByteBuffer; @@ -25,28 +25,27 @@ final class CloudStorageReadChannel implements SeekableByteChannel { static CloudStorageReadChannel create( - GcsService gcsService, GcsFilename file, long position) throws IOException { + Storage gcsStorage, BlobId file, long position) throws IOException { // XXX: Reading size and opening file should be atomic. - long size = fetchSize(gcsService, file); - return new CloudStorageReadChannel(gcsService, file, position, size, - gcsService.openReadChannel(file, position)); + long size = fetchSize(gcsStorage, file); + ReadChannel channel = gcsStorage.reader(file); + if (position > 0) { + channel.seek((int) position); + } + return new CloudStorageReadChannel(position, size, channel); } - private final GcsService gcsService; - private final GcsFilename file; + private final ReadChannel channel; private long position; private long size; - private GcsInputChannel channel; - private CloudStorageReadChannel( - GcsService gcsService, GcsFilename file, long position, long size, GcsInputChannel channel) { - this.gcsService = gcsService; - this.file = file; + private CloudStorageReadChannel(long position, long size, ReadChannel channel) { this.position = position; this.size = size; this.channel = channel; } + @Override public boolean isOpen() { synchronized (this) { @@ -101,10 +100,8 @@ public SeekableByteChannel position(long newPosition) throws IOException { if (newPosition == position) { return this; } + channel.seek((int) newPosition); position = newPosition; - size = fetchSize(gcsService, file); - channel.close(); - channel = gcsService.openReadChannel(file, position); return this; } } @@ -125,13 +122,12 @@ private void checkOpen() throws ClosedChannelException { } } - private static long fetchSize(GcsService gcsService, GcsFilename file) throws IOException, - NoSuchFileException { - GcsFileMetadata metadata = gcsService.getMetadata(file); - if (metadata == null) { + private static long fetchSize(Storage gcsStorage, BlobId file) throws IOException { + BlobInfo blobInfo = gcsStorage.get(file); + if (blobInfo == null) { throw new NoSuchFileException( - String.format("gs://%s/%s", file.getBucketName(), file.getObjectName())); + String.format("gs://%s/%s", file.bucket(), file.name())); } - return metadata.getLength(); + return blobInfo.size(); } } diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageUtil.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageUtil.java new file mode 100644 index 000000000000..11a7e01edbef --- /dev/null +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageUtil.java @@ -0,0 +1,57 @@ +package com.google.cloud.storage.contrib.nio; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.file.Path; +import java.nio.file.ProviderMismatchException; +import java.util.regex.Pattern; + +final class CloudStorageUtil { + + private static final Pattern BUCKET_PATTERN = Pattern.compile("[a-z0-9][-._a-z0-9]+[a-z0-9]"); + + static void checkBucket(String bucket) { + // TODO: The true check is actually more complicated. Consider implementing it. + checkArgument(BUCKET_PATTERN.matcher(bucket).matches(), "" + + "Invalid bucket name: '" + bucket + "'. " + + "GCS bucket names must contain only lowercase letters, numbers, dashes (-), " + + "underscores (_), and dots (.). Bucket names must start and end with a number or letter. " + + "See https://developers.google.com/storage/docs/bucketnaming for more details."); + } + + static CloudStoragePath checkPath(Path path) { + if (!(checkNotNull(path) instanceof CloudStoragePath)) { + throw new ProviderMismatchException(String.format( + "Not a cloud storage path: %s (%s)", path, path.getClass().getSimpleName())); + } + return (CloudStoragePath) path; + } + + static URI stripPathFromUri(URI uri) { + try { + return new URI( + uri.getScheme(), + uri.getUserInfo(), + uri.getHost(), + uri.getPort(), + null, + uri.getQuery(), + uri.getFragment()); + } catch (URISyntaxException e) { + throw new IllegalArgumentException(e.getMessage()); + } + } + + /** Makes NullPointerTester happy. */ + @SafeVarargs + static void checkNotNullArray(T... values) { + for (T value : values) { + checkNotNull(value); + } + } + + private CloudStorageUtil() {} +} diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageWriteChannel.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageWriteChannel.java similarity index 82% rename from gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageWriteChannel.java rename to gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageWriteChannel.java index 2b5a004e1bc4..7eb9f7b612a1 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageWriteChannel.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageWriteChannel.java @@ -1,6 +1,6 @@ -package com.google.gcloud.storage.contrib.nio; +package com.google.cloud.storage.contrib.nio; -import com.google.appengine.tools.cloudstorage.GcsOutputChannel; +import com.google.cloud.WriteChannel; import java.io.IOException; import java.nio.ByteBuffer; @@ -20,11 +20,11 @@ @ThreadSafe final class CloudStorageWriteChannel implements SeekableByteChannel { - private GcsOutputChannel channel; + private final WriteChannel channel; private long position; private long size; - CloudStorageWriteChannel(GcsOutputChannel channel) { + CloudStorageWriteChannel(WriteChannel channel) { this.channel = channel; } @@ -83,8 +83,8 @@ public long size() throws IOException { @Override public SeekableByteChannel truncate(long newSize) throws IOException { - // TODO(b/18997913): Emulate this functionality by closing and rewriting old file up to newSize. - // Or maybe just swap out GcsService for the Apiary client. + // TODO: Emulate this functionality by closing and rewriting old file up to newSize. + // Or maybe just swap out GcsStorage for the API client. throw new UnsupportedOperationException(); } diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/OptionAcl.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/OptionAcl.java similarity index 54% rename from gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/OptionAcl.java rename to gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/OptionAcl.java index c0068ee1c61d..74c58db70b19 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/OptionAcl.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/OptionAcl.java @@ -1,13 +1,14 @@ -package com.google.gcloud.storage.contrib.nio; +package com.google.cloud.storage.contrib.nio; import com.google.auto.value.AutoValue; +import com.google.cloud.storage.Acl; @AutoValue abstract class OptionAcl implements CloudStorageOption.OpenCopy { - static OptionAcl create(String acl) { + static OptionAcl create(Acl acl) { return new AutoValue_OptionAcl(acl); } - abstract String acl(); + abstract Acl acl(); } diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/OptionBlockSize.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/OptionBlockSize.java similarity index 84% rename from gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/OptionBlockSize.java rename to gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/OptionBlockSize.java index f33f601fad61..0616f407429b 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/OptionBlockSize.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/OptionBlockSize.java @@ -1,4 +1,4 @@ -package com.google.gcloud.storage.contrib.nio; +package com.google.cloud.storage.contrib.nio; import com.google.auto.value.AutoValue; diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/OptionCacheControl.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/OptionCacheControl.java similarity index 85% rename from gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/OptionCacheControl.java rename to gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/OptionCacheControl.java index c33d4394fa24..2c4bb7b8b8aa 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/OptionCacheControl.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/OptionCacheControl.java @@ -1,4 +1,4 @@ -package com.google.gcloud.storage.contrib.nio; +package com.google.cloud.storage.contrib.nio; import com.google.auto.value.AutoValue; diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/OptionContentDisposition.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/OptionContentDisposition.java similarity index 87% rename from gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/OptionContentDisposition.java rename to gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/OptionContentDisposition.java index 7bebefcba19c..5a3b7c8cd4bf 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/OptionContentDisposition.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/OptionContentDisposition.java @@ -1,4 +1,4 @@ -package com.google.gcloud.storage.contrib.nio; +package com.google.cloud.storage.contrib.nio; import com.google.auto.value.AutoValue; diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/OptionContentEncoding.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/OptionContentEncoding.java similarity index 86% rename from gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/OptionContentEncoding.java rename to gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/OptionContentEncoding.java index 879d05983f37..4beee9fef349 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/OptionContentEncoding.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/OptionContentEncoding.java @@ -1,4 +1,4 @@ -package com.google.gcloud.storage.contrib.nio; +package com.google.cloud.storage.contrib.nio; import com.google.auto.value.AutoValue; diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/OptionMimeType.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/OptionMimeType.java similarity index 84% rename from gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/OptionMimeType.java rename to gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/OptionMimeType.java index feb1b0c6dd1b..62789727714d 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/OptionMimeType.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/OptionMimeType.java @@ -1,4 +1,4 @@ -package com.google.gcloud.storage.contrib.nio; +package com.google.cloud.storage.contrib.nio; import com.google.auto.value.AutoValue; diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/OptionUserMetadata.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/OptionUserMetadata.java similarity index 86% rename from gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/OptionUserMetadata.java rename to gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/OptionUserMetadata.java index 9851d2ad5c4f..f99a0abed434 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/OptionUserMetadata.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/OptionUserMetadata.java @@ -1,4 +1,4 @@ -package com.google.gcloud.storage.contrib.nio; +package com.google.cloud.storage.contrib.nio; import com.google.auto.value.AutoValue; @@ -10,5 +10,6 @@ static OptionUserMetadata create(String key, String value) { } abstract String key(); + abstract String value(); } diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/UnixPath.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/UnixPath.java similarity index 99% rename from gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/UnixPath.java rename to gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/UnixPath.java index 7b80a6299be6..85474ac2d92c 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/UnixPath.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/UnixPath.java @@ -1,4 +1,4 @@ -package com.google.gcloud.storage.contrib.nio; +package com.google.cloud.storage.contrib.nio; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; @@ -403,7 +403,7 @@ public UnixPath toAbsolutePath(UnixPath currentWorkingDirectory) { return isAbsolute() ? this : currentWorkingDirectory.resolve(this); } - /** Returns {@code toAbsolutePath(ROOT_PATH)} */ + /** Returns {@code toAbsolutePath(ROOT_PATH)}. */ public UnixPath toAbsolutePath() { return toAbsolutePath(ROOT_PATH); } diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/package-info.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/package-info.java similarity index 92% rename from gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/package-info.java rename to gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/package-info.java index e07c960b5512..aa08a5cb8693 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/package-info.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/package-info.java @@ -37,7 +37,7 @@ * } * *

You can set various attributes using - * {@link com.google.gcloud.storage.contrib.nio.CloudStorageOptions CloudStorageOptions} static + * {@link com.google.cloud.storage.contrib.nio.CloudStorageOptions CloudStorageOptions} static * helpers: *

  *   Files.write(csvPath, csvLines, StandardCharsets.UTF_8,
@@ -49,7 +49,7 @@
  * includes a trailing slash, will be considered a directory. It will always be assumed to exist,
  * without performing any I/O. This allows you to do path manipulation in the same manner as you
  * would with the normal UNIX file system implementation. You can disable this feature with
- * {@link com.google.gcloud.storage.contrib.nio.CloudStorageConfiguration#usePseudoDirectories()}.
+ * {@link com.google.cloud.storage.contrib.nio.CloudStorageConfiguration#usePseudoDirectories()}.
  *
  * 

Unit Testing

* @@ -79,4 +79,4 @@ * data = Files.readBytes(path);}
*/ @javax.annotation.ParametersAreNonnullByDefault -package com.google.gcloud.storage.contrib.nio; +package com.google.cloud.storage.contrib.nio; diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageUtil.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageUtil.java deleted file mode 100644 index 79f46af172ee..000000000000 --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageUtil.java +++ /dev/null @@ -1,110 +0,0 @@ -package com.google.gcloud.storage.contrib.nio; - -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.base.Strings.isNullOrEmpty; - -import com.google.appengine.tools.cloudstorage.GcsFileOptions; - -import java.net.URI; -import java.net.URISyntaxException; -import java.nio.file.Path; -import java.nio.file.ProviderMismatchException; -import java.util.Map; -import java.util.regex.Pattern; - -final class CloudStorageUtil { - - private static final Pattern BUCKET_PATTERN = Pattern.compile("[a-z0-9][-._a-z0-9.]+[a-z0-9]"); - - static void checkBucket(String bucket) { - // TODO(b/18998200): The true check is actually more complicated. Consider implementing it. - checkArgument(BUCKET_PATTERN.matcher(bucket).matches(), "" - + "Invalid bucket name: '" + bucket + "'. " - + "GCS bucket names must contain only lowercase letters, numbers, dashes (-), " - + "underscores (_), and dots (.). Bucket names must start and end with a number or letter. " - + "See https://developers.google.com/storage/docs/bucketnaming for more details."); - } - - static CloudStoragePath checkPath(Path path) { - if (!(checkNotNull(path) instanceof CloudStoragePath)) { - throw new ProviderMismatchException(String.format( - "Not a cloud storage path: %s (%s)", path, path.getClass().getSimpleName())); - } - return (CloudStoragePath) path; - } - - static GcsFileOptions.Builder copyFileOptions(GcsFileOptions options) { - GcsFileOptions.Builder builder = new GcsFileOptions.Builder(); - if (!isNullOrEmpty(options.getAcl())) { - builder.acl(options.getAcl()); - } - if (!isNullOrEmpty(options.getCacheControl())) { - builder.cacheControl(options.getCacheControl()); - } - if (!isNullOrEmpty(options.getContentDisposition())) { - builder.contentDisposition(options.getContentDisposition()); - } - if (!isNullOrEmpty(options.getContentEncoding())) { - builder.contentEncoding(options.getContentEncoding()); - } - if (!isNullOrEmpty(options.getMimeType())) { - builder.mimeType(options.getMimeType()); - } - for (Map.Entry entry : options.getUserMetadata().entrySet()) { - builder.addUserMetadata(entry.getKey(), entry.getValue()); - } - return builder; - } - - @SafeVarargs - static GcsFileOptions buildFileOptions(GcsFileOptions.Builder builder, T... options) { - for (Object option : options) { - if (option instanceof OptionAcl) { - builder.acl(((OptionAcl) option).acl()); - } else if (option instanceof OptionCacheControl) { - builder.cacheControl(((OptionCacheControl) option).cacheControl()); - } else if (option instanceof OptionContentDisposition) { - builder.contentDisposition(((OptionContentDisposition) option).contentDisposition()); - } else if (option instanceof OptionContentEncoding) { - builder.contentEncoding(((OptionContentEncoding) option).contentEncoding()); - } else if (option instanceof OptionMimeType) { - builder.mimeType(((OptionMimeType) option).mimeType()); - } else if (option instanceof OptionUserMetadata) { - OptionUserMetadata metadata = (OptionUserMetadata) option; - builder.addUserMetadata(metadata.key(), metadata.value()); - } - } - return builder.build(); - } - - static URI stripPathFromUri(URI uri) { - try { - return new URI( - uri.getScheme(), - uri.getUserInfo(), - uri.getHost(), - uri.getPort(), - null, - uri.getQuery(), - uri.getFragment()); - } catch (URISyntaxException e) { - throw new IllegalArgumentException(e.getMessage()); - } - } - - /** Makes NullPointerTester happy. */ - @SafeVarargs - static void checkNotNullArray(T... values) { - for (T value : values) { - checkNotNull(value); - } - } - - static boolean getPropertyBoolean(String property, boolean defaultValue) { - String value = System.getProperty(property); - return value != null ? Boolean.valueOf(value) : defaultValue; - } - - private CloudStorageUtil() {} -} diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageConfigurationTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageConfigurationTest.java similarity index 97% rename from gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageConfigurationTest.java rename to gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageConfigurationTest.java index 9bb987e86707..5c91f2d2ff08 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageConfigurationTest.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageConfigurationTest.java @@ -1,4 +1,4 @@ -package com.google.gcloud.storage.contrib.nio; +package com.google.cloud.storage.contrib.nio; import static com.google.common.truth.Truth.assertThat; diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileAttributeViewTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributeViewTest.java similarity index 89% rename from gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileAttributeViewTest.java rename to gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributeViewTest.java index c376d2456dfc..e0d4e4734039 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileAttributeViewTest.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributeViewTest.java @@ -1,11 +1,11 @@ -package com.google.gcloud.storage.contrib.nio; +package com.google.cloud.storage.contrib.nio; import static com.google.common.truth.Truth.assertThat; -import static com.google.gcloud.storage.contrib.nio.CloudStorageOptions.withCacheControl; import static java.nio.charset.StandardCharsets.UTF_8; import com.google.common.testing.EqualsTester; import com.google.common.testing.NullPointerTester; +import com.google.cloud.storage.testing.LocalGcsHelper; import org.junit.Before; import org.junit.Rule; @@ -30,19 +30,17 @@ public class CloudStorageFileAttributeViewTest { @Rule public final ExpectedException thrown = ExpectedException.none(); - @Rule - public final AppEngineRule appEngineRule = new AppEngineRule(); - private Path path; @Before - public void before() throws Exception { + public void before() { + CloudStorageFileSystemProvider.setGCloudOptions(LocalGcsHelper.options()); path = Paths.get(URI.create("gs://red/water")); } @Test public void testReadAttributes() throws Exception { - Files.write(path, HAPPY, withCacheControl("potato")); + Files.write(path, HAPPY, CloudStorageOptions.withCacheControl("potato")); CloudStorageFileAttributeView lazyAttributes = Files.getFileAttributeView(path, CloudStorageFileAttributeView.class); assertThat(lazyAttributes.readAttributes().cacheControl().get()).isEqualTo("potato"); @@ -67,7 +65,7 @@ public void testReadAttributes_pseudoDirectory() throws Exception { @Test public void testName() throws Exception { - Files.write(path, HAPPY, withCacheControl("potato")); + Files.write(path, HAPPY, CloudStorageOptions.withCacheControl("potato")); CloudStorageFileAttributeView lazyAttributes = Files.getFileAttributeView(path, CloudStorageFileAttributeView.class); assertThat(lazyAttributes.name()).isEqualTo("gcs"); diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileAttributesTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributesTest.java similarity index 70% rename from gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileAttributesTest.java rename to gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributesTest.java index 856a5dcb2c8c..b3b2cb0f6960 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileAttributesTest.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributesTest.java @@ -1,19 +1,20 @@ -package com.google.gcloud.storage.contrib.nio; - +package com.google.cloud.storage.contrib.nio; + +import static com.google.cloud.storage.contrib.nio.CloudStorageOptions.withAcl; +import static com.google.cloud.storage.contrib.nio.CloudStorageOptions.withCacheControl; +import static com.google.cloud.storage.contrib.nio.CloudStorageOptions.withContentDisposition; +import static com.google.cloud.storage.contrib.nio.CloudStorageOptions.withContentEncoding; +import static com.google.cloud.storage.contrib.nio.CloudStorageOptions.withMimeType; +import static com.google.cloud.storage.contrib.nio.CloudStorageOptions.withUserMetadata; import static com.google.common.truth.Truth.assertThat; -import static com.google.gcloud.storage.contrib.nio.CloudStorageOptions.withAcl; -import static com.google.gcloud.storage.contrib.nio.CloudStorageOptions.withCacheControl; -import static com.google.gcloud.storage.contrib.nio.CloudStorageOptions.withContentDisposition; -import static com.google.gcloud.storage.contrib.nio.CloudStorageOptions.withContentEncoding; -import static com.google.gcloud.storage.contrib.nio.CloudStorageOptions.withMimeType; -import static com.google.gcloud.storage.contrib.nio.CloudStorageOptions.withUserMetadata; import static java.nio.charset.StandardCharsets.UTF_8; +import com.google.cloud.storage.Acl; +import com.google.cloud.storage.testing.LocalGcsHelper; import com.google.common.testing.EqualsTester; import com.google.common.testing.NullPointerTester; import org.junit.Before; -import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -29,14 +30,14 @@ public class CloudStorageFileAttributesTest { private static final byte[] HAPPY = "(✿◕ ‿◕ )ノ".getBytes(UTF_8); - @Rule - public final AppEngineRule appEngineRule = new AppEngineRule(); private Path path; private Path dir; + /** empty test storage and make sure we use it instead of the real GCS. Create a few paths. **/ @Before - public void before() throws Exception { + public void before() { + CloudStorageFileSystemProvider.setGCloudOptions(LocalGcsHelper.options()); path = Paths.get(URI.create("gs://bucket/randompath")); dir = Paths.get(URI.create("gs://bucket/randompath/")); } @@ -57,9 +58,10 @@ public void testMimeType() throws Exception { @Test public void testAcl() throws Exception { - Files.write(path, HAPPY, withAcl("potato")); + Acl acl = Acl.of(new Acl.User("serf@example.com"), Acl.Role.READER); + Files.write(path, HAPPY, withAcl(acl)); assertThat(Files.readAttributes(path, CloudStorageFileAttributes.class).acl().get()) - .isEqualTo("potato"); + .contains(acl); } @Test @@ -129,6 +131,25 @@ public void testEquals_equalsTester() throws Exception { new EqualsTester().addEqualityGroup(a1, a2).addEqualityGroup(b1, b2).testEquals(); } + @Test + public void testFilekey() throws Exception { + Files.write(path, HAPPY, withMimeType("text/plain")); + Path path2 = Paths.get(URI.create("gs://bucket/anotherrandompath")); + Files.write(path2, HAPPY, withMimeType("text/plain")); + + // diff files cannot have same filekey + CloudStorageFileAttributes a1 = Files.readAttributes(path, CloudStorageFileAttributes.class); + CloudStorageFileAttributes a2 = Files.readAttributes(path2, CloudStorageFileAttributes.class); + assertThat(a1.fileKey()).isNotEqualTo(a2.fileKey()); + + // same for directories + CloudStorageFileAttributes b1 = Files.readAttributes(dir, CloudStorageFileAttributes.class); + CloudStorageFileAttributes b2 = Files.readAttributes( + Paths.get(URI.create("gs://bucket/jacket/")), CloudStorageFileAttributes.class); + assertThat(a1.fileKey()).isNotEqualTo(b1.fileKey()); + assertThat(b1.fileKey()).isNotEqualTo(b2.fileKey()); + } + @Test public void testNullness() throws Exception { Files.write(path, HAPPY); diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystemProviderTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProviderTest.java similarity index 79% rename from gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystemProviderTest.java rename to gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProviderTest.java index 8dad56ff984e..c72f6ee2ed22 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystemProviderTest.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProviderTest.java @@ -1,9 +1,11 @@ -package com.google.gcloud.storage.contrib.nio; +package com.google.cloud.storage.contrib.nio; +import static com.google.cloud.storage.contrib.nio.CloudStorageOptions.withCacheControl; +import static com.google.cloud.storage.contrib.nio.CloudStorageOptions.withContentDisposition; +import static com.google.cloud.storage.contrib.nio.CloudStorageOptions.withContentEncoding; +import static com.google.cloud.storage.contrib.nio.CloudStorageOptions.withMimeType; +import static com.google.cloud.storage.contrib.nio.CloudStorageOptions.withUserMetadata; import static com.google.common.truth.Truth.assertThat; -import static com.google.gcloud.storage.contrib.nio.CloudStorageFileSystem.forBucket; -import static com.google.gcloud.storage.contrib.nio.CloudStorageOptions.withCacheControl; -import static com.google.gcloud.storage.contrib.nio.CloudStorageOptions.withMimeType; import static java.nio.charset.StandardCharsets.UTF_8; import static java.nio.file.StandardCopyOption.ATOMIC_MOVE; import static java.nio.file.StandardCopyOption.COPY_ATTRIBUTES; @@ -11,15 +13,12 @@ import static java.nio.file.StandardOpenOption.CREATE_NEW; import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING; import static java.nio.file.StandardOpenOption.WRITE; -import static org.junit.Assume.assumeTrue; -import com.google.appengine.tools.cloudstorage.GcsFileMetadata; -import com.google.appengine.tools.cloudstorage.GcsFilename; -import com.google.appengine.tools.cloudstorage.GcsService; -import com.google.appengine.tools.cloudstorage.GcsServiceFactory; +import com.google.cloud.storage.testing.LocalGcsHelper; import com.google.common.collect.ImmutableList; import com.google.common.testing.NullPointerTester; +import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -48,6 +47,7 @@ /** Unit tests for {@link CloudStorageFileSystemProvider}. */ @RunWith(JUnit4.class) +@SuppressWarnings("resource") public class CloudStorageFileSystemProviderTest { private static final List FILE_CONTENTS = ImmutableList.of( @@ -65,8 +65,10 @@ public class CloudStorageFileSystemProviderTest { @Rule public final ExpectedException thrown = ExpectedException.none(); - @Rule - public final AppEngineRule appEngineRule = new AppEngineRule(); + @Before + public void before() { + CloudStorageFileSystemProvider.setGCloudOptions(LocalGcsHelper.options()); + } @Test public void testSize() throws Exception { @@ -82,7 +84,7 @@ public void testSize_trailingSlash_returnsFakePseudoDirectorySize() throws Excep @Test public void testSize_trailingSlash_disablePseudoDirectories() throws Exception { - try (CloudStorageFileSystem fs = forBucket("doodle", usePseudoDirectories(false))) { + try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle", usePseudoDirectories(false))) { Path path = fs.getPath("wat/"); byte[] rapture = SINGULARITY.getBytes(UTF_8); Files.write(path, rapture); @@ -249,15 +251,13 @@ public void testNewOutputStream_truncateExplicitly() throws Exception { public void testNewOutputStream_trailingSlash() throws Exception { Path path = Paths.get(URI.create("gs://bucket/wat/")); thrown.expect(CloudStoragePseudoDirectoryException.class); - try (OutputStream output = Files.newOutputStream(path)) { - } + Files.newOutputStream(path); } @Test public void testNewOutputStream_createNew() throws Exception { Path path = Paths.get(URI.create("gs://cry/wednesday")); - try (OutputStream output = Files.newOutputStream(path, CREATE_NEW)) { - } + Files.newOutputStream(path, CREATE_NEW); } @Test @@ -265,8 +265,7 @@ public void testNewOutputStream_createNew_alreadyExists() throws Exception { Path path = Paths.get(URI.create("gs://cry/wednesday")); Files.write(path, SINGULARITY.getBytes(UTF_8)); thrown.expect(FileAlreadyExistsException.class); - try (OutputStream output = Files.newOutputStream(path, CREATE_NEW)) { - } + Files.newOutputStream(path, CREATE_NEW); } @Test @@ -278,24 +277,20 @@ public void testWrite_objectNameWithExtraSlashes_throwsIae() throws Exception { @Test public void testWrite_objectNameWithExtraSlashes_canBeNormalized() throws Exception { - try (CloudStorageFileSystem fs = forBucket("greenbean", permitEmptyPathComponents(false))) { + try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("greenbean", permitEmptyPathComponents(false))) { Path path = fs.getPath("adipose//yep").normalize(); Files.write(path, FILE_CONTENTS, UTF_8); assertThat(Files.readAllLines(path, UTF_8)).isEqualTo(FILE_CONTENTS); - GcsService gcsService = GcsServiceFactory.createGcsService(); - assertThat(gcsService.getMetadata(new GcsFilename("greenbean", "adipose/yep"))).isNotNull(); - assertThat(Files.exists(path)).isTrue(); + assertThat(Files.exists(fs.getPath("adipose", "yep"))).isTrue(); } } @Test public void testWrite_objectNameWithExtraSlashes_permitEmptyPathComponents() throws Exception { - try (CloudStorageFileSystem fs = forBucket("greenbean", permitEmptyPathComponents(true))) { + try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("greenbean", permitEmptyPathComponents(true))) { Path path = fs.getPath("adipose//yep"); Files.write(path, FILE_CONTENTS, UTF_8); assertThat(Files.readAllLines(path, UTF_8)).isEqualTo(FILE_CONTENTS); - GcsService gcsService = GcsServiceFactory.createGcsService(); - assertThat(gcsService.getMetadata(new GcsFilename("greenbean", "adipose//yep"))).isNotNull(); assertThat(Files.exists(path)).isTrue(); } } @@ -305,23 +300,19 @@ public void testWrite_absoluteObjectName_prefixSlashGetsRemoved() throws Excepti Path path = Paths.get(URI.create("gs://greenbean/adipose/yep")); Files.write(path, FILE_CONTENTS, UTF_8); assertThat(Files.readAllLines(path, UTF_8)).isEqualTo(FILE_CONTENTS); - GcsService gcsService = GcsServiceFactory.createGcsService(); - assertThat(gcsService.getMetadata(new GcsFilename("greenbean", "adipose/yep"))).isNotNull(); assertThat(Files.exists(path)).isTrue(); } @Test public void testWrite_absoluteObjectName_disableStrip_slashGetsPreserved() throws Exception { try (CloudStorageFileSystem fs = - forBucket("greenbean", + CloudStorageFileSystem.forBucket("greenbean", CloudStorageConfiguration.builder() .stripPrefixSlash(false) .build())) { Path path = fs.getPath("/adipose/yep"); Files.write(path, FILE_CONTENTS, UTF_8); assertThat(Files.readAllLines(path, UTF_8)).isEqualTo(FILE_CONTENTS); - GcsService gcsService = GcsServiceFactory.createGcsService(); - assertThat(gcsService.getMetadata(new GcsFilename("greenbean", "/adipose/yep"))).isNotNull(); assertThat(Files.exists(path)).isTrue(); } } @@ -333,6 +324,30 @@ public void testWrite() throws Exception { assertThat(Files.readAllLines(path, UTF_8)).isEqualTo(FILE_CONTENTS); } + @Test + public void testWriteOnClose() throws Exception { + Path path = Paths.get(URI.create("gs://greenbean/adipose")); + try (SeekableByteChannel chan = Files.newByteChannel(path, StandardOpenOption.WRITE)) { + // writing lots of contents to defeat channel-internal buffering. + for (int i = 0; i < 9999; i++) { + for (String s : FILE_CONTENTS) { + chan.write(ByteBuffer.wrap(s.getBytes(UTF_8))); + } + } + try { + Files.size(path); + // we shouldn't make it to this line. Not using thrown.expect because + // I still want to run a few lines after the exception. + assertThat(false).isTrue(); + } catch (NoSuchFileException nsf) { + // that's what we wanted, we're good. + } + } + // channel now closed, the file should be there and with the new contents. + assertThat(Files.exists(path)).isTrue(); + assertThat(Files.size(path)).isGreaterThan(100L); + } + @Test public void testWrite_trailingSlash() throws Exception { thrown.expect(CloudStoragePseudoDirectoryException.class); @@ -355,7 +370,7 @@ public void testExists_trailingSlash() throws Exception { @Test public void testExists_trailingSlash_disablePseudoDirectories() throws Exception { - try (CloudStorageFileSystem fs = forBucket("military", usePseudoDirectories(false))) { + try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("military", usePseudoDirectories(false))) { assertThat(Files.exists(fs.getPath("fashion/"))).isFalse(); } } @@ -382,29 +397,23 @@ public void testDelete_trailingSlash() throws Exception { @Test public void testDelete_trailingSlash_disablePseudoDirectories() throws Exception { - try (CloudStorageFileSystem fs = forBucket("pumpkin", usePseudoDirectories(false))) { + try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("pumpkin", usePseudoDirectories(false))) { Path path = fs.getPath("wat/"); Files.write(path, FILE_CONTENTS, UTF_8); - GcsService gcsService = GcsServiceFactory.createGcsService(); - assertThat(gcsService.getMetadata(new GcsFilename("pumpkin", "wat/"))).isNotNull(); + assertThat(Files.exists(path)); Files.delete(path); - assertThat(gcsService.getMetadata(new GcsFilename("pumpkin", "wat/"))).isNull(); + assertThat(!Files.exists(path)); } } @Test public void testDelete_notFound() throws Exception { - GcsService gcsService = GcsServiceFactory.createGcsService(); - assumeTrue(!gcsService.delete(new GcsFilename("loveh", "passionehu"))); // XXX: b/15832793 thrown.expect(NoSuchFileException.class); Files.delete(Paths.get(URI.create("gs://loveh/passionehu"))); } @Test public void testDeleteIfExists() throws Exception { - GcsService gcsService = GcsServiceFactory.createGcsService(); - assumeTrue(!gcsService.delete(new GcsFilename("loveh", "passionehu"))); // XXX: b/15832793 - assertThat(Files.deleteIfExists(Paths.get(URI.create("gs://love/passionz")))).isFalse(); Files.write(Paths.get(URI.create("gs://love/passionz")), "(✿◕ ‿◕ )ノ".getBytes(UTF_8)); assertThat(Files.deleteIfExists(Paths.get(URI.create("gs://love/passionz")))).isTrue(); } @@ -517,7 +526,7 @@ public void testIsDirectory_trailingSlash_alwaysTrue() throws Exception { @Test public void testIsDirectory_trailingSlash_pseudoDirectoriesDisabled_false() throws Exception { - try (CloudStorageFileSystem fs = forBucket("doodle", usePseudoDirectories(false))) { + try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle", usePseudoDirectories(false))) { assertThat(Files.isDirectory(fs.getPath("fundir/"))).isFalse(); } } @@ -528,12 +537,20 @@ public void testCopy_withCopyAttributes_preservesAttributes() throws Exception { Path target = Paths.get(URI.create("gs://greenbean/adipose")); Files.write(source, "(✿◕ ‿◕ )ノ".getBytes(UTF_8), withMimeType("text/lolcat"), - withCacheControl("public; max-age=666")); + withCacheControl("public; max-age=666"), + withContentEncoding("foobar"), + withContentDisposition("my-content-disposition"), + withUserMetadata("answer", "42")); Files.copy(source, target, COPY_ATTRIBUTES); - GcsService gcsService = GcsServiceFactory.createGcsService(); - GcsFileMetadata metadata = gcsService.getMetadata(new GcsFilename("greenbean", "adipose")); - assertThat(metadata.getOptions().getMimeType()).isEqualTo("text/lolcat"); - assertThat(metadata.getOptions().getCacheControl()).isEqualTo("public; max-age=666"); + + CloudStorageFileAttributes attributes = + Files.readAttributes(target, CloudStorageFileAttributes.class); + assertThat(attributes.mimeType()).hasValue("text/lolcat"); + assertThat(attributes.cacheControl()).hasValue("public; max-age=666"); + assertThat(attributes.contentEncoding()).hasValue("foobar"); + assertThat(attributes.contentDisposition()).hasValue("my-content-disposition"); + assertThat(attributes.userMetadata().containsKey("answer")).isTrue(); + assertThat(attributes.userMetadata().get("answer")).isEqualTo("42"); } @Test @@ -542,27 +559,39 @@ public void testCopy_withoutOptions_doesntPreservesAttributes() throws Exception Path target = Paths.get(URI.create("gs://greenbean/adipose")); Files.write(source, "(✿◕ ‿◕ )ノ".getBytes(UTF_8), withMimeType("text/lolcat"), - withCacheControl("public; max-age=666")); + withCacheControl("public; max-age=666"), + withUserMetadata("answer", "42")); Files.copy(source, target); - GcsService gcsService = GcsServiceFactory.createGcsService(); - GcsFileMetadata metadata = gcsService.getMetadata(new GcsFilename("greenbean", "adipose")); - assertThat(metadata.getOptions().getMimeType()).isNull(); - assertThat(metadata.getOptions().getCacheControl()).isNull(); + + CloudStorageFileAttributes attributes = + Files.readAttributes(target, CloudStorageFileAttributes.class); + String mimeType = attributes.mimeType().orNull(); + String cacheControl = attributes.cacheControl().orNull(); + assertThat(mimeType).isNotEqualTo("text/lolcat"); + assertThat(cacheControl).isNull(); + assertThat(attributes.userMetadata().containsKey("answer")).isFalse(); } @Test public void testCopy_overwriteAttributes() throws Exception { Path source = Paths.get(URI.create("gs://military/fashion.show")); - Path target = Paths.get(URI.create("gs://greenbean/adipose")); + Path target1 = Paths.get(URI.create("gs://greenbean/adipose")); + Path target2 = Paths.get(URI.create("gs://greenbean/round")); Files.write(source, "(✿◕ ‿◕ )ノ".getBytes(UTF_8), withMimeType("text/lolcat"), withCacheControl("public; max-age=666")); - Files.copy(source, target, COPY_ATTRIBUTES, + Files.copy(source, target1, COPY_ATTRIBUTES); + Files.copy(source, target2, COPY_ATTRIBUTES, withMimeType("text/palfun")); - GcsService gcsService = GcsServiceFactory.createGcsService(); - GcsFileMetadata metadata = gcsService.getMetadata(new GcsFilename("greenbean", "adipose")); - assertThat(metadata.getOptions().getMimeType()).isEqualTo("text/palfun"); - assertThat(metadata.getOptions().getCacheControl()).isEqualTo("public; max-age=666"); + + CloudStorageFileAttributes attributes = + Files.readAttributes(target1, CloudStorageFileAttributes.class); + assertThat(attributes.mimeType()).hasValue("text/lolcat"); + assertThat(attributes.cacheControl()).hasValue("public; max-age=666"); + + attributes = Files.readAttributes(target2, CloudStorageFileAttributes.class); + assertThat(attributes.mimeType()).hasValue("text/palfun"); + assertThat(attributes.cacheControl()).hasValue("public; max-age=666"); } @Test @@ -573,11 +602,22 @@ public void testNullness() throws Exception { .setDefault(Path.class, fs.getPath("and/one")) .setDefault(OpenOption.class, StandardOpenOption.CREATE) .setDefault(CopyOption.class, StandardCopyOption.COPY_ATTRIBUTES); - tester.testAllPublicStaticMethods(CloudStorageFileSystemProvider.class); + // can't do that, setGCloudOptions accepts a null argument. + // TODO(jart): Figure out how to re-enable this. + //tester.testAllPublicStaticMethods(CloudStorageFileSystemProvider.class); tester.testAllPublicInstanceMethods(new CloudStorageFileSystemProvider()); } } + @Test + public void testProviderEquals() throws Exception { + Path path1 = Paths.get(URI.create("gs://bucket/tuesday")); + Path path2 = Paths.get(URI.create("gs://blood/wednesday")); + Path path3 = Paths.get("tmp"); + assertThat(path1.getFileSystem().provider()).isEqualTo(path2.getFileSystem().provider()); + assertThat(path1.getFileSystem().provider()).isNotEqualTo(path3.getFileSystem().provider()); + } + private static CloudStorageConfiguration permitEmptyPathComponents(boolean value) { return CloudStorageConfiguration.builder() .permitEmptyPathComponents(value) diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystemTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemTest.java similarity index 94% rename from gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystemTest.java rename to gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemTest.java index 7cb750238b14..1b6479aebe09 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystemTest.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemTest.java @@ -1,12 +1,13 @@ -package com.google.gcloud.storage.contrib.nio; +package com.google.cloud.storage.contrib.nio; import static com.google.common.truth.Truth.assertThat; import static java.nio.charset.StandardCharsets.UTF_8; import com.google.common.testing.EqualsTester; import com.google.common.testing.NullPointerTester; +import com.google.cloud.storage.testing.LocalGcsHelper; -import org.junit.Rule; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -31,8 +32,11 @@ public class CloudStorageFileSystemTest { + "The Heart-ache, and the thousand Natural shocks\n" + "That Flesh is heir to? 'Tis a consummation\n"; - @Rule - public final AppEngineRule appEngineRule = new AppEngineRule(); + + @Before + public void before() { + CloudStorageFileSystemProvider.setGCloudOptions(LocalGcsHelper.options()); + } @Test public void testGetPath() throws Exception { diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageOptionsTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageOptionsTest.java similarity index 52% rename from gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageOptionsTest.java rename to gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageOptionsTest.java index 0ed9dc5f7507..790a0dbdcda0 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageOptionsTest.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageOptionsTest.java @@ -1,21 +1,20 @@ -package com.google.gcloud.storage.contrib.nio; +package com.google.cloud.storage.contrib.nio; -import static com.google.appengine.tools.cloudstorage.GcsServiceFactory.createGcsService; +import static com.google.cloud.storage.contrib.nio.CloudStorageOptions.withAcl; +import static com.google.cloud.storage.contrib.nio.CloudStorageOptions.withCacheControl; +import static com.google.cloud.storage.contrib.nio.CloudStorageOptions.withContentDisposition; +import static com.google.cloud.storage.contrib.nio.CloudStorageOptions.withContentEncoding; +import static com.google.cloud.storage.contrib.nio.CloudStorageOptions.withMimeType; +import static com.google.cloud.storage.contrib.nio.CloudStorageOptions.withUserMetadata; +import static com.google.cloud.storage.contrib.nio.CloudStorageOptions.withoutCaching; import static com.google.common.truth.Truth.assertThat; -import static com.google.gcloud.storage.contrib.nio.CloudStorageOptions.withAcl; -import static com.google.gcloud.storage.contrib.nio.CloudStorageOptions.withCacheControl; -import static com.google.gcloud.storage.contrib.nio.CloudStorageOptions.withContentDisposition; -import static com.google.gcloud.storage.contrib.nio.CloudStorageOptions.withContentEncoding; -import static com.google.gcloud.storage.contrib.nio.CloudStorageOptions.withMimeType; -import static com.google.gcloud.storage.contrib.nio.CloudStorageOptions.withUserMetadata; -import static com.google.gcloud.storage.contrib.nio.CloudStorageOptions.withoutCaching; import static java.nio.charset.StandardCharsets.UTF_8; -import com.google.appengine.tools.cloudstorage.GcsFileMetadata; -import com.google.appengine.tools.cloudstorage.GcsFilename; +import com.google.cloud.storage.Acl; +import com.google.cloud.storage.testing.LocalGcsHelper; import com.google.common.testing.NullPointerTester; -import org.junit.Rule; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -29,15 +28,18 @@ @RunWith(JUnit4.class) public class CloudStorageOptionsTest { - @Rule - public final AppEngineRule appEngineRule = new AppEngineRule(); + @Before + public void before() { + CloudStorageFileSystemProvider.setGCloudOptions(LocalGcsHelper.options()); + } @Test public void testWithoutCaching() throws Exception { Path path = Paths.get(URI.create("gs://bucket/path")); Files.write(path, "(✿◕ ‿◕ )ノ".getBytes(UTF_8), withoutCaching()); - assertThat(getMetadata("bucket", "path").getOptions().getCacheControl()).isEqualTo("no-cache"); + assertThat(Files.readAttributes(path, CloudStorageFileAttributes.class).cacheControl().get()) + .isEqualTo("no-cache"); } @Test @@ -45,16 +47,18 @@ public void testCacheControl() throws Exception { Path path = Paths.get(URI.create("gs://bucket/path")); Files.write(path, "(✿◕ ‿◕ )ノ".getBytes(UTF_8), withCacheControl("potato")); - assertThat(getMetadata("bucket", "path").getOptions().getCacheControl()).isEqualTo("potato"); + assertThat(Files.readAttributes(path, CloudStorageFileAttributes.class).cacheControl().get()) + .isEqualTo("potato"); } @Test public void testWithAcl() throws Exception { Path path = Paths.get(URI.create("gs://bucket/path")); + Acl acl = Acl.of(new Acl.User("king@example.com"), Acl.Role.OWNER); Files.write(path, "(✿◕ ‿◕ )ノ".getBytes(UTF_8), - withAcl("mine empire of dirt")); - assertThat(getMetadata("bucket", "path").getOptions().getAcl()) - .isEqualTo("mine empire of dirt"); + withAcl(acl)); + assertThat(Files.readAttributes(path, CloudStorageFileAttributes.class).acl().get()) + .contains(acl); } @Test @@ -62,7 +66,8 @@ public void testWithContentDisposition() throws Exception { Path path = Paths.get(URI.create("gs://bucket/path")); Files.write(path, "(✿◕ ‿◕ )ノ".getBytes(UTF_8), withContentDisposition("bubbly fun")); - assertThat(getMetadata("bucket", "path").getOptions().getContentDisposition()) + assertThat( + Files.readAttributes(path, CloudStorageFileAttributes.class).contentDisposition().get()) .isEqualTo("bubbly fun"); } @@ -71,7 +76,8 @@ public void testWithContentEncoding() throws Exception { Path path = Paths.get(URI.create("gs://bucket/path")); Files.write(path, "(✿◕ ‿◕ )ノ".getBytes(UTF_8), withContentEncoding("gzip")); - assertThat(getMetadata("bucket", "path").getOptions().getContentEncoding()).isEqualTo("gzip"); + assertThat(Files.readAttributes(path, CloudStorageFileAttributes.class).contentEncoding().get()) + .isEqualTo("gzip"); } @Test @@ -80,9 +86,14 @@ public void testWithUserMetadata() throws Exception { Files.write(path, "(✿◕ ‿◕ )ノ".getBytes(UTF_8), withUserMetadata("nolo", "contendere"), withUserMetadata("eternal", "sadness")); - GcsFileMetadata metadata = getMetadata("bucket", "path"); - assertThat(metadata.getOptions().getUserMetadata().get("nolo")).isEqualTo("contendere"); - assertThat(metadata.getOptions().getUserMetadata().get("eternal")).isEqualTo("sadness"); + assertThat( + Files.readAttributes(path, CloudStorageFileAttributes.class) + .userMetadata().get("nolo")) + .isEqualTo("contendere"); + assertThat( + Files.readAttributes(path, CloudStorageFileAttributes.class) + .userMetadata().get("eternal")) + .isEqualTo("sadness"); } @Test @@ -90,11 +101,8 @@ public void testWithMimeType_string() throws Exception { Path path = Paths.get(URI.create("gs://bucket/path")); Files.write(path, "(✿◕ ‿◕ )ノ".getBytes(UTF_8), withMimeType("text/plain")); - assertThat(getMetadata("bucket", "path").getOptions().getMimeType()).isEqualTo("text/plain"); - } - - private static GcsFileMetadata getMetadata(String bucket, String objectName) throws Exception { - return createGcsService().getMetadata(new GcsFilename(bucket, objectName)); + assertThat(Files.readAttributes(path, CloudStorageFileAttributes.class).mimeType().get()) + .isEqualTo("text/plain"); } @Test diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStoragePathTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStoragePathTest.java similarity index 94% rename from gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStoragePathTest.java rename to gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStoragePathTest.java index aaf9e5b4fd16..3680aeab7320 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStoragePathTest.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStoragePathTest.java @@ -1,12 +1,14 @@ -package com.google.gcloud.storage.contrib.nio; +package com.google.cloud.storage.contrib.nio; +import static com.google.cloud.storage.contrib.nio.CloudStorageFileSystem.forBucket; import static com.google.common.truth.Truth.assertThat; -import static com.google.gcloud.storage.contrib.nio.CloudStorageFileSystem.forBucket; +import com.google.cloud.storage.testing.LocalGcsHelper; import com.google.common.collect.Iterables; import com.google.common.testing.EqualsTester; import com.google.common.testing.NullPointerTester; +import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -27,8 +29,10 @@ public class CloudStoragePathTest { @Rule public final ExpectedException thrown = ExpectedException.none(); - @Rule - public final AppEngineRule appEngineRule = new AppEngineRule(); + @Before + public void before() { + CloudStorageFileSystemProvider.setGCloudOptions(LocalGcsHelper.options()); + } @Test public void testCreate_neverRemoveExtraSlashes() { @@ -50,21 +54,21 @@ public void testCreate_preservesTrailingSlash() { public void testGetGcsFilename_empty_notAllowed() { try (CloudStorageFileSystem fs = forBucket("doodle")) { thrown.expect(IllegalArgumentException.class); - fs.getPath("").getGcsFilename(); + fs.getPath("").getBlobId(); } } @Test public void testGetGcsFilename_stripsPrefixSlash() { try (CloudStorageFileSystem fs = forBucket("doodle")) { - assertThat(fs.getPath("/hi").getGcsFilename().getObjectName()).isEqualTo("hi"); + assertThat(fs.getPath("/hi").getBlobId().name()).isEqualTo("hi"); } } @Test public void testGetGcsFilename_overrideStripPrefixSlash_doesntStripPrefixSlash() { try (CloudStorageFileSystem fs = forBucket("doodle", stripPrefixSlash(false))) { - assertThat(fs.getPath("/hi").getGcsFilename().getObjectName()).isEqualTo("/hi"); + assertThat(fs.getPath("/hi").getBlobId().name()).isEqualTo("/hi"); } } @@ -72,14 +76,14 @@ public void testGetGcsFilename_overrideStripPrefixSlash_doesntStripPrefixSlash() public void testGetGcsFilename_extraSlashes_throwsIae() { try (CloudStorageFileSystem fs = forBucket("doodle")) { thrown.expect(IllegalArgumentException.class); - fs.getPath("a//b").getGcsFilename(); + fs.getPath("a//b").getBlobId().name(); } } @Test public void testGetGcsFilename_overridepermitEmptyPathComponents() { try (CloudStorageFileSystem fs = forBucket("doodle", permitEmptyPathComponents(true))) { - assertThat(fs.getPath("a//b").getGcsFilename().getObjectName()).isEqualTo("a//b"); + assertThat(fs.getPath("a//b").getBlobId().name()).isEqualTo("a//b"); } } @@ -87,7 +91,7 @@ public void testGetGcsFilename_overridepermitEmptyPathComponents() { public void testGetGcsFilename_freaksOutOnExtraSlashesAndDotDirs() { try (CloudStorageFileSystem fs = forBucket("doodle")) { thrown.expect(IllegalArgumentException.class); - fs.getPath("a//b/..").getGcsFilename(); + fs.getPath("a//b/..").getBlobId().name(); } } @@ -212,7 +216,8 @@ public void testToRealPath_extraSlashes_throwsIae() { @Test public void testToRealPath_overridePermitEmptyPathComponents_extraSlashes_slashesRemain() { try (CloudStorageFileSystem fs = forBucket("doodle", permitEmptyPathComponents(true))) { - assertThat(fs.getPath("/life///b/./good/").toRealPath().toString()).isEqualTo("life///b/./good/"); + assertThat(fs.getPath("/life///b/./good/").toRealPath().toString()) + .isEqualTo("life///b/./good/"); } } @@ -396,19 +401,21 @@ public void testEndsWith() { } } - /** @see "http://stackoverflow.com/a/10068306" */ + /** @see "http://stackoverflow.com/a/10068306". + */ @Test public void testResolve_willWorkWithRecursiveCopy() throws Exception { try (FileSystem fsSource = FileSystems.getFileSystem(URI.create("gs://hello")); FileSystem fsTarget = FileSystems.getFileSystem(URI.create("gs://cat"))) { Path targetPath = fsTarget.getPath("/some/folder/"); - Path relativeSourcePath = fsSource.getPath("file.txt"); - assertThat((Object) targetPath.resolve(relativeSourcePath)) + Path relSrcPath = fsSource.getPath("file.txt"); + assertThat((Object) targetPath.resolve(relSrcPath)) .isEqualTo(fsTarget.getPath("/some/folder/file.txt")); } } - /** @see "http://stackoverflow.com/a/10068306" */ + /** @see "http://stackoverflow.com/a/10068306". + */ @Test public void testRelativize_willWorkWithRecursiveCopy() throws Exception { try (FileSystem fsSource = FileSystems.getFileSystem(URI.create("gs://hello")); diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageReadChannelTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageReadChannelTest.java similarity index 56% rename from gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageReadChannelTest.java rename to gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageReadChannelTest.java index c46ec0ac7981..64cb5b2bdc42 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageReadChannelTest.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageReadChannelTest.java @@ -1,21 +1,34 @@ -package com.google.gcloud.storage.contrib.nio; +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * 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 com.google.cloud.storage.contrib.nio; import static com.google.common.truth.Truth.assertThat; import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; -import com.google.appengine.tools.cloudstorage.GcsFileMetadata; -import com.google.appengine.tools.cloudstorage.GcsFileOptions; -import com.google.appengine.tools.cloudstorage.GcsFilename; -import com.google.appengine.tools.cloudstorage.GcsInputChannel; -import com.google.appengine.tools.cloudstorage.GcsService; +import com.google.cloud.ReadChannel; +import com.google.cloud.storage.Blob; +import com.google.cloud.storage.BlobId; +import com.google.cloud.storage.Storage; import org.junit.Before; import org.junit.Rule; @@ -24,36 +37,39 @@ import org.junit.runner.RunWith; import org.junit.runners.JUnit4; +import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.ClosedChannelException; import java.nio.channels.NonWritableChannelException; -/** Unit tests for {@link CloudStorageReadChannel}. */ +/** + * Unit tests for {@link CloudStorageReadChannel}. + */ @RunWith(JUnit4.class) public class CloudStorageReadChannelTest { - @Rule - public final ExpectedException thrown = ExpectedException.none(); + @Rule public final ExpectedException thrown = ExpectedException.none(); - private final GcsService gcsService = mock(GcsService.class); - private final GcsInputChannel gcsChannel = mock(GcsInputChannel.class); - private final GcsFilename file = new GcsFilename("enya", "rocks"); - private final GcsFileOptions options = GcsFileOptions.getDefaultInstance(); - private final GcsFileMetadata metadata = new GcsFileMetadata(file, options, null, 42, null); private CloudStorageReadChannel chan; + private final Storage gcsStorage = mock(Storage.class); + private final BlobId file = BlobId.of("blob", "attack"); + private Blob metadata = mock(Blob.class); + private final ReadChannel gcsChannel = mock(ReadChannel.class); + @Before - public void before() throws Exception { - when(gcsService.getMetadata(eq(file))).thenReturn(metadata); - when(gcsService.openReadChannel(eq(file), anyInt())).thenReturn(gcsChannel); + public void before() throws IOException { + when(metadata.size()).thenReturn(42L); + when(gcsStorage.get(file)).thenReturn(metadata); + when(gcsStorage.reader(eq(file))).thenReturn(gcsChannel); when(gcsChannel.isOpen()).thenReturn(true); - chan = CloudStorageReadChannel.create(gcsService, file, 0); - verify(gcsService).getMetadata(eq(file)); - verify(gcsService).openReadChannel(eq(file), eq(0L)); + chan = CloudStorageReadChannel.create(gcsStorage, file, 0); + verify(gcsStorage).get(eq(file)); + verify(gcsStorage).reader(eq(file)); } @Test - public void testRead() throws Exception { + public void testRead() throws IOException { ByteBuffer buffer = ByteBuffer.allocate(1); when(gcsChannel.read(eq(buffer))).thenReturn(1); assertThat(chan.position()).isEqualTo(0L); @@ -61,86 +77,80 @@ public void testRead() throws Exception { assertThat(chan.position()).isEqualTo(1L); verify(gcsChannel).read(any(ByteBuffer.class)); verify(gcsChannel, times(3)).isOpen(); - verifyNoMoreInteractions(gcsService, gcsChannel); } @Test - public void testRead_whenClosed_throwsCce() throws Exception { + public void testRead_whenClosed_throwsCce() throws IOException { when(gcsChannel.isOpen()).thenReturn(false); thrown.expect(ClosedChannelException.class); chan.read(ByteBuffer.allocate(1)); } @Test - public void testWrite_throwsNonWritableChannelException() throws Exception { + public void testWrite_throwsNonWritableChannelException() throws IOException { thrown.expect(NonWritableChannelException.class); chan.write(ByteBuffer.allocate(1)); } @Test - public void testTruncate_throwsNonWritableChannelException() throws Exception { + public void testTruncate_throwsNonWritableChannelException() throws IOException { thrown.expect(NonWritableChannelException.class); chan.truncate(0); } @Test - public void testIsOpen() throws Exception { + public void testIsOpen() throws IOException { when(gcsChannel.isOpen()).thenReturn(true).thenReturn(false); assertThat(chan.isOpen()).isTrue(); chan.close(); assertThat(chan.isOpen()).isFalse(); verify(gcsChannel, times(2)).isOpen(); verify(gcsChannel).close(); - verifyNoMoreInteractions(gcsService, gcsChannel); } @Test - public void testSize() throws Exception { + public void testSize() throws IOException { assertThat(chan.size()).isEqualTo(42L); verify(gcsChannel).isOpen(); verifyZeroInteractions(gcsChannel); - verifyNoMoreInteractions(gcsService); } @Test - public void testSize_whenClosed_throwsCce() throws Exception { + public void testSize_whenClosed_throwsCce() throws IOException { when(gcsChannel.isOpen()).thenReturn(false); thrown.expect(ClosedChannelException.class); chan.size(); } @Test - public void testPosition_whenClosed_throwsCce() throws Exception { + public void testPosition_whenClosed_throwsCce() throws IOException { when(gcsChannel.isOpen()).thenReturn(false); thrown.expect(ClosedChannelException.class); chan.position(); } @Test - public void testSetPosition_whenClosed_throwsCce() throws Exception { + public void testSetPosition_whenClosed_throwsCce() throws IOException { when(gcsChannel.isOpen()).thenReturn(false); thrown.expect(ClosedChannelException.class); chan.position(0); } @Test - public void testClose_calledMultipleTimes_doesntThrowAnError() throws Exception { + public void testClose_calledMultipleTimes_doesntThrowAnError() throws IOException { chan.close(); chan.close(); chan.close(); } @Test - public void testSetPosition() throws Exception { + public void testSetPosition() throws IOException { assertThat(chan.position()).isEqualTo(0L); assertThat(chan.size()).isEqualTo(42L); chan.position(1L); assertThat(chan.position()).isEqualTo(1L); assertThat(chan.size()).isEqualTo(42L); - verify(gcsChannel).close(); + verify(gcsChannel).seek(1); verify(gcsChannel, times(5)).isOpen(); - verify(gcsService, times(2)).getMetadata(eq(file)); - verify(gcsService).openReadChannel(eq(file), eq(1L)); - verifyNoMoreInteractions(gcsService, gcsChannel); } } diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageWriteChannelTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageWriteChannelTest.java similarity index 93% rename from gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageWriteChannelTest.java rename to gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageWriteChannelTest.java index 7b0e298b56c0..b98d92b4c85e 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageWriteChannelTest.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageWriteChannelTest.java @@ -1,4 +1,4 @@ -package com.google.gcloud.storage.contrib.nio; +package com.google.cloud.storage.contrib.nio; import static com.google.common.truth.Truth.assertThat; import static org.mockito.Matchers.any; @@ -10,7 +10,7 @@ import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; -import com.google.appengine.tools.cloudstorage.GcsOutputChannel; +import com.google.cloud.WriteChannel; import org.junit.Before; import org.junit.Rule; @@ -30,11 +30,11 @@ public class CloudStorageWriteChannelTest { @Rule public final ExpectedException thrown = ExpectedException.none(); - private final GcsOutputChannel gcsChannel = mock(GcsOutputChannel.class); + private final WriteChannel gcsChannel = mock(WriteChannel.class); private CloudStorageWriteChannel chan = new CloudStorageWriteChannel(gcsChannel); @Before - public void before() throws Exception { + public void before() { when(gcsChannel.isOpen()).thenReturn(true); } diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/UnixPathTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/UnixPathTest.java similarity index 99% rename from gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/UnixPathTest.java rename to gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/UnixPathTest.java index e6417fc75c8c..78c1b7f34f25 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/UnixPathTest.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/UnixPathTest.java @@ -1,4 +1,4 @@ -package com.google.gcloud.storage.contrib.nio; +package com.google.cloud.storage.contrib.nio; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assume.assumeTrue; diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/it/ITGcsNio.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/it/ITGcsNio.java new file mode 100644 index 000000000000..e5ead7d6ce37 --- /dev/null +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/it/ITGcsNio.java @@ -0,0 +1,351 @@ +package com.google.cloud.storage.contrib.nio.it; + +import static com.google.common.truth.Truth.assertThat; +import static java.nio.charset.StandardCharsets.UTF_8; + +import com.google.cloud.storage.contrib.nio.CloudStorageConfiguration; +import com.google.cloud.storage.contrib.nio.CloudStorageFileSystem; +import com.google.common.collect.ImmutableList; +import com.google.cloud.storage.BlobInfo; +import com.google.cloud.storage.BucketInfo; +import com.google.cloud.storage.Storage; +import com.google.cloud.storage.StorageOptions; +import com.google.cloud.storage.testing.RemoteStorageHelper; + +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.io.ByteArrayOutputStream; +import java.io.EOFException; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.nio.ByteBuffer; +import java.nio.channels.ReadableByteChannel; +import java.nio.channels.SeekableByteChannel; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.util.Arrays; +import java.util.List; +import java.util.Random; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Integration test for gcloud-nio. This test actually talks to GCS (you need an account). + * Tests both reading and writing. + * + * You *must* set the GOOGLE_APPLICATION_CREDENTIALS environment variable + * for this test to work. It must contain the name of a local file that contains + * your Service Account JSON Key. + * + * The instructions for how to get the Service Account JSON Key are + * at https://cloud.google.com/storage/docs/authentication?hl=en#service_accounts + * + * The short version is this: go to cloud.google.com/console, + * select your project, search for "API manager", click "Credentials", + * click "create credentials/service account key", new service account, + * JSON. The contents of the file that's sent to your browsers is your + * "Service Account JSON Key". + * + */ +@RunWith(JUnit4.class) +public class ITGcsNio { + + private static final List FILE_CONTENTS = ImmutableList.of( + "Tous les êtres humains naissent libres et égaux en dignité et en droits.", + "Ils sont doués de raison et de conscience et doivent agir ", + "les uns envers les autres dans un esprit de fraternité."); + + private static final Logger log = Logger.getLogger(ITGcsNio.class.getName()); + private static final String BUCKET = RemoteStorageHelper.generateBucketName(); + private static final String SML_FILE = "tmp-test-small-file.txt"; + private static final int SML_SIZE = 100; + // it's big, relatively speaking. + private static final String BIG_FILE = "tmp-test-big-file.txt"; + // arbitrary size that's not too round. + private static final int BIG_SIZE = 2 * 1024 * 1024 - 50; + private static final String PREFIX = "tmp-test-file"; + private static Storage storage; + private static StorageOptions storageOptions; + + private final Random rnd = new Random(); + + @BeforeClass + public static void beforeClass() throws IOException { + // loads the credentials from local disk as par README + RemoteStorageHelper gcsHelper = RemoteStorageHelper.create(); + storageOptions = gcsHelper.options(); + storage = storageOptions.service(); + // create and populate test bucket + storage.create(BucketInfo.of(BUCKET)); + fillFile(storage, SML_FILE, SML_SIZE); + fillFile(storage, BIG_FILE, BIG_SIZE); + } + + @AfterClass + public static void afterClass() throws ExecutionException, InterruptedException { + if (storage != null && !RemoteStorageHelper.forceDelete(storage, BUCKET, 5, TimeUnit.SECONDS) && + log.isLoggable(Level.WARNING)) { + log.log(Level.WARNING, "Deletion of bucket {0} timed out, bucket is not empty", BUCKET); + } + } + + private static byte[] randomContents(int size) { + byte[] bytes = new byte[size]; + new Random(size).nextBytes(bytes); + return bytes; + } + + private static void fillFile(Storage storage, String fname, int size) throws IOException { + storage.create(BlobInfo.builder(BUCKET, fname).build(), randomContents(size)); + } + + @Test + public void testFileExists() throws IOException { + CloudStorageFileSystem testBucket = getTestBucket(); + Path path = testBucket.getPath(SML_FILE); + assertThat(Files.exists(path)).isTrue(); + } + + @Test + public void testFileSize() throws IOException { + CloudStorageFileSystem testBucket = getTestBucket(); + Path path = testBucket.getPath(SML_FILE); + assertThat(Files.size(path)).isEqualTo(SML_SIZE); + } + + @Test(timeout = 60_000) + public void testReadByteChannel() throws IOException { + CloudStorageFileSystem testBucket = getTestBucket(); + Path path = testBucket.getPath(SML_FILE); + long size = Files.size(path); + SeekableByteChannel chan = Files.newByteChannel(path, StandardOpenOption.READ); + assertThat(chan.size()).isEqualTo(size); + ByteBuffer buf = ByteBuffer.allocate(SML_SIZE); + int read = 0; + while (chan.isOpen()) { + int rc = chan.read(buf); + assertThat(chan.size()).isEqualTo(size); + if (rc < 0) { + // EOF + break; + } + assertThat(rc).isGreaterThan(0); + read += rc; + assertThat(chan.position()).isEqualTo(read); + } + assertThat(read).isEqualTo(size); + byte[] expected = new byte[SML_SIZE]; + new Random(SML_SIZE).nextBytes(expected); + assertThat(Arrays.equals(buf.array(), expected)).isTrue(); + } + + @Test + public void testSeek() throws IOException { + CloudStorageFileSystem testBucket = getTestBucket(); + Path path = testBucket.getPath(BIG_FILE); + int size = BIG_SIZE; + byte[] contents = randomContents(size); + byte[] sample = new byte[100]; + byte[] wanted; + byte[] wanted2; + SeekableByteChannel chan = Files.newByteChannel(path, StandardOpenOption.READ); + assertThat(chan.size()).isEqualTo(size); + + // check seek + int dest = size / 2; + chan.position(dest); + readFully(chan, sample); + wanted = Arrays.copyOfRange(contents, dest, dest + 100); + assertThat(wanted).isEqualTo(sample); + // now go back and check the beginning + // (we do 2 locations because 0 is sometimes a special case). + chan.position(0); + readFully(chan, sample); + wanted2 = Arrays.copyOf(contents, 100); + assertThat(wanted2).isEqualTo(sample); + // if the two spots in the file have the same contents, then this isn't a good file for this + // test. + assertThat(wanted).isNotEqualTo(wanted2); + } + + @Test + public void testCreate() throws IOException { + CloudStorageFileSystem testBucket = getTestBucket(); + Path path = testBucket.getPath(PREFIX + randomSuffix()); + // file shouldn't exist initially. If it does it's either because it's a leftover + // from a previous run (so we should delete the file) + // or because we're misconfigured and pointing to an actually important file + // (so we should absolutely not delete it). + // So if the file's here, don't try to fix it automatically, let the user deal with it. + assertThat(Files.exists(path)).isFalse(); + try { + Files.createFile(path); + // now it does, and it has size 0. + assertThat(Files.exists(path)).isTrue(); + long size = Files.size(path); + assertThat(size).isEqualTo(0); + } finally { + // let's not leave files around + Files.deleteIfExists(path); + } + } + + @Test + public void testWrite() throws IOException { + CloudStorageFileSystem testBucket = getTestBucket(); + Path path = testBucket.getPath(PREFIX + randomSuffix()); + // file shouldn't exist initially. If it does it's either because it's a leftover + // from a previous run (so we should delete the file) + // or because we're misconfigured and pointing to an actually important file + // (so we should absolutely not delete it). + // So if the file's here, don't try to fix it automatically, let the user deal with it. + assertThat(Files.exists(path)).isFalse(); + try { + Files.write(path, FILE_CONTENTS, UTF_8); + // now it does. + assertThat(Files.exists(path)).isTrue(); + + // let's check that the contents is OK. + ByteArrayOutputStream wantBytes = new ByteArrayOutputStream(); + PrintWriter writer = new PrintWriter(new OutputStreamWriter(wantBytes, UTF_8)); + for (String content : FILE_CONTENTS) { + writer.println(content); + } + writer.close(); + SeekableByteChannel chan = Files.newByteChannel(path, StandardOpenOption.READ); + byte[] gotBytes = new byte[(int) chan.size()]; + readFully(chan, gotBytes); + assertThat(gotBytes).isEqualTo(wantBytes.toByteArray()); + } finally { + // let's not leave files around + Files.deleteIfExists(path); + } + } + + @Test + public void testCreateAndWrite() throws IOException { + CloudStorageFileSystem testBucket = getTestBucket(); + Path path = testBucket.getPath(PREFIX + randomSuffix()); + // file shouldn't exist initially (see above). + assertThat(Files.exists(path)).isFalse(); + try { + Files.createFile(path); + Files.write(path, FILE_CONTENTS, UTF_8); + // now it does. + assertThat(Files.exists(path)).isTrue(); + + // let's check that the contents is OK. + ByteArrayOutputStream wantBytes = new ByteArrayOutputStream(); + PrintWriter writer = new PrintWriter(new OutputStreamWriter(wantBytes, UTF_8)); + for (String content : FILE_CONTENTS) { + writer.println(content); + } + writer.close(); + SeekableByteChannel chan = Files.newByteChannel(path, StandardOpenOption.READ); + byte[] gotBytes = new byte[(int) chan.size()]; + readFully(chan, gotBytes); + assertThat(gotBytes).isEqualTo(wantBytes.toByteArray()); + } finally { + // let's not leave files around + Files.deleteIfExists(path); + } + } + + @Test + public void testWriteOnClose() throws Exception { + CloudStorageFileSystem testBucket = getTestBucket(); + Path path = testBucket.getPath(PREFIX + randomSuffix()); + // file shouldn't exist initially (see above) + assertThat(Files.exists(path)).isFalse(); + try { + long expectedSize = 0; + try (SeekableByteChannel chan = Files.newByteChannel(path, StandardOpenOption.WRITE)) { + // writing lots of contents to defeat channel-internal buffering. + for (String s : FILE_CONTENTS) { + byte[] sBytes = s.getBytes(UTF_8); + expectedSize += sBytes.length * 9999; + for (int i = 0; i < 9999; i++) { + chan.write(ByteBuffer.wrap(sBytes)); + } + } + try { + Files.size(path); + // we shouldn't make it to this line. Not using thrown.expect because + // I still want to run a few lines after the exception. + Assert.fail("Files.size should have thrown an exception"); + } catch (NoSuchFileException nsf) { + // that's what we wanted, we're good. + } + } + // channel now closed, the file should be there and with the new contents. + assertThat(Files.exists(path)).isTrue(); + assertThat(Files.size(path)).isEqualTo(expectedSize); + } finally { + Files.deleteIfExists(path); + } + } + + @Test + public void testCopy() throws IOException { + CloudStorageFileSystem testBucket = getTestBucket(); + Path src = testBucket.getPath(SML_FILE); + Path dst = testBucket.getPath(PREFIX + randomSuffix()); + // file shouldn't exist initially (see above). + assertThat(Files.exists(dst)).isFalse(); + try { + Files.copy(src, dst); + + assertThat(Files.exists(dst)).isTrue(); + assertThat(Files.size(dst)).isEqualTo(SML_SIZE); + byte[] got = new byte[SML_SIZE]; + readFully(Files.newByteChannel(dst), got); + assertThat(got).isEqualTo(randomContents(SML_SIZE)); + } finally { + // let's not leave files around + Files.deleteIfExists(dst); + } + } + + private int readFully(ReadableByteChannel chan, byte[] outputBuf) throws IOException { + ByteBuffer buf = ByteBuffer.wrap(outputBuf); + int sofar = 0; + int bytes = buf.remaining(); + while (sofar < bytes) { + int read = chan.read(buf); + if (read < 0) { + throw new EOFException("channel EOF"); + } + sofar += read; + } + return sofar; + } + + private String randomSuffix() { + return "-" + rnd.nextInt(99999); + } + + + private CloudStorageFileSystem getTestBucket() throws IOException { + // in typical usage we use the single-argument version of forBucket + // and rely on the user being logged into their project with the + // gcloud tool, and then everything authenticates automagically + // (or we just use paths that start with "gs://" and rely on NIO's magic). + // + // However for the tests we want to be able to run in automated environments + // where we can set environment variables but not necessarily install gcloud + // or run it. That's why we're setting the credentials programmatically. + return CloudStorageFileSystem.forBucket( + BUCKET, CloudStorageConfiguration.DEFAULT, storageOptions); + } + +} diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/AppEngineRule.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/AppEngineRule.java deleted file mode 100644 index 090ffa04ef77..000000000000 --- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/AppEngineRule.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.google.gcloud.storage.contrib.nio; - -import com.google.appengine.tools.development.testing.LocalDatastoreServiceTestConfig; -import com.google.appengine.tools.development.testing.LocalServiceTestHelper; - -import org.junit.rules.ExternalResource; - -import java.io.IOException; - -/** JUnit rule for App Engine testing environment. */ -public final class AppEngineRule extends ExternalResource { - - private LocalServiceTestHelper helper; - - @Override - protected void before() throws IOException { - helper = new LocalServiceTestHelper(new LocalDatastoreServiceTestConfig()); - helper.setUp(); - } - - @Override - protected void after() { - helper.tearDown(); - } -} diff --git a/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/Stat.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/Stat.java new file mode 100644 index 000000000000..7a83706124e6 --- /dev/null +++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/Stat.java @@ -0,0 +1,104 @@ +package com.google.cloud.examples.nio; + +import java.io.IOException; +import java.net.URI; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.spi.FileSystemProvider; + +/** + * Stat is a super-simple program that just displays the size of the file + * passed as argument. + * + *

It's meant to be used to test GCloud's integration with Java NIO. + * + *

You can either use the '--check' argument to see whether GCS is enabled, + * or you can directly pass in a GCS file name to use. In that case you have to + * be logged in (using e.g. the gcloud auth command). + * + *

See the README for a command line to run this example. + */ +public class Stat { + + /** + * See the class documentation. + */ + public static void main(String[] args) throws IOException { + if (args.length == 0 || args[0].equals("--help")) { + help(); + return; + } + if (args[0].equals("--list")) { + listFilesystems(); + return; + } + if (args[0].equals("--check")) { + checkGcs(); + return; + } + for (String a : args) { + statFile(a); + } + } + + /** + * Print the length of the indicated file. + * + *

This uses the normal Java NIO Api, so it can take advantage of any installed + * NIO Filesystem provider without any extra effort. + */ + private static void statFile(String fname) { + try { + Path path = Paths.get(new URI(fname)); + long size = Files.size(path); + System.out.println(fname + ": " + size + " bytes."); + } catch (Exception ex) { + System.out.println(fname + ": " + ex.toString()); + } + } + + private static void help() { + String[] help = + {"The arguments can be one of:", + " * ", + " to display the length of that file.", + "", + " * --list", + " to list the filesystem providers.", + "", + " * --check", + " to double-check the GCS provider is installed.", + "", + "The purpose of this tool is to demonstrate that the gcloud NIO filesystem provider", + "can add Google Cloud Storage support to programs not explicitly designed for it.", + "", + "This tool normally knows nothing of Google Cloud Storage. If you pass it --check", + "or a GCS file name (e.g. gs://mybucket/myfile), it will show an error.", + "However, by just adding the gcloud-nio jar in your classpath, this tool is made", + "aware of gs:// paths and can access files on the cloud.", + "", + "The gcloud NIO filesystem provider can similarly enable existing Java 7 programs", + "to read and write cloud files, even if they have no special built-in cloud support." + }; + for (String s : help) { + System.out.println(s); + } + } + + private static void listFilesystems() { + System.out.println("Installed filesystem providers:"); + for (FileSystemProvider p : FileSystemProvider.installedProviders()) { + System.out.println(" " + p.getScheme()); + } + } + + private static void checkGcs() { + FileSystem fs = FileSystems.getFileSystem(URI.create("gs://domain-registry-alpha")); + System.out.println("We seem to be able to instantiate a gs:// filesystem."); + System.out.println("isOpen: " + fs.isOpen()); + System.out.println("isReadOnly: " + fs.isReadOnly()); + } +} diff --git a/gcloud-java-storage/src/main/java/com/google/cloud/storage/testing/FakeStorageRpc.java b/gcloud-java-storage/src/main/java/com/google/cloud/storage/testing/FakeStorageRpc.java new file mode 100644 index 000000000000..3e3c0d11eeec --- /dev/null +++ b/gcloud-java-storage/src/main/java/com/google/cloud/storage/testing/FakeStorageRpc.java @@ -0,0 +1,279 @@ +package com.google.cloud.storage.testing; + +import com.google.api.services.storage.model.Bucket; +import com.google.api.services.storage.model.StorageObject; +import com.google.cloud.storage.StorageBatch; +import com.google.cloud.storage.spi.RpcBatch; +import com.google.cloud.storage.spi.StorageRpc; +import com.google.cloud.storage.Storage; +import com.google.cloud.storage.StorageException; + +import java.io.IOException; +import java.io.InputStream; +import java.math.BigInteger; +import java.nio.file.FileAlreadyExistsException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +import javax.annotation.concurrent.NotThreadSafe; + +/** + * A bare-bones in-memory implementation of Storage, meant for testing. + * See LocalGcsHelper. + * + * This class is NOT thread-safe. + */ +@NotThreadSafe +public class FakeStorageRpc implements StorageRpc { + + // fullname -> metadata + Map stuff = new HashMap<>(); + // fullname -> contents + Map contents = new HashMap<>(); + // fullname -> future contents that will be visible on close. + Map futureContents = new HashMap<>(); + + private final boolean throwIfOption; + + /** + * @param throwIfOption if true, we throw when given any option. + */ + public FakeStorageRpc(boolean throwIfOption) { + this.throwIfOption = throwIfOption; + } + + // remove all files + void reset() { + stuff = new HashMap<>(); + contents = new HashMap<>(); + } + + @Override + public Bucket create(Bucket bucket, Map options) throws StorageException { + throw new UnsupportedOperationException(); + } + + @Override + public StorageObject create(StorageObject object, InputStream content, Map options) + throws StorageException { + potentiallyThrow(options); + String key = fullname(object); + stuff.put(key, object); + try { + contents.put(key, com.google.common.io.ByteStreams.toByteArray(content)); + } catch (IOException e) { + throw new StorageException(e); + } + // TODO: crc, etc + return object; + } + + @Override + public Tuple> list(Map options) throws StorageException { + throw new UnsupportedOperationException(); + } + + @Override + public Tuple> list(String bucket, Map options) + throws StorageException { + potentiallyThrow(options); + return null; + } + + /** + * Returns the requested bucket or {@code null} if not found. + */ + @Override + public Bucket get(Bucket bucket, Map options) throws StorageException { + potentiallyThrow(options); + return null; + } + + /** + * Returns the requested storage object or {@code null} if not found. + */ + @Override + public StorageObject get(StorageObject object, Map options) throws StorageException { + // we allow the "ID" option because we need to, but then we give a whole answer anyways + // because the caller won't mind the extra fields. + if (throwIfOption && !options.isEmpty() && options.size()>1 + && options.keySet().toArray()[0] != Storage.BlobGetOption.fields(Storage.BlobField.ID)) { + throw new UnsupportedOperationException(); + } + + String key = fullname(object); + if (stuff.containsKey(key)) { + StorageObject ret = stuff.get(key); + if (contents.containsKey(key)) { + ret.setSize(BigInteger.valueOf(contents.get(key).length)); + } + ret.setId(key); + return ret; + } + return null; + } + + @Override + public Bucket patch(Bucket bucket, Map options) throws StorageException { + potentiallyThrow(options); + return null; + } + + @Override + public StorageObject patch(StorageObject storageObject, Map options) + throws StorageException { + potentiallyThrow(options); + return null; + } + + @Override + public boolean delete(Bucket bucket, Map options) throws StorageException { + return false; + } + + @Override + public boolean delete(StorageObject object, Map options) throws StorageException { + String key = fullname(object); + contents.remove(key); + return null != stuff.remove(key); + } + + @Override + public RpcBatch createBatch() { + return null; + } + + @Override + public StorageObject compose(Iterable sources, StorageObject target, + Map targetOptions) throws StorageException { + return null; + } + + @Override + public byte[] load(StorageObject storageObject, Map options) throws StorageException { + String key = fullname(storageObject); + if (!contents.containsKey(key)) { + throw new StorageException(404, "File not found: " + key); + } + return contents.get(key); + } + + @Override + public Tuple read( + StorageObject from, Map options, long zposition, int zbytes) + throws StorageException { + potentiallyThrow(options); + String key = fullname(from); + if (!contents.containsKey(key)) { + throw new StorageException(404, "File not found: " + key); + } + long position = zposition; + int bytes = zbytes; + if (position < 0) { + position = 0; + } + byte[] full = contents.get(key); + if ((int) position + bytes > full.length) { + bytes = full.length - (int) position; + } + if (bytes <= 0) { + // special case: you're trying to read past the end + return Tuple.of("etag-goes-here", new byte[0]); + } + byte[] ret = new byte[bytes]; + System.arraycopy(full, (int) position, ret, 0, bytes); + return Tuple.of("etag-goes-here", ret); + } + + @Override + public String open(StorageObject object, Map options) throws StorageException { + String key = fullname(object); + boolean mustNotExist = false; + for (Option option : options.keySet()) { + if (option instanceof StorageRpc.Option) { + // this is a bit of a hack, since we don't implement generations. + if ((StorageRpc.Option) option == Option.IF_GENERATION_MATCH + && ((Long) options.get(option)).longValue() == 0L) { + mustNotExist = true; + } + } + } + if (mustNotExist && stuff.containsKey(key)) { + throw new StorageException(new FileAlreadyExistsException(key)); + } + stuff.put(key, object); + + return fullname(object); + } + + @Override + public void write(String uploadId, byte[] toWrite, int toWriteOffset, long destOffset, + int length, boolean last) throws StorageException { + // this may have a lot more allocations than ideal, but it'll work. + byte[] bytes; + if (futureContents.containsKey(uploadId)) { + bytes = futureContents.get(uploadId); + if (bytes.length < length + destOffset) { + bytes = new byte[(int) (length + destOffset)]; + } + } else { + bytes = new byte[(int) (length + destOffset)]; + } + System.arraycopy(toWrite, toWriteOffset, bytes, (int) destOffset, length); + // we want to mimic the GCS behavior that file contents are only visible on close. + if (last) { + contents.put(uploadId, bytes); + futureContents.remove(uploadId); + } else { + futureContents.put(uploadId, bytes); + } + } + + @Override + public RewriteResponse openRewrite(RewriteRequest rewriteRequest) throws StorageException { + String sourceKey = fullname(rewriteRequest.source); + // a little hackish, just good enough for the tests to work. + if (!contents.containsKey(sourceKey)) { + throw new StorageException(404, "File not found: " + sourceKey); + } + + boolean mustNotExist = false; + for (Option option : rewriteRequest.targetOptions.keySet()) { + if (option instanceof StorageRpc.Option) { + // this is a bit of a hack, since we don't implement generations. + if ((StorageRpc.Option) option == Option.IF_GENERATION_MATCH + && ((Long) rewriteRequest.targetOptions.get(option)).longValue() == 0L) { + mustNotExist = true; + } + } + } + + String destKey = fullname(rewriteRequest.target); + if (mustNotExist && contents.containsKey(destKey)) { + throw new StorageException(new FileAlreadyExistsException(destKey)); + } + + stuff.put(destKey, rewriteRequest.target); + + byte[] data = contents.get(sourceKey); + contents.put(destKey, Arrays.copyOf(data, data.length)); + return new RewriteResponse(rewriteRequest, rewriteRequest.target, data.length, true, + "rewriteToken goes here", data.length); + } + + @Override + public RewriteResponse continueRewrite(RewriteResponse previousResponse) throws StorageException { + throw new UnsupportedOperationException(); + } + + private String fullname(StorageObject so) { + return (so.getBucket() + "/" + so.getName()); + } + + private void potentiallyThrow(Map options) throws UnsupportedOperationException { + if (throwIfOption && !options.isEmpty()) { + throw new UnsupportedOperationException(); + } + } +} diff --git a/gcloud-java-storage/src/main/java/com/google/cloud/storage/testing/LocalGcsHelper.java b/gcloud-java-storage/src/main/java/com/google/cloud/storage/testing/LocalGcsHelper.java new file mode 100644 index 000000000000..deb12c896511 --- /dev/null +++ b/gcloud-java-storage/src/main/java/com/google/cloud/storage/testing/LocalGcsHelper.java @@ -0,0 +1,68 @@ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * + * 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 com.google.cloud.storage.testing; + +import com.google.cloud.spi.ServiceRpcFactory; +import com.google.cloud.storage.spi.StorageRpc; +import com.google.cloud.storage.StorageOptions; + +/** + * Utility to create an in-memory storage configuration for testing. Storage options can be + * obtained via the {@link #options()} method. Returned options will point to FakeStorageRpc. + */ +public class LocalGcsHelper { + + // used for testing. Will throw if you pass it an option. + private static final FakeStorageRpc instance = new FakeStorageRpc(true); + + /** + * Returns a {@link StorageOptions} that use the static FakeStorageRpc instance, + * and resets it first so you start from a clean slate. + * That instance will throw if you pass it any option. * + */ + public static StorageOptions options() { + instance.reset(); + return StorageOptions.builder() + .projectId("dummy-project-for-testing") + .serviceRpcFactory( + new ServiceRpcFactory() { + @Override + public StorageRpc create(StorageOptions options) { + return instance; + } + }) + .build(); + } + + /** + * Returns a {@link StorageOptions} that creates a new FakeStorageRpc instance + * with the given option. + */ + public static StorageOptions customOptions(final boolean throwIfOptions) { + return StorageOptions.builder() + .projectId("dummy-project-for-testing") + .serviceRpcFactory( + new ServiceRpcFactory() { + @Override + public StorageRpc create(StorageOptions options) { + return new FakeStorageRpc(throwIfOptions); + } + }) + .build(); + } + +} diff --git a/gcloud-java-storage/src/main/java/com/google/cloud/storage/testing/package-info.java b/gcloud-java-storage/src/main/java/com/google/cloud/storage/testing/package-info.java index a8732b029900..3fbf95fb29f7 100644 --- a/gcloud-java-storage/src/main/java/com/google/cloud/storage/testing/package-info.java +++ b/gcloud-java-storage/src/main/java/com/google/cloud/storage/testing/package-info.java @@ -15,7 +15,9 @@ */ /** - * A testing helper for Google Cloud Storage. + * Two testing helpers for Google Cloud Storage. + * + * RemoteGcsHelper helps with testing on the actual cloud. * *

A simple usage example: * @@ -32,6 +34,21 @@ * RemoteStorageHelper.forceDelete(storage, bucket, 5, TimeUnit.SECONDS); * } * + * LocalGcsHelper helps with testing on an in-memory filesystem (this is best for unit tests). + * Note that this filesystem isn't a complete implementation. In particular, it is not thread-safe. + * + *

A simple usage example: + * + *

+ * CloudStorageFileSystemProvider.setGCloudOptions(LocalGcsHelper.options());
+ * Path path = Paths.get(URI.create("gs://bucket/wednesday"));
+ * thrown.expect(NoSuchFileException.class);
+ * Files.newByteChannel(path);
+ * 
+ * + *

The first line ensures that the test uses the in-memory GCS instead of the real one + * (note that you have to set the cloud options before the first usage of the gs:// prefix). + * * @see * gcloud-java tools for testing */ From 9784842ef3755e0256529f3ea60378d800d50917 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Wed, 2 Mar 2016 19:23:54 -0500 Subject: [PATCH 03/38] Run google-java-format on GCS NIO I also made the following additional changes: - Don't use one-line javadocs, per request of @aozarov - Use public domain John Keats poetry published in 1819 - Minor cleanup to a few JavaDocs --- .../nio/CloudStorageConfiguration.java | 9 +- .../nio/CloudStorageFileAttributeView.java | 25 ++--- .../contrib/nio/CloudStorageFileSystem.java | 63 ++++++----- .../nio/CloudStorageFileSystemProvider.java | 105 ++++++++++-------- .../nio/CloudStorageObjectAttributes.java | 9 +- .../CloudStorageObjectImmutableException.java | 5 +- .../contrib/nio/CloudStorageOption.java | 16 ++- .../contrib/nio/CloudStorageOptions.java | 12 +- .../storage/contrib/nio/CloudStoragePath.java | 42 ++++--- ...CloudStoragePseudoDirectoryAttributes.java | 6 +- .../CloudStoragePseudoDirectoryException.java | 5 +- .../contrib/nio/CloudStorageReadChannel.java | 11 +- .../storage/contrib/nio/CloudStorageUtil.java | 23 ++-- .../cloud/storage/contrib/nio/UnixPath.java | 85 +++++++++----- .../storage/contrib/nio/package-info.java | 4 +- .../nio/CloudStorageConfigurationTest.java | 39 ++++--- .../CloudStorageFileAttributeViewTest.java | 16 ++- .../nio/CloudStorageFileAttributesTest.java | 22 ++-- .../CloudStorageFileSystemProviderTest.java | 92 ++++++++------- .../nio/CloudStorageFileSystemTest.java | 28 ++--- .../contrib/nio/CloudStorageOptionsTest.java | 34 +++--- .../contrib/nio/CloudStoragePathTest.java | 30 ++--- .../nio/CloudStorageWriteChannelTest.java | 7 +- .../storage/contrib/nio/UnixPathTest.java | 16 ++- 24 files changed, 399 insertions(+), 305 deletions(-) diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageConfiguration.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageConfiguration.java index 0fbe7dff4c93..5de5f2b04dd5 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageConfiguration.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageConfiguration.java @@ -6,12 +6,14 @@ import java.util.Map; -/** CloudStorageConfiguration is the configuration class for - * {@link CloudStorageFileSystem#forBucket}. */ +/** + * Configuration for a {@link CloudStorageFileSystem} instance. + */ @AutoValue public abstract class CloudStorageConfiguration { - /** Returns the path of the current working directory. Defaults to the root directory. + /** + * Returns path of the current working directory. This defaults to the root directory. */ public abstract String workingDirectory(); @@ -107,7 +109,6 @@ public Builder blockSize(int value) { return this; } - /** Creates a new instance, but does not destroy the builder. */ public CloudStorageConfiguration build() { diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributeView.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributeView.java index 526122e45ef6..63168210c8c4 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributeView.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributeView.java @@ -1,6 +1,6 @@ package com.google.cloud.storage.contrib.nio; -import static com.google.common.base.Verify.verifyNotNull; +import static com.google.common.base.Preconditions.checkNotNull; import com.google.cloud.storage.BlobInfo; import com.google.cloud.storage.Storage; @@ -15,21 +15,22 @@ import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; -/** Metadata view for a Google Cloud Storage object. +/** + * Metadata view for a Google Cloud Storage object. */ @Immutable public final class CloudStorageFileAttributeView implements BasicFileAttributeView { - //private final CloudStorageFileSystemProvider provider; private final Storage storage; private final CloudStoragePath path; CloudStorageFileAttributeView(Storage storage, CloudStoragePath path) { - this.storage = verifyNotNull(storage); - this.path = verifyNotNull(path); + this.storage = checkNotNull(storage); + this.path = checkNotNull(path); } - /** Returns {@value CloudStorageFileSystem#GCS_VIEW}. + /** + * Returns {@value CloudStorageFileSystem#GCS_VIEW}. */ @Override public String name() { @@ -38,8 +39,7 @@ public String name() { @Override public CloudStorageFileAttributes readAttributes() throws IOException { - if (path.seemsLikeADirectory() - && path.getFileSystem().config().usePseudoDirectories()) { + if (path.seemsLikeADirectory() && path.getFileSystem().config().usePseudoDirectories()) { return new CloudStoragePseudoDirectoryAttributes(path); } BlobInfo blobInfo = storage.get(path.getBlobId()); @@ -62,8 +62,8 @@ public void setTimes(FileTime lastModifiedTime, FileTime lastAccessTime, FileTim public boolean equals(@Nullable Object other) { return this == other || other instanceof CloudStorageFileAttributeView - && Objects.equals(storage, ((CloudStorageFileAttributeView) other).storage) - && Objects.equals(path, ((CloudStorageFileAttributeView) other).path); + && Objects.equals(storage, ((CloudStorageFileAttributeView) other).storage) + && Objects.equals(path, ((CloudStorageFileAttributeView) other).path); } @Override @@ -73,9 +73,6 @@ public int hashCode() { @Override public String toString() { - return MoreObjects.toStringHelper(this) - .add("storage", storage) - .add("path", path) - .toString(); + return MoreObjects.toStringHelper(this).add("storage", storage).add("path", path).toString(); } } diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystem.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystem.java index cceab822d7f9..b768ba757329 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystem.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystem.java @@ -59,8 +59,8 @@ public static CloudStorageFileSystem forBucket(String bucket) { * @see #forBucket(String) */ public static CloudStorageFileSystem forBucket(String bucket, CloudStorageConfiguration config) { - checkArgument(!bucket.startsWith(URI_SCHEME + ":"), - "Bucket name must not have schema: %s", bucket); + checkArgument( + !bucket.startsWith(URI_SCHEME + ":"), "Bucket name must not have schema: %s", bucket); return new CloudStorageFileSystem( new CloudStorageFileSystemProvider(), bucket, checkNotNull(config)); } @@ -100,9 +100,7 @@ public static CloudStorageFileSystem forBucket(String bucket, CloudStorageConfig private final CloudStorageConfiguration config; CloudStorageFileSystem( - CloudStorageFileSystemProvider provider, - String bucket, - CloudStorageConfiguration config) { + CloudStorageFileSystemProvider provider, String bucket, CloudStorageConfiguration config) { checkArgument(!bucket.isEmpty(), "bucket"); this.provider = provider; this.bucket = bucket; @@ -114,47 +112,56 @@ public CloudStorageFileSystemProvider provider() { return provider; } - /** Returns the Cloud Storage bucket name being served by this file system. - */ + /** + * Returns the Cloud Storage bucket name being served by this file system. + */ public String bucket() { return bucket; } - /** Returns the configuration object for this filesystem instance. - */ + /** + * Returns the configuration object for this filesystem instance. + */ public CloudStorageConfiguration config() { return config; } - /** Converts a cloud storage object name to a {@link Path} object. - */ + /** + * Converts a cloud storage object name to a {@link Path} object. + */ @Override public CloudStoragePath getPath(String first, String... more) { - checkArgument(!first.startsWith(URI_SCHEME + ":"), - "GCS FileSystem.getPath() must not have schema and bucket name: %s", first); + checkArgument( + !first.startsWith(URI_SCHEME + ":"), + "GCS FileSystem.getPath() must not have schema and bucket name: %s", + first); return CloudStoragePath.getPath(this, first, more); } - /** Does nothing. - */ + /** + * Does nothing. + */ @Override public void close() {} - /** Returns {@code true}. - */ + /** + * Returns {@code true}. + */ @Override public boolean isOpen() { return true; } - /** Returns {@code false}. - */ + /** + * Returns {@code false}. + */ @Override public boolean isReadOnly() { return false; } - /** Returns {@value UnixPath#SEPARATOR}. + /** + * Returns {@value UnixPath#SEPARATOR}. */ @Override public String getSeparator() { @@ -176,22 +183,26 @@ public Set supportedFileAttributeViews() { return SUPPORTED_VIEWS; } - /** Always throws {@link UnsupportedOperationException}. */ + /** + * Throws {@link UnsupportedOperationException} because this feature hasn't been implemented yet. + */ @Override public PathMatcher getPathMatcher(String syntaxAndPattern) { // TODO: Implement me. throw new UnsupportedOperationException(); } - /** Always throws {@link UnsupportedOperationException}. - */ + /** + * Throws {@link UnsupportedOperationException} because this feature hasn't been implemented yet. + */ @Override public UserPrincipalLookupService getUserPrincipalLookupService() { // TODO: Implement me. throw new UnsupportedOperationException(); } - /** Always throws {@link UnsupportedOperationException}. + /** + * Throws {@link UnsupportedOperationException} because this feature hasn't been implemented yet. */ @Override public WatchService newWatchService() throws IOException { @@ -203,8 +214,8 @@ public WatchService newWatchService() throws IOException { public boolean equals(@Nullable Object other) { return this == other || other instanceof CloudStorageFileSystem - && Objects.equals(config, ((CloudStorageFileSystem) other).config) - && Objects.equals(bucket, ((CloudStorageFileSystem) other).bucket); + && Objects.equals(config, ((CloudStorageFileSystem) other).config) + && Objects.equals(bucket, ((CloudStorageFileSystem) other).bucket); } @Override diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProvider.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProvider.java index bf6b1e2657a0..38086e7a1439 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProvider.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProvider.java @@ -67,7 +67,9 @@ public final class CloudStorageFileSystemProvider extends FileSystemProvider { // used only when we create a new instance of CloudStorageFileSystemProvider. private static StorageOptions storageOptions; - /** Those options are only used by the CloudStorageFileSystemProvider ctor. */ + /** + * Sets options that are only used by the constructor. + */ @VisibleForTesting public static void setGCloudOptions(StorageOptions newStorageOptions) { storageOptions = newStorageOptions; @@ -96,25 +98,35 @@ public String getScheme() { return CloudStorageFileSystem.URI_SCHEME; } - /** Returns cloud storage file system, provided a URI with no path, e.g. {@code gs://bucket} */ + /** + * Returns cloud storage file system, provided a URI with no path, e.g. {@code gs://bucket}. + */ @Override public CloudStorageFileSystem getFileSystem(URI uri) { return newFileSystem(uri, Collections.emptyMap()); } - /** Returns cloud storage file system, provided a URI with no path, e.g. {@code gs://bucket} */ + /** + * Returns cloud storage file system, provided a URI with no path, e.g. {@code gs://bucket}. + */ @Override public CloudStorageFileSystem newFileSystem(URI uri, Map env) { - checkArgument(uri.getScheme().equalsIgnoreCase(CloudStorageFileSystem.URI_SCHEME), - "Cloud Storage URIs must have '%s' scheme: %s", CloudStorageFileSystem.URI_SCHEME, uri); - checkArgument(!isNullOrEmpty(uri.getHost()), - "%s:// URIs must have a host: %s", CloudStorageFileSystem.URI_SCHEME, uri); - checkArgument(uri.getPort() == -1 - && isNullOrEmpty(uri.getPath()) - && isNullOrEmpty(uri.getQuery()) - && isNullOrEmpty(uri.getFragment()) - && isNullOrEmpty(uri.getUserInfo()), - "GCS FileSystem URIs mustn't have: port, userinfo, path, query, or fragment: %s", uri); + checkArgument( + uri.getScheme().equalsIgnoreCase(CloudStorageFileSystem.URI_SCHEME), + "Cloud Storage URIs must have '%s' scheme: %s", + CloudStorageFileSystem.URI_SCHEME, + uri); + checkArgument( + !isNullOrEmpty(uri.getHost()), "%s:// URIs must have a host: %s", + CloudStorageFileSystem.URI_SCHEME, uri); + checkArgument( + uri.getPort() == -1 + && isNullOrEmpty(uri.getPath()) + && isNullOrEmpty(uri.getQuery()) + && isNullOrEmpty(uri.getFragment()) + && isNullOrEmpty(uri.getUserInfo()), + "GCS FileSystem URIs mustn't have: port, userinfo, path, query, or fragment: %s", + uri); checkBucket(uri.getHost()); return new CloudStorageFileSystem(this, uri.getHost(), CloudStorageConfiguration.fromMap(env)); } @@ -137,8 +149,8 @@ public SeekableByteChannel newByteChannel( } } - private SeekableByteChannel newReadChannel( - Path path, Set options) throws IOException { + private SeekableByteChannel newReadChannel(Path path, Set options) + throws IOException { for (OpenOption option : options) { if (option instanceof StandardOpenOption) { switch ((StandardOpenOption) option) { @@ -171,8 +183,8 @@ private SeekableByteChannel newReadChannel( return CloudStorageReadChannel.create(storage, cloudPath.getBlobId(), 0); } - private SeekableByteChannel newWriteChannel( - Path path, Set options) throws IOException { + private SeekableByteChannel newWriteChannel(Path path, Set options) + throws IOException { CloudStoragePath cloudPath = checkPath(path); if (cloudPath.seemsLikeADirectoryAndUsePseudoDirectories()) { @@ -183,7 +195,6 @@ private SeekableByteChannel newWriteChannel( List writeOptions = new ArrayList<>(); List acls = new ArrayList<>(); - HashMap metas = new HashMap<>(); for (OpenOption option : options) { if (option instanceof OptionMimeType) { @@ -238,8 +249,9 @@ private SeekableByteChannel newWriteChannel( } try { - return new CloudStorageWriteChannel(storage.writer(infoBuilder.build(), - writeOptions.toArray(new Storage.BlobWriteOption[0]))); + return new CloudStorageWriteChannel( + storage.writer( + infoBuilder.build(), writeOptions.toArray(new Storage.BlobWriteOption[0]))); } catch (StorageException oops) { throw asIOException(oops); } @@ -279,7 +291,9 @@ public void delete(Path path) throws IOException { public void move(Path source, Path target, CopyOption... options) throws IOException { for (CopyOption option : options) { if (option == StandardCopyOption.ATOMIC_MOVE) { - throw new AtomicMoveNotSupportedException(source.toString(), target.toString(), + throw new AtomicMoveNotSupportedException( + source.toString(), + target.toString(), "Google Cloud Storage does not support atomic move operations."); } } @@ -326,8 +340,8 @@ public void copy(Path source, Path target, CopyOption... options) throws IOExcep tgtInfoBuilder.contentEncoding(((OptionContentEncoding) option).contentEncoding()); setContentEncoding = true; } else if (option instanceof OptionContentDisposition) { - tgtInfoBuilder.contentDisposition(((OptionContentDisposition) option) - .contentDisposition()); + tgtInfoBuilder.contentDisposition( + ((OptionContentDisposition) option).contentDisposition()); setContentDisposition = true; } else { throw new UnsupportedOperationException(option.toString()); @@ -339,9 +353,12 @@ public void copy(Path source, Path target, CopyOption... options) throws IOExcep CloudStoragePath fromPath = checkPath(source); - blockSize = blockSize != -1 ? blockSize - : Ints.max(fromPath.getFileSystem().config().blockSize(), - toPath.getFileSystem().config().blockSize()); + blockSize = + blockSize != -1 + ? blockSize + : Ints.max( + fromPath.getFileSystem().config().blockSize(), + toPath.getFileSystem().config().blockSize()); // TODO: actually use blockSize if (fromPath.seemsLikeADirectory() && toPath.seemsLikeADirectory()) { @@ -350,8 +367,9 @@ public void copy(Path source, Path target, CopyOption... options) throws IOExcep // NOOP: This would normally create an empty directory. return; } else { - checkArgument(!fromPath.getFileSystem().config().usePseudoDirectories() - && !toPath.getFileSystem().config().usePseudoDirectories(), + checkArgument( + !fromPath.getFileSystem().config().usePseudoDirectories() + && !toPath.getFileSystem().config().usePseudoDirectories(), "File systems associated with paths don't agree on pseudo-directories."); } } @@ -363,8 +381,6 @@ public void copy(Path source, Path target, CopyOption... options) throws IOExcep } try { - - if (wantCopyAttributes) { BlobInfo blobInfo = storage.get(fromPath.getBlobId()); if (null == blobInfo) { @@ -387,8 +403,8 @@ public void copy(Path source, Path target, CopyOption... options) throws IOExcep } BlobInfo tgtInfo = tgtInfoBuilder.build(); - Storage.CopyRequest.Builder copyReqBuilder = Storage.CopyRequest.builder() - .source(fromPath.getBlobId()); + Storage.CopyRequest.Builder copyReqBuilder = + Storage.CopyRequest.builder().source(fromPath.getBlobId()); if (wantReplaceExisting) { copyReqBuilder = copyReqBuilder.target(tgtInfo); } else { @@ -401,13 +417,13 @@ public void copy(Path source, Path target, CopyOption... options) throws IOExcep } } - @Override public boolean isSameFile(Path path, Path path2) { return checkPath(path).equals(checkPath(path2)); } - /** Returns {@code false}. + /** + * Always returns {@code false}, because GCS doesn't support hidden files. */ @Override public boolean isHidden(Path path) { @@ -486,7 +502,8 @@ public V getFileAttributeView( return result; } - /** Does nothing since GCS uses fake directories. + /** + * Does nothing since GCS uses fake directories. */ @Override public void createDirectory(Path dir, FileAttribute... attrs) { @@ -494,7 +511,8 @@ public void createDirectory(Path dir, FileAttribute... attrs) { checkNotNullArray(attrs); } - /** Always @throws UnsupportedOperationException. + /** + * Throws {@link UnsupportedOperationException} because this feature hasn't been implemented yet. */ @Override public DirectoryStream newDirectoryStream(Path dir, Filter filter) { @@ -503,8 +521,7 @@ public DirectoryStream newDirectoryStream(Path dir, Filter f } /** - * This feature is not supported, since Cloud Storage objects are immutable. - * Always @throws UnsupportedOperationException. + * Throws {@link UnsupportedOperationException} because Cloud Storage objects are immutable. */ @Override public void setAttribute(Path path, String attribute, Object value, LinkOption... options) { @@ -512,8 +529,7 @@ public void setAttribute(Path path, String attribute, Object value, LinkOption.. } /** - * This feature is not supported. - * Always @throws UnsupportedOperationException. + * Throws {@link UnsupportedOperationException} because this feature hasn't been implemented yet. */ @Override public FileStore getFileStore(Path path) { @@ -524,7 +540,7 @@ public FileStore getFileStore(Path path) { public boolean equals(@Nullable Object other) { return this == other || other instanceof CloudStorageFileSystemProvider - && Objects.equals(storage, ((CloudStorageFileSystemProvider) other).storage); + && Objects.equals(storage, ((CloudStorageFileSystemProvider) other).storage); } @Override @@ -534,13 +550,9 @@ public int hashCode() { @Override public String toString() { - return MoreObjects.toStringHelper(this) - .add("storage", storage) - .toString(); + return MoreObjects.toStringHelper(this).add("storage", storage).toString(); } - - private IOException asIOException(StorageException oops) { if (oops.code() == 404) { return new NoSuchFileException(oops.reason()); @@ -561,5 +573,4 @@ private IOException asIOException(StorageException oops) { } return new IOException(oops.getMessage(), oops); } - } diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageObjectAttributes.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageObjectAttributes.java index 651aa6a33d3b..0ca7f55cd6b1 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageObjectAttributes.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageObjectAttributes.java @@ -22,8 +22,7 @@ @Immutable final class CloudStorageObjectAttributes implements CloudStorageFileAttributes { - @Nonnull - private final BlobInfo info; + @Nonnull private final BlobInfo info; CloudStorageObjectAttributes(BlobInfo info) { this.info = checkNotNull(info); @@ -150,7 +149,7 @@ public Object fileKey() { public boolean equals(@Nullable Object other) { return this == other || other instanceof CloudStorageObjectAttributes - && Objects.equals(info, ((CloudStorageObjectAttributes) other).info); + && Objects.equals(info, ((CloudStorageObjectAttributes) other).info); } @Override @@ -160,8 +159,6 @@ public int hashCode() { @Override public String toString() { - return MoreObjects.toStringHelper(this) - .add("info", info) - .toString(); + return MoreObjects.toStringHelper(this).add("info", info).toString(); } } diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageObjectImmutableException.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageObjectImmutableException.java index ebe816e2665b..31dfaa130659 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageObjectImmutableException.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageObjectImmutableException.java @@ -1,7 +1,10 @@ package com.google.cloud.storage.contrib.nio; -/** Exception thrown to indicate we don't support a mutation of a cloud storage object. */ +/** + * Exception thrown to indicate we don't support a mutation of a cloud storage object. + */ public final class CloudStorageObjectImmutableException extends UnsupportedOperationException { + CloudStorageObjectImmutableException() { super("Cloud Storage objects are immutable."); } diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageOption.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageOption.java index 8928c5ff4415..4e65069c8cbc 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageOption.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageOption.java @@ -3,15 +3,23 @@ import java.nio.file.CopyOption; import java.nio.file.OpenOption; -/** Master interface for file operation option classes related to Google Cloud Storage. */ +/** + * Master interface for file operation option classes related to Google Cloud Storage. + */ public interface CloudStorageOption { - /** Interface for GCS options that can be specified when opening files. */ + /** + * Interface for GCS options that can be specified when opening files. + */ interface Open extends CloudStorageOption, OpenOption {} - /** Interface for GCS options that can be specified when copying files. */ + /** + * Interface for GCS options that can be specified when copying files. + */ interface Copy extends CloudStorageOption, CopyOption {} - /** Interface for GCS options that can be specified when opening or copying files. */ + /** + * Interface for GCS options that can be specified when opening or copying files. + */ interface OpenCopy extends Open, Copy {} } diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageOptions.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageOptions.java index a22b429ef31b..ec1421f06b6a 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageOptions.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageOptions.java @@ -2,15 +2,21 @@ import com.google.cloud.storage.Acl; -/** Helper class for specifying options when opening and copying Cloud Storage files. */ +/** + * Helper class for specifying options when opening and copying Cloud Storage files. + */ public final class CloudStorageOptions { - /** Sets the mime type header on an object, e.g. {@code "text/plain"}. */ + /** + * Sets the mime type header on an object, e.g. {@code "text/plain"}. + */ public static CloudStorageOption.OpenCopy withMimeType(String mimeType) { return OptionMimeType.create(mimeType); } - /** Disables caching on an object. Same as: {@code withCacheControl("no-cache")}. */ + /** + * Disables caching on an object. Same as: {@code withCacheControl("no-cache")}. + */ public static CloudStorageOption.OpenCopy withoutCaching() { return withCacheControl("no-cache"); } diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStoragePath.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStoragePath.java index 60b7ab0700cb..57e5b04542ce 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStoragePath.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStoragePath.java @@ -44,19 +44,20 @@ private CloudStoragePath(CloudStorageFileSystem fileSystem, UnixPath path) { this.path = path; } - static CloudStoragePath getPath( - CloudStorageFileSystem fileSystem, String path, String... more) { + static CloudStoragePath getPath(CloudStorageFileSystem fileSystem, String path, String... more) { return new CloudStoragePath( fileSystem, UnixPath.getPath(fileSystem.config().permitEmptyPathComponents(), path, more)); } - /** Returns the Cloud Storage bucket name being served by this file system. + /** + * Returns the Cloud Storage bucket name being served by this file system. */ public String bucket() { return fileSystem.bucket(); } - /** Returns path converted to a {@link BlobId} so I/O can be performed. + /** + * Returns path converted to a {@link BlobId} so I/O can be performed. */ BlobId getBlobId() { return BlobId.of(bucket(), toRealPath().path.toString()); @@ -87,8 +88,8 @@ public boolean isAbsolute() { } /** - * Changes relative path to absolute, using - * {@link CloudStorageConfiguration#workingDirectory() workingDirectory} as the current dir. + * Changes relative path to be absolute, using + * {@link CloudStorageConfiguration#workingDirectory() workingDirectory} as current dir. */ @Override public CloudStoragePath toAbsolutePath() { @@ -115,15 +116,16 @@ public CloudStoragePath toRealPath(LinkOption... options) { private UnixPath toRealPathInternal(boolean errorCheck) { UnixPath objectName = path.toAbsolutePath(getWorkingDirectory()); if (errorCheck && !fileSystem.config().permitEmptyPathComponents()) { - checkArgument(!EXTRA_SLASHES_OR_DOT_DIRS_PATTERN.matcher(objectName).find(), + checkArgument( + !EXTRA_SLASHES_OR_DOT_DIRS_PATTERN.matcher(objectName).find(), "I/O not allowed on dot-dirs or extra slashes when !permitEmptyPathComponents: %s", objectName); } if (fileSystem.config().stripPrefixSlash()) { objectName = objectName.removeBeginningSeparator(); } - checkArgument(!errorCheck || !objectName.isEmpty(), - "I/O not allowed on empty GCS object names."); + checkArgument( + !errorCheck || !objectName.isEmpty(), "I/O not allowed on empty GCS object names."); return objectName; } @@ -221,7 +223,8 @@ public boolean endsWith(String other) { return path.endsWith(getUnixPath(other)); } - /** Always @throws UnsupportedOperationException. + /** + * Throws {@link UnsupportedOperationException} because this feature hasn't been implemented yet. */ @Override public WatchKey register(WatchService watcher, Kind[] events, Modifier... modifiers) { @@ -229,7 +232,8 @@ public WatchKey register(WatchService watcher, Kind[] events, Modifier... mod throw new UnsupportedOperationException(); } - /** Always @throws UnsupportedOperationException. + /** + * Throws {@link UnsupportedOperationException} because this feature hasn't been implemented yet. */ @Override public WatchKey register(WatchService watcher, Kind... events) { @@ -238,8 +242,8 @@ public WatchKey register(WatchService watcher, Kind... events) { } /** - * This operation is not supported, since GCS files aren't backed by the local file system. - * Always @throws UnsupportedOperationException. + * Throws {@link UnsupportedOperationException} because GCS files are not backed by the local file + * system. */ @Override public File toFile() { @@ -272,9 +276,9 @@ public int compareTo(Path other) { public boolean equals(@Nullable Object other) { return this == other || other instanceof CloudStoragePath - && Objects.equals(bucket(), ((CloudStoragePath) other).bucket()) - && Objects.equals(toRealPathInternal(false), - ((CloudStoragePath) other).toRealPathInternal(false)); + && Objects.equals(bucket(), ((CloudStoragePath) other).bucket()) + && Objects.equals( + toRealPathInternal(false), ((CloudStoragePath) other).toRealPathInternal(false)); } @Override @@ -298,7 +302,7 @@ public URI toUri() { @Nullable private CloudStoragePath newPath(@Nullable UnixPath newPath) { - if (newPath == path) { // Nonuse of equals is intentional. + if (newPath == path) { // Nonuse of equals is intentional. return this; } else if (newPath != null) { return new CloudStoragePath(fileSystem, newPath); @@ -315,7 +319,9 @@ private UnixPath getWorkingDirectory() { return getUnixPath(fileSystem.config().workingDirectory()); } - /** Transform iterator providing a slight performance boost over {@code FluentIterable}. */ + /** + * Transform iterator providing a slight performance boost over {@code FluentIterable}. + */ private final class PathIterator extends UnmodifiableIterator { private final Iterator delegate = path.split(); diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStoragePseudoDirectoryAttributes.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStoragePseudoDirectoryAttributes.java index 49f062f622b3..20c4b9bbd353 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStoragePseudoDirectoryAttributes.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStoragePseudoDirectoryAttributes.java @@ -9,7 +9,9 @@ import java.nio.file.attribute.FileTime; import java.util.List; -/** Metadata for a cloud storage pseudo-directory. */ +/** + * Metadata for a cloud storage pseudo-directory. + */ final class CloudStoragePseudoDirectoryAttributes implements CloudStorageFileAttributes { private final String id; @@ -45,7 +47,7 @@ public Object fileKey() { @Override public long size() { - return 1; // Allow I/O to happen before we fail. + return 1; // Allow I/O to happen before we fail. } @Override diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStoragePseudoDirectoryException.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStoragePseudoDirectoryException.java index 15cfffad0c69..dfad2ed1f503 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStoragePseudoDirectoryException.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStoragePseudoDirectoryException.java @@ -2,8 +2,11 @@ import java.nio.file.InvalidPathException; -/** Exception thrown when erroneously trying to operate on a path with a trailing slash. */ +/** + * Exception thrown when erroneously trying to operate on a path with a trailing slash. + */ public final class CloudStoragePseudoDirectoryException extends InvalidPathException { + CloudStoragePseudoDirectoryException(CloudStoragePath path) { super(path.toString(), "Can't perform I/O on pseudo-directories (trailing slash)"); } diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageReadChannel.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageReadChannel.java index 46629694e7cf..023ef04175f8 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageReadChannel.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageReadChannel.java @@ -14,6 +14,7 @@ import java.nio.channels.SeekableByteChannel; import java.nio.file.NoSuchFileException; +import javax.annotation.CheckReturnValue; import javax.annotation.concurrent.ThreadSafe; /** @@ -24,8 +25,10 @@ @ThreadSafe final class CloudStorageReadChannel implements SeekableByteChannel { - static CloudStorageReadChannel create( - Storage gcsStorage, BlobId file, long position) throws IOException { + @CheckReturnValue + @SuppressWarnings("resource") + static CloudStorageReadChannel create(Storage gcsStorage, BlobId file, long position) + throws IOException { // XXX: Reading size and opening file should be atomic. long size = fetchSize(gcsStorage, file); ReadChannel channel = gcsStorage.reader(file); @@ -45,7 +48,6 @@ private CloudStorageReadChannel(long position, long size, ReadChannel channel) { this.channel = channel; } - @Override public boolean isOpen() { synchronized (this) { @@ -125,8 +127,7 @@ private void checkOpen() throws ClosedChannelException { private static long fetchSize(Storage gcsStorage, BlobId file) throws IOException { BlobInfo blobInfo = gcsStorage.get(file); if (blobInfo == null) { - throw new NoSuchFileException( - String.format("gs://%s/%s", file.bucket(), file.name())); + throw new NoSuchFileException(String.format("gs://%s/%s", file.bucket(), file.name())); } return blobInfo.size(); } diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageUtil.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageUtil.java index 11a7e01edbef..42890f51ff5b 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageUtil.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageUtil.java @@ -15,17 +15,22 @@ final class CloudStorageUtil { static void checkBucket(String bucket) { // TODO: The true check is actually more complicated. Consider implementing it. - checkArgument(BUCKET_PATTERN.matcher(bucket).matches(), "" - + "Invalid bucket name: '" + bucket + "'. " - + "GCS bucket names must contain only lowercase letters, numbers, dashes (-), " - + "underscores (_), and dots (.). Bucket names must start and end with a number or letter. " - + "See https://developers.google.com/storage/docs/bucketnaming for more details."); + checkArgument( + BUCKET_PATTERN.matcher(bucket).matches(), + "Invalid bucket name: '" + + bucket + + "'. " + + "GCS bucket names must contain only lowercase letters, numbers, dashes (-), " + + "underscores (_), and dots (.). Bucket names must start and end with a number or a " + + "letter. See the following page for more details: " + + "https://developers.google.com/storage/docs/bucketnaming"); } static CloudStoragePath checkPath(Path path) { if (!(checkNotNull(path) instanceof CloudStoragePath)) { - throw new ProviderMismatchException(String.format( - "Not a cloud storage path: %s (%s)", path, path.getClass().getSimpleName())); + throw new ProviderMismatchException( + String.format( + "Not a cloud storage path: %s (%s)", path, path.getClass().getSimpleName())); } return (CloudStoragePath) path; } @@ -45,7 +50,9 @@ static URI stripPathFromUri(URI uri) { } } - /** Makes NullPointerTester happy. */ + /** + * Makes {@code NullPointerTester} happy. + */ @SafeVarargs static void checkNotNullArray(T... values) { for (T value : values) { diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/UnixPath.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/UnixPath.java index 85474ac2d92c..7147530980b8 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/UnixPath.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/UnixPath.java @@ -28,7 +28,7 @@ * preserve trailing backslashes, in order to ensure the path will continue to be recognized as a * directory. * - *

NOTE: This code might not play nice with + *

Note: This code might not play nice with * Supplementary * Characters as Surrogates. */ @@ -57,7 +57,9 @@ private UnixPath(boolean permitEmptyComponents, String path) { this.permitEmptyComponents = permitEmptyComponents; } - /** Returns new UnixPath of {@code first}. */ + /** + * Returns new UnixPath of {@code first}. + */ public static UnixPath getPath(boolean permitEmptyComponents, String path) { if (path.isEmpty()) { return EMPTY_PATH; @@ -99,7 +101,9 @@ public static UnixPath getPath(boolean permitEmptyComponents, String first, Stri return new UnixPath(permitEmptyComponents, builder.toString()); } - /** Returns {@code true} consists only of {@code separator}. */ + /** + * Returns {@code true} consists only of {@code separator}. + */ public boolean isRoot() { return isRootInternal(path); } @@ -108,7 +112,9 @@ private static boolean isRootInternal(String path) { return path.length() == 1 && path.charAt(0) == SEPARATOR; } - /** Returns {@code true} if path starts with {@code separator}. */ + /** + * Returns {@code true} if path starts with {@code separator}. + */ public boolean isAbsolute() { return isAbsoluteInternal(path); } @@ -117,7 +123,9 @@ private static boolean isAbsoluteInternal(String path) { return !path.isEmpty() && path.charAt(0) == SEPARATOR; } - /** Returns {@code true} if path ends with {@code separator}. */ + /** + * Returns {@code true} if path ends with {@code separator}. + */ public boolean hasTrailingSeparator() { return hasTrailingSeparatorInternal(path); } @@ -126,7 +134,9 @@ private static boolean hasTrailingSeparatorInternal(CharSequence path) { return path.length() != 0 && path.charAt(path.length() - 1) == SEPARATOR; } - /** Returns {@code true} if path ends with a trailing slash, or would after normalization. */ + /** + * Returns {@code true} if path ends with a trailing slash, or would after normalization. + */ public boolean seemsLikeADirectory() { int length = path.length(); return path.isEmpty() @@ -150,7 +160,8 @@ public UnixPath getFileName() { List parts = getParts(); String last = parts.get(parts.size() - 1); return parts.size() == 1 && path.equals(last) - ? this : new UnixPath(permitEmptyComponents, last); + ? this + : new UnixPath(permitEmptyComponents, last); } } @@ -164,9 +175,10 @@ public UnixPath getParent() { if (path.isEmpty() || isRoot()) { return null; } - int index = hasTrailingSeparator() - ? path.lastIndexOf(SEPARATOR, path.length() - 2) - : path.lastIndexOf(SEPARATOR); + int index = + hasTrailingSeparator() + ? path.lastIndexOf(SEPARATOR, path.length() - 2) + : path.lastIndexOf(SEPARATOR); if (index == -1) { return isAbsolute() ? ROOT_PATH : null; } else { @@ -397,28 +409,38 @@ public int compareTo(UnixPath other) { return ORDERING.compare(getParts(), other.getParts()); } - /** Converts relative path to an absolute path. */ + /** + * Converts relative path to an absolute path. + */ public UnixPath toAbsolutePath(UnixPath currentWorkingDirectory) { checkArgument(currentWorkingDirectory.isAbsolute()); return isAbsolute() ? this : currentWorkingDirectory.resolve(this); } - /** Returns {@code toAbsolutePath(ROOT_PATH)}. */ + /** + * Returns {@code toAbsolutePath(ROOT_PATH)}. + */ public UnixPath toAbsolutePath() { return toAbsolutePath(ROOT_PATH); } - /** Removes beginning separator from path, if an absolute path. */ + /** + * Removes beginning separator from path, if an absolute path. + */ public UnixPath removeBeginningSeparator() { return isAbsolute() ? new UnixPath(permitEmptyComponents, path.substring(1)) : this; } - /** Adds trailing separator to path, if it isn't present. */ + /** + * Adds trailing separator to path, if it isn't present. + */ public UnixPath addTrailingSeparator() { return hasTrailingSeparator() ? this : new UnixPath(permitEmptyComponents, path + SEPARATOR); } - /** Removes trailing separator from path, unless it's root. */ + /** + * Removes trailing separator from path, unless it's root. + */ public UnixPath removeTrailingSeparator() { if (!isRoot() && hasTrailingSeparator()) { return new UnixPath(permitEmptyComponents, path.substring(0, path.length() - 1)); @@ -427,21 +449,23 @@ public UnixPath removeTrailingSeparator() { } } - /** Splits path into components, excluding separators and empty strings. */ + /** + * Splits path into components, excluding separators and empty strings. + */ public Iterator split() { return getParts().iterator(); } - /** Splits path into components in reverse, excluding separators and empty strings. */ + /** + * Splits path into components in reverse, excluding separators and empty strings. + */ public Iterator splitReverse() { return Lists.reverse(getParts()).iterator(); } @Override public boolean equals(@Nullable Object other) { - return this == other - || other instanceof UnixPath - && path.equals(((UnixPath) other).path); + return this == other || other instanceof UnixPath && path.equals(((UnixPath) other).path); } @Override @@ -449,7 +473,9 @@ public int hashCode() { return path.hashCode(); } - /** Returns path as a string. */ + /** + * Returns path as a string. + */ @Override public String toString() { return path; @@ -470,23 +496,28 @@ public CharSequence subSequence(int start, int end) { return path.subSequence(start, end); } - /** Returns {@code true} if this path is an empty string. */ + /** + * Returns {@code true} if this path is an empty string. + */ public boolean isEmpty() { return path.isEmpty(); } - /** Returns list of path components, excluding slashes. */ + /** + * Returns list of path components, excluding slashes. + */ private List getParts() { List result = lazyStringParts; return result != null - ? result : (lazyStringParts = path.isEmpty() || isRoot() - ? Collections.emptyList() : createParts()); + ? result + : (lazyStringParts = + path.isEmpty() || isRoot() ? Collections.emptyList() : createParts()); } private List createParts() { if (permitEmptyComponents) { - return SPLITTER_PERMIT_EMPTY_COMPONENTS - .splitToList(path.charAt(0) == SEPARATOR ? path.substring(1) : path); + return SPLITTER_PERMIT_EMPTY_COMPONENTS.splitToList( + path.charAt(0) == SEPARATOR ? path.substring(1) : path); } else { return SPLITTER.splitToList(path); } diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/package-info.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/package-info.java index aa08a5cb8693..c956c122e121 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/package-info.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/package-info.java @@ -18,7 +18,7 @@ * static String bucket = System.getProperty(...); * static FileSystem fs = FileSystems.getFileSystem(URI.create("gs://" + bucket)); * void bar() { - * byte[] data = "hello kitty".getBytes(StandardCharsets.UTF_8); + * byte[] data = "hello world".getBytes(StandardCharsets.UTF_8); * Path path = fs.getPath("/object"); * Files.write(path, data); * data = Files.readBytes(path); @@ -73,7 +73,7 @@ * Google Auto, you can instantiate this file system directly as follows:

   {@code
  *
  *   CloudStorageFileSystem fs = CloudStorageFileSystemProvider.forBucket("bucket");
- *   byte[] data = "hello kitty".getBytes(StandardCharsets.UTF_8);
+ *   byte[] data = "hello world".getBytes(StandardCharsets.UTF_8);
  *   Path path = fs.getPath("/object");
  *   Files.write(path, data);
  *   data = Files.readBytes(path);}
diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageConfigurationTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageConfigurationTest.java index 5c91f2d2ff08..9b887ffff833 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageConfigurationTest.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageConfigurationTest.java @@ -10,22 +10,24 @@ import org.junit.runner.RunWith; import org.junit.runners.JUnit4; -/** Unit tests for {@link CloudStorageConfiguration}. */ +/** + * Unit tests for {@link CloudStorageConfiguration}. + */ @RunWith(JUnit4.class) public class CloudStorageConfigurationTest { - @Rule - public final ExpectedException thrown = ExpectedException.none(); + @Rule public final ExpectedException thrown = ExpectedException.none(); @Test public void testBuilder() { - CloudStorageConfiguration config = CloudStorageConfiguration.builder() - .workingDirectory("/omg") - .permitEmptyPathComponents(true) - .stripPrefixSlash(false) - .usePseudoDirectories(false) - .blockSize(666) - .build(); + CloudStorageConfiguration config = + CloudStorageConfiguration.builder() + .workingDirectory("/omg") + .permitEmptyPathComponents(true) + .stripPrefixSlash(false) + .usePseudoDirectories(false) + .blockSize(666) + .build(); assertThat(config.workingDirectory()).isEqualTo("/omg"); assertThat(config.permitEmptyPathComponents()).isTrue(); assertThat(config.stripPrefixSlash()).isFalse(); @@ -35,14 +37,15 @@ public void testBuilder() { @Test public void testFromMap() { - CloudStorageConfiguration config = CloudStorageConfiguration.fromMap( - new ImmutableMap.Builder() - .put("workingDirectory", "/omg") - .put("permitEmptyPathComponents", true) - .put("stripPrefixSlash", false) - .put("usePseudoDirectories", false) - .put("blockSize", 666) - .build()); + CloudStorageConfiguration config = + CloudStorageConfiguration.fromMap( + new ImmutableMap.Builder() + .put("workingDirectory", "/omg") + .put("permitEmptyPathComponents", true) + .put("stripPrefixSlash", false) + .put("usePseudoDirectories", false) + .put("blockSize", 666) + .build()); assertThat(config.workingDirectory()).isEqualTo("/omg"); assertThat(config.permitEmptyPathComponents()).isTrue(); assertThat(config.stripPrefixSlash()).isFalse(); diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributeViewTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributeViewTest.java index e0d4e4734039..cf11b85f916b 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributeViewTest.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributeViewTest.java @@ -21,14 +21,15 @@ import java.nio.file.Paths; import java.nio.file.attribute.FileTime; -/** Unit tests for {@link CloudStorageFileAttributeView}. */ +/** + * Unit tests for {@link CloudStorageFileAttributeView}. + */ @RunWith(JUnit4.class) public class CloudStorageFileAttributeViewTest { private static final byte[] HAPPY = "(✿◕ ‿◕ )ノ".getBytes(UTF_8); - @Rule - public final ExpectedException thrown = ExpectedException.none(); + @Rule public final ExpectedException thrown = ExpectedException.none(); private Path path; @@ -76,15 +77,12 @@ public void testEquals_equalsTester() throws Exception { new EqualsTester() .addEqualityGroup( Files.getFileAttributeView( - Paths.get(URI.create("gs://red/rum")), - CloudStorageFileAttributeView.class), + Paths.get(URI.create("gs://red/rum")), CloudStorageFileAttributeView.class), Files.getFileAttributeView( - Paths.get(URI.create("gs://red/rum")), - CloudStorageFileAttributeView.class)) + Paths.get(URI.create("gs://red/rum")), CloudStorageFileAttributeView.class)) .addEqualityGroup( Files.getFileAttributeView( - Paths.get(URI.create("gs://red/lol/dog")), - CloudStorageFileAttributeView.class)) + Paths.get(URI.create("gs://red/lol/dog")), CloudStorageFileAttributeView.class)) .testEquals(); } diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributesTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributesTest.java index b3b2cb0f6960..7dfa605fa767 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributesTest.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributesTest.java @@ -24,19 +24,20 @@ import java.nio.file.Path; import java.nio.file.Paths; -/** Unit tests for {@link CloudStorageFileAttributes}. */ +/** + * Unit tests for {@link CloudStorageFileAttributes}. + */ @RunWith(JUnit4.class) public class CloudStorageFileAttributesTest { private static final byte[] HAPPY = "(✿◕ ‿◕ )ノ".getBytes(UTF_8); - private Path path; private Path dir; /** empty test storage and make sure we use it instead of the real GCS. Create a few paths. **/ @Before - public void before() { + public void before() { CloudStorageFileSystemProvider.setGCloudOptions(LocalGcsHelper.options()); path = Paths.get(URI.create("gs://bucket/randompath")); dir = Paths.get(URI.create("gs://bucket/randompath/")); @@ -68,8 +69,8 @@ public void testAcl() throws Exception { public void testContentDisposition() throws Exception { Files.write(path, HAPPY, withContentDisposition("crash call")); assertThat( - Files.readAttributes(path, CloudStorageFileAttributes.class).contentDisposition().get()) - .isEqualTo("crash call"); + Files.readAttributes(path, CloudStorageFileAttributes.class).contentDisposition().get()) + .isEqualTo("crash call"); } @Test @@ -83,8 +84,10 @@ public void testContentEncoding() throws Exception { public void testUserMetadata() throws Exception { Files.write(path, HAPPY, withUserMetadata("green", "bean")); assertThat( - Files.readAttributes(path, CloudStorageFileAttributes.class).userMetadata().get("green")) - .isEqualTo("bean"); + Files.readAttributes(path, CloudStorageFileAttributes.class) + .userMetadata() + .get("green")) + .isEqualTo("bean"); } @Test @@ -144,8 +147,9 @@ public void testFilekey() throws Exception { // same for directories CloudStorageFileAttributes b1 = Files.readAttributes(dir, CloudStorageFileAttributes.class); - CloudStorageFileAttributes b2 = Files.readAttributes( - Paths.get(URI.create("gs://bucket/jacket/")), CloudStorageFileAttributes.class); + CloudStorageFileAttributes b2 = + Files.readAttributes( + Paths.get(URI.create("gs://bucket/jacket/")), CloudStorageFileAttributes.class); assertThat(a1.fileKey()).isNotEqualTo(b1.fileKey()); assertThat(b1.fileKey()).isNotEqualTo(b2.fileKey()); } diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProviderTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProviderTest.java index c72f6ee2ed22..1c64010850ad 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProviderTest.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProviderTest.java @@ -1,5 +1,6 @@ package com.google.cloud.storage.contrib.nio; +import static com.google.cloud.storage.contrib.nio.CloudStorageFileSystem.forBucket; import static com.google.cloud.storage.contrib.nio.CloudStorageOptions.withCacheControl; import static com.google.cloud.storage.contrib.nio.CloudStorageOptions.withContentDisposition; import static com.google.cloud.storage.contrib.nio.CloudStorageOptions.withContentEncoding; @@ -45,25 +46,36 @@ import java.nio.file.StandardOpenOption; import java.util.List; -/** Unit tests for {@link CloudStorageFileSystemProvider}. */ +/** + * Unit tests for {@link CloudStorageFileSystemProvider}. + */ @RunWith(JUnit4.class) -@SuppressWarnings("resource") public class CloudStorageFileSystemProviderTest { - private static final List FILE_CONTENTS = ImmutableList.of( - "To be, or not to be, that is the question—", - "Whether 'tis Nobler in the mind to suffer", - "The Slings and Arrows of outrageous Fortune,", - "Or to take Arms against a Sea of troubles,", - "And by opposing, end them? To die, to sleep—", - "No more; and by a sleep, to say we end", - "The Heart-ache, and the thousand Natural shocks", - "That Flesh is heir to? 'Tis a consummation"); + private static final List FILE_CONTENTS = + ImmutableList.of( + "Fanatics have their dreams, wherewith they weave", + "A paradise for a sect; the savage too", + "From forth the loftiest fashion of his sleep", + "Guesses at Heaven; pity these have not", + "Trac'd upon vellum or wild Indian leaf", + "The shadows of melodious utterance.", + "But bare of laurel they live, dream, and die;", + "For Poesy alone can tell her dreams,", + "With the fine spell of words alone can save", + "Imagination from the sable charm", + "And dumb enchantment. Who alive can say,", + "'Thou art no Poet may'st not tell thy dreams?'", + "Since every man whose soul is not a clod", + "Hath visions, and would speak, if he had loved", + "And been well nurtured in his mother tongue.", + "Whether the dream now purpos'd to rehearse", + "Be poet's or fanatic's will be known", + "When this warm scribe my hand is in the grave."); private static final String SINGULARITY = "A string"; - @Rule - public final ExpectedException thrown = ExpectedException.none(); + @Rule public final ExpectedException thrown = ExpectedException.none(); @Before public void before() { @@ -84,7 +96,7 @@ public void testSize_trailingSlash_returnsFakePseudoDirectorySize() throws Excep @Test public void testSize_trailingSlash_disablePseudoDirectories() throws Exception { - try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle", usePseudoDirectories(false))) { + try (CloudStorageFileSystem fs = forBucket("doodle", usePseudoDirectories(false))) { Path path = fs.getPath("wat/"); byte[] rapture = SINGULARITY.getBytes(UTF_8); Files.write(path, rapture); @@ -277,7 +289,7 @@ public void testWrite_objectNameWithExtraSlashes_throwsIae() throws Exception { @Test public void testWrite_objectNameWithExtraSlashes_canBeNormalized() throws Exception { - try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("greenbean", permitEmptyPathComponents(false))) { + try (CloudStorageFileSystem fs = forBucket("greenbean", permitEmptyPathComponents(false))) { Path path = fs.getPath("adipose//yep").normalize(); Files.write(path, FILE_CONTENTS, UTF_8); assertThat(Files.readAllLines(path, UTF_8)).isEqualTo(FILE_CONTENTS); @@ -287,7 +299,7 @@ public void testWrite_objectNameWithExtraSlashes_canBeNormalized() throws Except @Test public void testWrite_objectNameWithExtraSlashes_permitEmptyPathComponents() throws Exception { - try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("greenbean", permitEmptyPathComponents(true))) { + try (CloudStorageFileSystem fs = forBucket("greenbean", permitEmptyPathComponents(true))) { Path path = fs.getPath("adipose//yep"); Files.write(path, FILE_CONTENTS, UTF_8); assertThat(Files.readAllLines(path, UTF_8)).isEqualTo(FILE_CONTENTS); @@ -306,10 +318,8 @@ public void testWrite_absoluteObjectName_prefixSlashGetsRemoved() throws Excepti @Test public void testWrite_absoluteObjectName_disableStrip_slashGetsPreserved() throws Exception { try (CloudStorageFileSystem fs = - CloudStorageFileSystem.forBucket("greenbean", - CloudStorageConfiguration.builder() - .stripPrefixSlash(false) - .build())) { + forBucket( + "greenbean", CloudStorageConfiguration.builder().stripPrefixSlash(false).build())) { Path path = fs.getPath("/adipose/yep"); Files.write(path, FILE_CONTENTS, UTF_8); assertThat(Files.readAllLines(path, UTF_8)).isEqualTo(FILE_CONTENTS); @@ -370,7 +380,7 @@ public void testExists_trailingSlash() throws Exception { @Test public void testExists_trailingSlash_disablePseudoDirectories() throws Exception { - try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("military", usePseudoDirectories(false))) { + try (CloudStorageFileSystem fs = forBucket("military", usePseudoDirectories(false))) { assertThat(Files.exists(fs.getPath("fashion/"))).isFalse(); } } @@ -397,7 +407,7 @@ public void testDelete_trailingSlash() throws Exception { @Test public void testDelete_trailingSlash_disablePseudoDirectories() throws Exception { - try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("pumpkin", usePseudoDirectories(false))) { + try (CloudStorageFileSystem fs = forBucket("pumpkin", usePseudoDirectories(false))) { Path path = fs.getPath("wat/"); Files.write(path, FILE_CONTENTS, UTF_8); assertThat(Files.exists(path)); @@ -526,7 +536,7 @@ public void testIsDirectory_trailingSlash_alwaysTrue() throws Exception { @Test public void testIsDirectory_trailingSlash_pseudoDirectoriesDisabled_false() throws Exception { - try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle", usePseudoDirectories(false))) { + try (CloudStorageFileSystem fs = forBucket("doodle", usePseudoDirectories(false))) { assertThat(Files.isDirectory(fs.getPath("fundir/"))).isFalse(); } } @@ -535,7 +545,9 @@ public void testIsDirectory_trailingSlash_pseudoDirectoriesDisabled_false() thro public void testCopy_withCopyAttributes_preservesAttributes() throws Exception { Path source = Paths.get(URI.create("gs://military/fashion.show")); Path target = Paths.get(URI.create("gs://greenbean/adipose")); - Files.write(source, "(✿◕ ‿◕ )ノ".getBytes(UTF_8), + Files.write( + source, + "(✿◕ ‿◕ )ノ".getBytes(UTF_8), withMimeType("text/lolcat"), withCacheControl("public; max-age=666"), withContentEncoding("foobar"), @@ -557,7 +569,9 @@ public void testCopy_withCopyAttributes_preservesAttributes() throws Exception { public void testCopy_withoutOptions_doesntPreservesAttributes() throws Exception { Path source = Paths.get(URI.create("gs://military/fashion.show")); Path target = Paths.get(URI.create("gs://greenbean/adipose")); - Files.write(source, "(✿◕ ‿◕ )ノ".getBytes(UTF_8), + Files.write( + source, + "(✿◕ ‿◕ )ノ".getBytes(UTF_8), withMimeType("text/lolcat"), withCacheControl("public; max-age=666"), withUserMetadata("answer", "42")); @@ -577,12 +591,13 @@ public void testCopy_overwriteAttributes() throws Exception { Path source = Paths.get(URI.create("gs://military/fashion.show")); Path target1 = Paths.get(URI.create("gs://greenbean/adipose")); Path target2 = Paths.get(URI.create("gs://greenbean/round")); - Files.write(source, "(✿◕ ‿◕ )ノ".getBytes(UTF_8), + Files.write( + source, + "(✿◕ ‿◕ )ノ".getBytes(UTF_8), withMimeType("text/lolcat"), withCacheControl("public; max-age=666")); Files.copy(source, target1, COPY_ATTRIBUTES); - Files.copy(source, target2, COPY_ATTRIBUTES, - withMimeType("text/palfun")); + Files.copy(source, target2, COPY_ATTRIBUTES, withMimeType("text/palfun")); CloudStorageFileAttributes attributes = Files.readAttributes(target1, CloudStorageFileAttributes.class); @@ -597,14 +612,15 @@ public void testCopy_overwriteAttributes() throws Exception { @Test public void testNullness() throws Exception { try (FileSystem fs = FileSystems.getFileSystem(URI.create("gs://blood"))) { - NullPointerTester tester = new NullPointerTester() - .setDefault(URI.class, URI.create("gs://blood")) - .setDefault(Path.class, fs.getPath("and/one")) - .setDefault(OpenOption.class, StandardOpenOption.CREATE) - .setDefault(CopyOption.class, StandardCopyOption.COPY_ATTRIBUTES); + NullPointerTester tester = + new NullPointerTester() + .setDefault(URI.class, URI.create("gs://blood")) + .setDefault(Path.class, fs.getPath("and/one")) + .setDefault(OpenOption.class, StandardOpenOption.CREATE) + .setDefault(CopyOption.class, StandardCopyOption.COPY_ATTRIBUTES); // can't do that, setGCloudOptions accepts a null argument. // TODO(jart): Figure out how to re-enable this. - //tester.testAllPublicStaticMethods(CloudStorageFileSystemProvider.class); + // tester.testAllPublicStaticMethods(CloudStorageFileSystemProvider.class); tester.testAllPublicInstanceMethods(new CloudStorageFileSystemProvider()); } } @@ -619,14 +635,10 @@ public void testProviderEquals() throws Exception { } private static CloudStorageConfiguration permitEmptyPathComponents(boolean value) { - return CloudStorageConfiguration.builder() - .permitEmptyPathComponents(value) - .build(); + return CloudStorageConfiguration.builder().permitEmptyPathComponents(value).build(); } private static CloudStorageConfiguration usePseudoDirectories(boolean value) { - return CloudStorageConfiguration.builder() - .usePseudoDirectories(value) - .build(); + return CloudStorageConfiguration.builder().usePseudoDirectories(value).build(); } } diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemTest.java index 1b6479aebe09..ce819c1216c7 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemTest.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemTest.java @@ -18,20 +18,21 @@ import java.nio.file.Files; import java.nio.file.Paths; -/** Unit tests for {@link CloudStorageFileSystem}. */ +/** + * Unit tests for {@link CloudStorageFileSystem}. + */ @RunWith(JUnit4.class) public class CloudStorageFileSystemTest { - private static final String ALONE = "" - + "To be, or not to be, that is the question—\n" - + "Whether 'tis Nobler in the mind to suffer\n" - + "The Slings and Arrows of outrageous Fortune,\n" - + "Or to take Arms against a Sea of troubles,\n" - + "And by opposing, end them? To die, to sleep—\n" - + "No more; and by a sleep, to say we end\n" - + "The Heart-ache, and the thousand Natural shocks\n" - + "That Flesh is heir to? 'Tis a consummation\n"; - + private static final String ALONE = + "To be, or not to be, that is the question—\n" + + "Whether 'tis Nobler in the mind to suffer\n" + + "The Slings and Arrows of outrageous Fortune,\n" + + "Or to take Arms against a Sea of troubles,\n" + + "And by opposing, end them? To die, to sleep—\n" + + "No more; and by a sleep, to say we end\n" + + "The Heart-ache, and the thousand Natural shocks\n" + + "That Flesh is heir to? 'Tis a consummation\n"; @Before public void before() { @@ -106,8 +107,9 @@ public void testEquals() throws Exception { @Test public void testNullness() throws Exception { try (FileSystem fs = FileSystems.getFileSystem(URI.create("gs://bucket"))) { - NullPointerTester tester = new NullPointerTester() - .setDefault(CloudStorageConfiguration.class, CloudStorageConfiguration.DEFAULT); + NullPointerTester tester = + new NullPointerTester() + .setDefault(CloudStorageConfiguration.class, CloudStorageConfiguration.DEFAULT); tester.testAllPublicStaticMethods(CloudStorageFileSystem.class); tester.testAllPublicInstanceMethods(fs); } diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageOptionsTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageOptionsTest.java index 790a0dbdcda0..9f7006e73fb5 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageOptionsTest.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageOptionsTest.java @@ -24,7 +24,9 @@ import java.nio.file.Path; import java.nio.file.Paths; -/** Unit tests for {@link CloudStorageOptions}. */ +/** + * Unit tests for {@link CloudStorageOptions}. + */ @RunWith(JUnit4.class) public class CloudStorageOptionsTest { @@ -36,8 +38,7 @@ public void before() { @Test public void testWithoutCaching() throws Exception { Path path = Paths.get(URI.create("gs://bucket/path")); - Files.write(path, "(✿◕ ‿◕ )ノ".getBytes(UTF_8), - withoutCaching()); + Files.write(path, "(✿◕ ‿◕ )ノ".getBytes(UTF_8), withoutCaching()); assertThat(Files.readAttributes(path, CloudStorageFileAttributes.class).cacheControl().get()) .isEqualTo("no-cache"); } @@ -45,8 +46,7 @@ public void testWithoutCaching() throws Exception { @Test public void testCacheControl() throws Exception { Path path = Paths.get(URI.create("gs://bucket/path")); - Files.write(path, "(✿◕ ‿◕ )ノ".getBytes(UTF_8), - withCacheControl("potato")); + Files.write(path, "(✿◕ ‿◕ )ノ".getBytes(UTF_8), withCacheControl("potato")); assertThat(Files.readAttributes(path, CloudStorageFileAttributes.class).cacheControl().get()) .isEqualTo("potato"); } @@ -55,8 +55,7 @@ public void testCacheControl() throws Exception { public void testWithAcl() throws Exception { Path path = Paths.get(URI.create("gs://bucket/path")); Acl acl = Acl.of(new Acl.User("king@example.com"), Acl.Role.OWNER); - Files.write(path, "(✿◕ ‿◕ )ノ".getBytes(UTF_8), - withAcl(acl)); + Files.write(path, "(✿◕ ‿◕ )ノ".getBytes(UTF_8), withAcl(acl)); assertThat(Files.readAttributes(path, CloudStorageFileAttributes.class).acl().get()) .contains(acl); } @@ -64,18 +63,16 @@ public void testWithAcl() throws Exception { @Test public void testWithContentDisposition() throws Exception { Path path = Paths.get(URI.create("gs://bucket/path")); - Files.write(path, "(✿◕ ‿◕ )ノ".getBytes(UTF_8), - withContentDisposition("bubbly fun")); + Files.write(path, "(✿◕ ‿◕ )ノ".getBytes(UTF_8), withContentDisposition("bubbly fun")); assertThat( - Files.readAttributes(path, CloudStorageFileAttributes.class).contentDisposition().get()) + Files.readAttributes(path, CloudStorageFileAttributes.class).contentDisposition().get()) .isEqualTo("bubbly fun"); } @Test public void testWithContentEncoding() throws Exception { Path path = Paths.get(URI.create("gs://bucket/path")); - Files.write(path, "(✿◕ ‿◕ )ノ".getBytes(UTF_8), - withContentEncoding("gzip")); + Files.write(path, "(✿◕ ‿◕ )ノ".getBytes(UTF_8), withContentEncoding("gzip")); assertThat(Files.readAttributes(path, CloudStorageFileAttributes.class).contentEncoding().get()) .isEqualTo("gzip"); } @@ -83,24 +80,25 @@ public void testWithContentEncoding() throws Exception { @Test public void testWithUserMetadata() throws Exception { Path path = Paths.get(URI.create("gs://bucket/path")); - Files.write(path, "(✿◕ ‿◕ )ノ".getBytes(UTF_8), + Files.write( + path, + "(✿◕ ‿◕ )ノ".getBytes(UTF_8), withUserMetadata("nolo", "contendere"), withUserMetadata("eternal", "sadness")); assertThat( - Files.readAttributes(path, CloudStorageFileAttributes.class) - .userMetadata().get("nolo")) + Files.readAttributes(path, CloudStorageFileAttributes.class).userMetadata().get("nolo")) .isEqualTo("contendere"); assertThat( Files.readAttributes(path, CloudStorageFileAttributes.class) - .userMetadata().get("eternal")) + .userMetadata() + .get("eternal")) .isEqualTo("sadness"); } @Test public void testWithMimeType_string() throws Exception { Path path = Paths.get(URI.create("gs://bucket/path")); - Files.write(path, "(✿◕ ‿◕ )ノ".getBytes(UTF_8), - withMimeType("text/plain")); + Files.write(path, "(✿◕ ‿◕ )ノ".getBytes(UTF_8), withMimeType("text/plain")); assertThat(Files.readAttributes(path, CloudStorageFileAttributes.class).mimeType().get()) .isEqualTo("text/plain"); } diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStoragePathTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStoragePathTest.java index 3680aeab7320..0235bcadec87 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStoragePathTest.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStoragePathTest.java @@ -22,12 +22,13 @@ import java.nio.file.Paths; import java.nio.file.ProviderMismatchException; -/** Unit tests for {@link CloudStoragePath}. */ +/** + * Unit tests for {@link CloudStoragePath}. + */ @RunWith(JUnit4.class) public class CloudStoragePathTest { - @Rule - public final ExpectedException thrown = ExpectedException.none(); + @Rule public final ExpectedException thrown = ExpectedException.none(); @Before public void before() { @@ -292,9 +293,9 @@ public void testGetRoot() { @Test public void testRelativize() { try (CloudStorageFileSystem fs = forBucket("doodle")) { - assertThat(fs.getPath("/foo/bar/lol/cat") - .relativize(fs.getPath("/foo/a/b/../../c")).toString()) - .isEqualTo("../../../a/b/../../c"); + assertThat( + fs.getPath("/foo/bar/lol/cat").relativize(fs.getPath("/foo/a/b/../../c")).toString()) + .isEqualTo("../../../a/b/../../c"); } } @@ -307,7 +308,7 @@ public void testRelativize_providerMismatch() { } @Test - @SuppressWarnings("ReturnValueIgnored") // testing that an Exception is thrown + @SuppressWarnings("ReturnValueIgnored") // testing that an Exception is thrown public void testRelativize_providerMismatch2() { try (CloudStorageFileSystem fs = forBucket("doodle")) { thrown.expect(ProviderMismatchException.class); @@ -466,28 +467,21 @@ public void testEquals_currentDirectoryIsTakenIntoConsideration() { @Test public void testNullness() { try (CloudStorageFileSystem fs = forBucket("doodle")) { - NullPointerTester tester = new NullPointerTester() - .setDefault(Path.class, fs.getPath("sup")); + NullPointerTester tester = new NullPointerTester().setDefault(Path.class, fs.getPath("sup")); tester.testAllPublicStaticMethods(CloudStoragePath.class); tester.testAllPublicInstanceMethods(fs.getPath("sup")); } } private static CloudStorageConfiguration stripPrefixSlash(boolean value) { - return CloudStorageConfiguration.builder() - .stripPrefixSlash(value) - .build(); + return CloudStorageConfiguration.builder().stripPrefixSlash(value).build(); } private static CloudStorageConfiguration permitEmptyPathComponents(boolean value) { - return CloudStorageConfiguration.builder() - .permitEmptyPathComponents(value) - .build(); + return CloudStorageConfiguration.builder().permitEmptyPathComponents(value).build(); } private static CloudStorageConfiguration workingDirectory(String value) { - return CloudStorageConfiguration.builder() - .workingDirectory(value) - .build(); + return CloudStorageConfiguration.builder().workingDirectory(value).build(); } } diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageWriteChannelTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageWriteChannelTest.java index b98d92b4c85e..2f529940450f 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageWriteChannelTest.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageWriteChannelTest.java @@ -23,12 +23,13 @@ import java.nio.channels.ClosedChannelException; import java.nio.channels.NonReadableChannelException; -/** Unit tests for {@link CloudStorageWriteChannel}. */ +/** + * Unit tests for {@link CloudStorageWriteChannel}. + */ @RunWith(JUnit4.class) public class CloudStorageWriteChannelTest { - @Rule - public final ExpectedException thrown = ExpectedException.none(); + @Rule public final ExpectedException thrown = ExpectedException.none(); private final WriteChannel gcsChannel = mock(WriteChannel.class); private CloudStorageWriteChannel chan = new CloudStorageWriteChannel(gcsChannel); diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/UnixPathTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/UnixPathTest.java index 78c1b7f34f25..9a83105d1550 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/UnixPathTest.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/UnixPathTest.java @@ -12,12 +12,13 @@ import org.junit.runner.RunWith; import org.junit.runners.JUnit4; -/** Unit tests for {@link UnixPath}. */ +/** + * Unit tests for {@link UnixPath}. + */ @RunWith(JUnit4.class) public class UnixPathTest { - @Rule - public final ExpectedException thrown = ExpectedException.none(); + @Rule public final ExpectedException thrown = ExpectedException.none(); @Test public void testNormalize() { @@ -90,8 +91,7 @@ public void testGetPath() { assertThat(UnixPath.getPath(false, "hello")).isEqualTo(p("hello")); assertThat(UnixPath.getPath(false, "hello", "cat")).isEqualTo(p("hello/cat")); assertThat(UnixPath.getPath(false, "/hello", "cat")).isEqualTo(p("/hello/cat")); - assertThat(UnixPath.getPath(false, "/hello", "cat", "inc.")) - .isEqualTo(p("/hello/cat/inc.")); + assertThat(UnixPath.getPath(false, "/hello", "cat", "inc.")).isEqualTo(p("/hello/cat/inc.")); assertThat(UnixPath.getPath(false, "hello/", "/hi/there")).isEqualTo(p("/hi/there")); } @@ -111,12 +111,10 @@ public void testResolveSibling_preservesTrailingSlash() { public void testRelativize() { assertThat(p("/foo/bar/hop/dog").relativize(p("/foo/mop/top"))) .isEqualTo(p("../../../mop/top")); - assertThat(p("/foo/bar/dog").relativize(p("/foo/mop/top"))) - .isEqualTo(p("../../mop/top")); + assertThat(p("/foo/bar/dog").relativize(p("/foo/mop/top"))).isEqualTo(p("../../mop/top")); assertThat(p("/foo/bar/hop/dog").relativize(p("/foo/mop/top/../../mog"))) .isEqualTo(p("../../../mop/top/../../mog")); - assertThat(p("/foo/bar/hop/dog").relativize(p("/foo/../mog"))) - .isEqualTo(p("../../../../mog")); + assertThat(p("/foo/bar/hop/dog").relativize(p("/foo/../mog"))).isEqualTo(p("../../../../mog")); assertThat(p("").relativize(p("foo/mop/top/"))).isEqualTo(p("foo/mop/top/")); } From a7c46792d40f3e7fe972d84309afafbff6ed5331 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Fri, 4 Mar 2016 14:34:19 -0500 Subject: [PATCH 04/38] Correct more style issues and remove warnings This change is a follow-up to post-merge comments in #706 made by @ajkannan and @aozarov. The following changes have been made: - Don't use @Nullable on equals() methods - Don't `throws Exception` in test methods unless needed - Use incomplete sentences in JavaDoc summary fragment - Capitalize Cloud Storage - Get rid of some one-line JavaDocs - Use @code formatting when appropriate - Remove Paths.get() usages which is deprecated in Java 8 - Remove @SuppressWarnings("null") which no one uses --- .../nio/CloudStorageConfiguration.java | 25 ++-- .../nio/CloudStorageFileAttributeView.java | 3 +- .../nio/CloudStorageFileAttributes.java | 18 +-- .../contrib/nio/CloudStorageFileSystem.java | 14 +- .../nio/CloudStorageFileSystemProvider.java | 8 +- .../nio/CloudStorageObjectAttributes.java | 36 +---- .../CloudStorageObjectImmutableException.java | 2 +- .../storage/contrib/nio/CloudStoragePath.java | 2 +- ...CloudStoragePseudoDirectoryAttributes.java | 2 +- .../storage/contrib/nio/CloudStorageUtil.java | 2 +- .../cloud/storage/contrib/nio/UnixPath.java | 6 +- .../CloudStorageFileAttributeViewTest.java | 16 ++- .../nio/CloudStorageFileAttributesTest.java | 29 +++-- .../CloudStorageFileSystemProviderTest.java | 123 +++++++++--------- .../nio/CloudStorageFileSystemTest.java | 18 +-- .../contrib/nio/CloudStorageOptionsTest.java | 17 +-- .../contrib/nio/CloudStoragePathTest.java | 33 +++-- .../nio/CloudStorageWriteChannelTest.java | 17 +-- .../storage/contrib/nio/UnixPathTest.java | 5 +- 19 files changed, 179 insertions(+), 197 deletions(-) diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageConfiguration.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageConfiguration.java index 5de5f2b04dd5..26c6b1063812 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageConfiguration.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageConfiguration.java @@ -7,13 +7,13 @@ import java.util.Map; /** - * Configuration for a {@link CloudStorageFileSystem} instance. + * Configuration for {@link CloudStorageFileSystem} instances. */ @AutoValue public abstract class CloudStorageConfiguration { /** - * Returns path of the current working directory. This defaults to the root directory. + * Returns path of current working directory. This defaults to the root directory. */ public abstract String workingDirectory(); @@ -31,10 +31,14 @@ public abstract class CloudStorageConfiguration { */ public abstract boolean stripPrefixSlash(); - /** Return {@code true} if paths with a trailing slash should be treated as fake directories. */ + /** + * Returns {@code true} if paths with a trailing slash should be treated as fake directories. + */ public abstract boolean usePseudoDirectories(); - /** Returns the block size (in bytes) used when talking to the GCS HTTP server. */ + /** + * Returns block size (in bytes) used when talking to the GCS HTTP server. + */ public abstract int blockSize(); /** @@ -50,7 +54,8 @@ public static Builder builder() { return new Builder(); } - /** Builder for {@link CloudStorageConfiguration}. + /** + * Builder for {@link CloudStorageConfiguration}. */ public static final class Builder { @@ -61,8 +66,8 @@ public static final class Builder { private int blockSize = CloudStorageFileSystem.BLOCK_SIZE_DEFAULT; /** - * Changes the current working directory for a new filesystem. This cannot be changed once it's - * been set. You'll need to simply create another filesystem object. + * Changes current working directory for new filesystem. This cannot be changed once it's + * been set. You'll need to create another {@link CloudStorageFileSystem} object. * * @throws IllegalArgumentException if {@code path} is not absolute. */ @@ -92,7 +97,8 @@ public Builder stripPrefixSlash(boolean value) { return this; } - /** Configures if paths with a trailing slash should be treated as fake directories. + /** + * Configures if paths with a trailing slash should be treated as fake directories. */ public Builder usePseudoDirectories(boolean value) { usePseudoDirectories = value; @@ -109,7 +115,8 @@ public Builder blockSize(int value) { return this; } - /** Creates a new instance, but does not destroy the builder. + /** + * Creates new instance without destroying builder. */ public CloudStorageConfiguration build() { return new AutoValue_CloudStorageConfiguration( diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributeView.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributeView.java index 63168210c8c4..baec035af0a2 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributeView.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributeView.java @@ -12,7 +12,6 @@ import java.nio.file.attribute.FileTime; import java.util.Objects; -import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; /** @@ -59,7 +58,7 @@ public void setTimes(FileTime lastModifiedTime, FileTime lastAccessTime, FileTim } @Override - public boolean equals(@Nullable Object other) { + public boolean equals(Object other) { return this == other || other instanceof CloudStorageFileAttributeView && Objects.equals(storage, ((CloudStorageFileAttributeView) other).storage) diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributes.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributes.java index a4af002f9223..49d4169d6228 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributes.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributes.java @@ -7,53 +7,55 @@ import java.nio.file.attribute.BasicFileAttributes; import java.util.List; -/** Interface for attributes on a cloud storage file or pseudo-directory. */ +/** + * Interface for attributes on a Cloud Storage file or pseudo-directory. + */ public interface CloudStorageFileAttributes extends BasicFileAttributes { /** - * Returns the HTTP etag hash for this object. + * Returns HTTP etag hash of object contents. * * @see "https://developers.google.com/storage/docs/hashes-etags" */ Optional etag(); /** - * Returns the mime type (e.g. text/plain) if it was set for this object. + * Returns mime type (e.g. text/plain), if set. * * @see "http://en.wikipedia.org/wiki/Internet_media_type#List_of_common_media_types" */ Optional mimeType(); /** - * Returns the ACL value on this Cloud Storage object. + * Returns access control list. * * @see "https://developers.google.com/storage/docs/reference-headers#acl" */ Optional> acl(); /** - * Returns the {@code Cache-Control} HTTP header value, if set on this object. + * Returns {@code Cache-Control} HTTP header value, if set. * * @see "https://developers.google.com/storage/docs/reference-headers#cachecontrol" */ Optional cacheControl(); /** - * Returns the {@code Content-Encoding} HTTP header value, if set on this object. + * Returns {@code Content-Encoding} HTTP header value, if set. * * @see "https://developers.google.com/storage/docs/reference-headers#contentencoding" */ Optional contentEncoding(); /** - * Returns the {@code Content-Disposition} HTTP header value, if set on this object. + * Returns {@code Content-Disposition} HTTP header value, if set. * * @see "https://developers.google.com/storage/docs/reference-headers#contentdisposition" */ Optional contentDisposition(); /** - * Returns user-specified metadata associated with this object. + * Returns user-specified metadata. * * @see "https://developers.google.com/storage/docs/reference-headers#contentdisposition" */ diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystem.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystem.java index b768ba757329..e0143f099ef4 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystem.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystem.java @@ -24,7 +24,7 @@ import javax.annotation.concurrent.Immutable; /** - * Google Cloud Storage {@link FileSystem} + * Google Cloud Storage {@link FileSystem} implementation. * * @see * Concepts and Terminology @@ -35,7 +35,7 @@ public final class CloudStorageFileSystem extends FileSystem { /** - * Returns Google Cloud Storage {@link FileSystem} object for a given bucket name. + * Returns Google Cloud Storage {@link FileSystem} object for {@code bucket}. * *

NOTE: You may prefer to use Java's standard API instead:

   {@code
    *
@@ -54,7 +54,7 @@ public static CloudStorageFileSystem forBucket(String bucket) {
   }
 
   /**
-   * Creates a new filesystem for a particular bucket, with customizable settings.
+   * Creates new file system instance for {@code bucket}, with customizable settings.
    *
    * @see #forBucket(String)
    */
@@ -113,21 +113,21 @@ public CloudStorageFileSystemProvider provider() {
   }
 
   /**
-   * Returns the Cloud Storage bucket name being served by this file system.
+   * Returns Cloud Storage bucket name being served by this file system.
    */
   public String bucket() {
     return bucket;
   }
 
   /**
-   * Returns the configuration object for this filesystem instance.
+   * Returns configuration object for this file system instance.
    */
   public CloudStorageConfiguration config() {
     return config;
   }
 
   /**
-   * Converts a cloud storage object name to a {@link Path} object.
+   * Converts Cloud Storage object name to a {@link Path} object.
    */
   @Override
   public CloudStoragePath getPath(String first, String... more) {
@@ -211,7 +211,7 @@ public WatchService newWatchService() throws IOException {
   }
 
   @Override
-  public boolean equals(@Nullable Object other) {
+  public boolean equals(Object other) {
     return this == other
         || other instanceof CloudStorageFileSystem
             && Objects.equals(config, ((CloudStorageFileSystem) other).config)
diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProvider.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProvider.java
index 38086e7a1439..3a90f20d48e2 100644
--- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProvider.java
+++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProvider.java
@@ -56,7 +56,7 @@
 import javax.annotation.concurrent.ThreadSafe;
 
 /**
- * Google Cloud Storage {@link FileSystemProvider}.
+ * Google Cloud Storage {@link FileSystemProvider} implementation.
  */
 @ThreadSafe
 @AutoService(FileSystemProvider.class)
@@ -99,7 +99,7 @@ public String getScheme() {
   }
 
   /**
-   * Returns cloud storage file system, provided a URI with no path, e.g. {@code gs://bucket}.
+   * Returns Cloud Storage file system, provided a URI with no path, e.g. {@code gs://bucket}.
    */
   @Override
   public CloudStorageFileSystem getFileSystem(URI uri) {
@@ -107,7 +107,7 @@ public CloudStorageFileSystem getFileSystem(URI uri) {
   }
 
   /**
-   * Returns cloud storage file system, provided a URI with no path, e.g. {@code gs://bucket}.
+   * Returns Cloud Storage file system, provided a URI with no path, e.g. {@code gs://bucket}.
    */
   @Override
   public CloudStorageFileSystem newFileSystem(URI uri, Map env) {
@@ -537,7 +537,7 @@ public FileStore getFileStore(Path path) {
   }
 
   @Override
-  public boolean equals(@Nullable Object other) {
+  public boolean equals(Object other) {
     return this == other
         || other instanceof CloudStorageFileSystemProvider
             && Objects.equals(storage, ((CloudStorageFileSystemProvider) other).storage);
diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageObjectAttributes.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageObjectAttributes.java
index 0ca7f55cd6b1..19e6cb96ab4f 100644
--- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageObjectAttributes.java
+++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageObjectAttributes.java
@@ -13,11 +13,10 @@
 import java.util.Objects;
 
 import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
 import javax.annotation.concurrent.Immutable;
 
 /**
- * Metadata for a Google Cloud Storage object.
+ * Metadata for a Google Cloud Storage file.
  */
 @Immutable
 final class CloudStorageObjectAttributes implements CloudStorageFileAttributes {
@@ -46,67 +45,36 @@ public FileTime lastModifiedTime() {
     return creationTime();
   }
 
-  /**
-   * Returns the HTTP etag hash for this object.
-   */
   @Override
   public Optional etag() {
     return Optional.fromNullable(info.etag());
   }
 
-  /**
-   * Returns the mime type (e.g. text/plain) if it was set for this object.
-   */
   @Override
   public Optional mimeType() {
     return Optional.fromNullable(info.contentType());
   }
 
-  /**
-   * Returns the ACL value on this Cloud Storage object.
-   *
-   * @see "https://developers.google.com/storage/docs/reference-headers#acl"
-   */
   @Override
   public Optional> acl() {
     return Optional.fromNullable(info.acl());
   }
 
-  /**
-   * Returns the {@code Cache-Control} HTTP header value, if set on this object.
-   *
-   * @see "https://developers.google.com/storage/docs/reference-headers#cachecontrol"
-   */
   @Override
   public Optional cacheControl() {
     return Optional.fromNullable(info.cacheControl());
   }
 
-  /**
-   * Returns the {@code Content-Encoding} HTTP header value, if set on this object.
-   *
-   * @see "https://developers.google.com/storage/docs/reference-headers#contentencoding"
-   */
   @Override
   public Optional contentEncoding() {
     return Optional.fromNullable(info.contentEncoding());
   }
 
-  /**
-   * Returns the {@code Content-Disposition} HTTP header value, if set on this object.
-   *
-   * @see "https://developers.google.com/storage/docs/reference-headers#contentdisposition"
-   */
   @Override
   public Optional contentDisposition() {
     return Optional.fromNullable(info.contentDisposition());
   }
 
-  /**
-   * Returns user-specified metadata associated with this object.
-   *
-   * @see "https://developers.google.com/storage/docs/reference-headers#contentdisposition"
-   */
   @Override
   public ImmutableMap userMetadata() {
     if (null == info.metadata()) {
@@ -146,7 +114,7 @@ public Object fileKey() {
   }
 
   @Override
-  public boolean equals(@Nullable Object other) {
+  public boolean equals(Object other) {
     return this == other
         || other instanceof CloudStorageObjectAttributes
             && Objects.equals(info, ((CloudStorageObjectAttributes) other).info);
diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageObjectImmutableException.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageObjectImmutableException.java
index 31dfaa130659..1e4e348f4036 100644
--- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageObjectImmutableException.java
+++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageObjectImmutableException.java
@@ -1,7 +1,7 @@
 package com.google.cloud.storage.contrib.nio;
 
 /**
- * Exception thrown to indicate we don't support a mutation of a cloud storage object.
+ * Exception reminding user that Cloud Storage objects can't be mutated.
  */
 public final class CloudStorageObjectImmutableException extends UnsupportedOperationException {
 
diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStoragePath.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStoragePath.java
index 57e5b04542ce..d31a3e0af1a1 100644
--- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStoragePath.java
+++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStoragePath.java
@@ -273,7 +273,7 @@ public int compareTo(Path other) {
   }
 
   @Override
-  public boolean equals(@Nullable Object other) {
+  public boolean equals(Object other) {
     return this == other
         || other instanceof CloudStoragePath
             && Objects.equals(bucket(), ((CloudStoragePath) other).bucket())
diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStoragePseudoDirectoryAttributes.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStoragePseudoDirectoryAttributes.java
index 20c4b9bbd353..de94d27588c1 100644
--- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStoragePseudoDirectoryAttributes.java
+++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStoragePseudoDirectoryAttributes.java
@@ -10,7 +10,7 @@
 import java.util.List;
 
 /**
- * Metadata for a cloud storage pseudo-directory.
+ * Metadata for a Cloud Storage pseudo-directory.
  */
 final class CloudStoragePseudoDirectoryAttributes implements CloudStorageFileAttributes {
 
diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageUtil.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageUtil.java
index 42890f51ff5b..e86bad9f9946 100644
--- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageUtil.java
+++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageUtil.java
@@ -30,7 +30,7 @@ static CloudStoragePath checkPath(Path path) {
     if (!(checkNotNull(path) instanceof CloudStoragePath)) {
       throw new ProviderMismatchException(
           String.format(
-              "Not a cloud storage path: %s (%s)", path, path.getClass().getSimpleName()));
+              "Not a Cloud Storage path: %s (%s)", path, path.getClass().getSimpleName()));
     }
     return (CloudStoragePath) path;
   }
diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/UnixPath.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/UnixPath.java
index 7147530980b8..d4949addb9ff 100644
--- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/UnixPath.java
+++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/UnixPath.java
@@ -58,7 +58,7 @@ private UnixPath(boolean permitEmptyComponents, String path) {
   }
 
   /**
-   * Returns new UnixPath of {@code first}.
+   * Returns new path of {@code first}.
    */
   public static UnixPath getPath(boolean permitEmptyComponents, String path) {
     if (path.isEmpty()) {
@@ -71,7 +71,7 @@ public static UnixPath getPath(boolean permitEmptyComponents, String path) {
   }
 
   /**
-   * Returns new UnixPath of {@code first} with {@code more} components resolved against it.
+   * Returns new path of {@code first} with {@code more} components resolved against it.
    *
    * @see #resolve(UnixPath)
    * @see java.nio.file.FileSystem#getPath(String, String...)
@@ -464,7 +464,7 @@ public Iterator splitReverse() {
   }
 
   @Override
-  public boolean equals(@Nullable Object other) {
+  public boolean equals(Object other) {
     return this == other || other instanceof UnixPath && path.equals(((UnixPath) other).path);
   }
 
diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributeViewTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributeViewTest.java
index cf11b85f916b..f0e082dca04f 100644
--- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributeViewTest.java
+++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributeViewTest.java
@@ -3,9 +3,9 @@
 import static com.google.common.truth.Truth.assertThat;
 import static java.nio.charset.StandardCharsets.UTF_8;
 
+import com.google.cloud.storage.testing.LocalGcsHelper;
 import com.google.common.testing.EqualsTester;
 import com.google.common.testing.NullPointerTester;
-import com.google.cloud.storage.testing.LocalGcsHelper;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -14,6 +14,7 @@
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
+import java.io.IOException;
 import java.net.URI;
 import java.nio.file.Files;
 import java.nio.file.NoSuchFileException;
@@ -40,7 +41,7 @@ public void before() {
   }
 
   @Test
-  public void testReadAttributes() throws Exception {
+  public void testReadAttributes() throws IOException {
     Files.write(path, HAPPY, CloudStorageOptions.withCacheControl("potato"));
     CloudStorageFileAttributeView lazyAttributes =
         Files.getFileAttributeView(path, CloudStorageFileAttributeView.class);
@@ -48,7 +49,7 @@ public void testReadAttributes() throws Exception {
   }
 
   @Test
-  public void testReadAttributes_notFound_throwsNoSuchFileException() throws Exception {
+  public void testReadAttributes_notFound_throwsNoSuchFileException() throws IOException {
     CloudStorageFileAttributeView lazyAttributes =
         Files.getFileAttributeView(path, CloudStorageFileAttributeView.class);
     thrown.expect(NoSuchFileException.class);
@@ -56,7 +57,7 @@ public void testReadAttributes_notFound_throwsNoSuchFileException() throws Excep
   }
 
   @Test
-  public void testReadAttributes_pseudoDirectory() throws Exception {
+  public void testReadAttributes_pseudoDirectory() throws IOException {
     Path dir = Paths.get(URI.create("gs://red/rum/"));
     CloudStorageFileAttributeView lazyAttributes =
         Files.getFileAttributeView(dir, CloudStorageFileAttributeView.class);
@@ -65,7 +66,7 @@ public void testReadAttributes_pseudoDirectory() throws Exception {
   }
 
   @Test
-  public void testName() throws Exception {
+  public void testName() throws IOException {
     Files.write(path, HAPPY, CloudStorageOptions.withCacheControl("potato"));
     CloudStorageFileAttributeView lazyAttributes =
         Files.getFileAttributeView(path, CloudStorageFileAttributeView.class);
@@ -73,7 +74,7 @@ public void testName() throws Exception {
   }
 
   @Test
-  public void testEquals_equalsTester() throws Exception {
+  public void testEquals_equalsTester() {
     new EqualsTester()
         .addEqualityGroup(
             Files.getFileAttributeView(
@@ -87,8 +88,9 @@ public void testEquals_equalsTester() throws Exception {
   }
 
   @Test
-  public void testNullness() throws Exception {
+  public void testNullness() throws NoSuchMethodException, SecurityException {
     new NullPointerTester()
+        .ignore(CloudStorageFileAttributeView.class.getMethod("equals", Object.class))
         .setDefault(FileTime.class, FileTime.fromMillis(0))
         .testAllPublicInstanceMethods(
             Files.getFileAttributeView(path, CloudStorageFileAttributeView.class));
diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributesTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributesTest.java
index 7dfa605fa767..d5bbcdf7dbb8 100644
--- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributesTest.java
+++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributesTest.java
@@ -19,6 +19,7 @@
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
+import java.io.IOException;
 import java.net.URI;
 import java.nio.file.Files;
 import java.nio.file.Path;
@@ -35,7 +36,6 @@ public class CloudStorageFileAttributesTest {
   private Path path;
   private Path dir;
 
-  /** empty test storage and make sure we use it instead of the real GCS. Create a few paths. **/
   @Before
   public void before() {
     CloudStorageFileSystemProvider.setGCloudOptions(LocalGcsHelper.options());
@@ -44,21 +44,21 @@ public void before() {
   }
 
   @Test
-  public void testCacheControl() throws Exception {
+  public void testCacheControl() throws IOException {
     Files.write(path, HAPPY, withCacheControl("potato"));
     assertThat(Files.readAttributes(path, CloudStorageFileAttributes.class).cacheControl().get())
         .isEqualTo("potato");
   }
 
   @Test
-  public void testMimeType() throws Exception {
+  public void testMimeType() throws IOException {
     Files.write(path, HAPPY, withMimeType("text/potato"));
     assertThat(Files.readAttributes(path, CloudStorageFileAttributes.class).mimeType().get())
         .isEqualTo("text/potato");
   }
 
   @Test
-  public void testAcl() throws Exception {
+  public void testAcl() throws IOException {
     Acl acl = Acl.of(new Acl.User("serf@example.com"), Acl.Role.READER);
     Files.write(path, HAPPY, withAcl(acl));
     assertThat(Files.readAttributes(path, CloudStorageFileAttributes.class).acl().get())
@@ -66,7 +66,7 @@ public void testAcl() throws Exception {
   }
 
   @Test
-  public void testContentDisposition() throws Exception {
+  public void testContentDisposition() throws IOException {
     Files.write(path, HAPPY, withContentDisposition("crash call"));
     assertThat(
             Files.readAttributes(path, CloudStorageFileAttributes.class).contentDisposition().get())
@@ -74,14 +74,14 @@ public void testContentDisposition() throws Exception {
   }
 
   @Test
-  public void testContentEncoding() throws Exception {
+  public void testContentEncoding() throws IOException {
     Files.write(path, HAPPY, withContentEncoding("my content encoding"));
     assertThat(Files.readAttributes(path, CloudStorageFileAttributes.class).contentEncoding().get())
         .isEqualTo("my content encoding");
   }
 
   @Test
-  public void testUserMetadata() throws Exception {
+  public void testUserMetadata() throws IOException {
     Files.write(path, HAPPY, withUserMetadata("green", "bean"));
     assertThat(
             Files.readAttributes(path, CloudStorageFileAttributes.class)
@@ -91,7 +91,7 @@ public void testUserMetadata() throws Exception {
   }
 
   @Test
-  public void testIsDirectory() throws Exception {
+  public void testIsDirectory() throws IOException {
     Files.write(path, HAPPY);
     assertThat(Files.readAttributes(path, CloudStorageFileAttributes.class).isDirectory())
         .isFalse();
@@ -99,7 +99,7 @@ public void testIsDirectory() throws Exception {
   }
 
   @Test
-  public void testIsRegularFile() throws Exception {
+  public void testIsRegularFile() throws IOException {
     Files.write(path, HAPPY);
     assertThat(Files.readAttributes(path, CloudStorageFileAttributes.class).isRegularFile())
         .isTrue();
@@ -108,14 +108,14 @@ public void testIsRegularFile() throws Exception {
   }
 
   @Test
-  public void testIsOther() throws Exception {
+  public void testIsOther() throws IOException {
     Files.write(path, HAPPY);
     assertThat(Files.readAttributes(path, CloudStorageFileAttributes.class).isOther()).isFalse();
     assertThat(Files.readAttributes(dir, CloudStorageFileAttributes.class).isOther()).isFalse();
   }
 
   @Test
-  public void testIsSymbolicLink() throws Exception {
+  public void testIsSymbolicLink() throws IOException {
     Files.write(path, HAPPY);
     assertThat(Files.readAttributes(path, CloudStorageFileAttributes.class).isSymbolicLink())
         .isFalse();
@@ -124,7 +124,7 @@ public void testIsSymbolicLink() throws Exception {
   }
 
   @Test
-  public void testEquals_equalsTester() throws Exception {
+  public void testEquals_equalsTester() throws IOException {
     Files.write(path, HAPPY, withMimeType("text/plain"));
     CloudStorageFileAttributes a1 = Files.readAttributes(path, CloudStorageFileAttributes.class);
     CloudStorageFileAttributes a2 = Files.readAttributes(path, CloudStorageFileAttributes.class);
@@ -135,7 +135,7 @@ public void testEquals_equalsTester() throws Exception {
   }
 
   @Test
-  public void testFilekey() throws Exception {
+  public void testFilekey() throws IOException {
     Files.write(path, HAPPY, withMimeType("text/plain"));
     Path path2 = Paths.get(URI.create("gs://bucket/anotherrandompath"));
     Files.write(path2, HAPPY, withMimeType("text/plain"));
@@ -155,13 +155,14 @@ public void testFilekey() throws Exception {
   }
 
   @Test
-  public void testNullness() throws Exception {
+  public void testNullness() throws IOException, NoSuchMethodException, SecurityException {
     Files.write(path, HAPPY);
     CloudStorageFileAttributes pathAttributes =
         Files.readAttributes(path, CloudStorageFileAttributes.class);
     CloudStorageFileAttributes dirAttributes =
         Files.readAttributes(dir, CloudStorageFileAttributes.class);
     NullPointerTester tester = new NullPointerTester();
+    tester.ignore(CloudStorageObjectAttributes.class.getMethod("equals", Object.class));
     tester.testAllPublicInstanceMethods(pathAttributes);
     tester.testAllPublicInstanceMethods(dirAttributes);
   }
diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProviderTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProviderTest.java
index 1c64010850ad..67b8f6b89263 100644
--- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProviderTest.java
+++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProviderTest.java
@@ -26,6 +26,7 @@
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
+import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.net.URI;
@@ -83,19 +84,19 @@ public void before() {
   }
 
   @Test
-  public void testSize() throws Exception {
+  public void testSize() throws IOException {
     Path path = Paths.get(URI.create("gs://bucket/wat"));
     Files.write(path, SINGULARITY.getBytes(UTF_8));
     assertThat(Files.size(path)).isEqualTo(SINGULARITY.getBytes(UTF_8).length);
   }
 
   @Test
-  public void testSize_trailingSlash_returnsFakePseudoDirectorySize() throws Exception {
+  public void testSize_trailingSlash_returnsFakePseudoDirectorySize() throws IOException {
     assertThat(Files.size(Paths.get(URI.create("gs://bucket/wat/")))).isEqualTo(1);
   }
 
   @Test
-  public void testSize_trailingSlash_disablePseudoDirectories() throws Exception {
+  public void testSize_trailingSlash_disablePseudoDirectories() throws IOException {
     try (CloudStorageFileSystem fs = forBucket("doodle", usePseudoDirectories(false))) {
       Path path = fs.getPath("wat/");
       byte[] rapture = SINGULARITY.getBytes(UTF_8);
@@ -105,20 +106,20 @@ public void testSize_trailingSlash_disablePseudoDirectories() throws Exception {
   }
 
   @Test
-  public void testReadAllBytes() throws Exception {
+  public void testReadAllBytes() throws IOException {
     Path path = Paths.get(URI.create("gs://bucket/wat"));
     Files.write(path, SINGULARITY.getBytes(UTF_8));
     assertThat(new String(Files.readAllBytes(path), UTF_8)).isEqualTo(SINGULARITY);
   }
 
   @Test
-  public void testReadAllBytes_trailingSlash() throws Exception {
+  public void testReadAllBytes_trailingSlash() throws IOException {
     thrown.expect(CloudStoragePseudoDirectoryException.class);
     Files.readAllBytes(Paths.get(URI.create("gs://bucket/wat/")));
   }
 
   @Test
-  public void testNewByteChannelRead() throws Exception {
+  public void testNewByteChannelRead() throws IOException {
     Path path = Paths.get(URI.create("gs://bucket/wat"));
     byte[] data = SINGULARITY.getBytes(UTF_8);
     Files.write(path, data);
@@ -132,7 +133,7 @@ public void testNewByteChannelRead() throws Exception {
   }
 
   @Test
-  public void testNewByteChannelRead_seeking() throws Exception {
+  public void testNewByteChannelRead_seeking() throws IOException {
     Path path = Paths.get(URI.create("gs://lol/cat"));
     Files.write(path, "helloworld".getBytes(UTF_8));
     try (SeekableByteChannel input = Files.newByteChannel(path)) {
@@ -153,7 +154,7 @@ public void testNewByteChannelRead_seeking() throws Exception {
   }
 
   @Test
-  public void testNewByteChannelRead_seekBeyondSize_reportsEofOnNextRead() throws Exception {
+  public void testNewByteChannelRead_seekBeyondSize_reportsEofOnNextRead() throws IOException {
     Path path = Paths.get(URI.create("gs://lol/cat"));
     Files.write(path, "hellocat".getBytes(UTF_8));
     try (SeekableByteChannel input = Files.newByteChannel(path)) {
@@ -167,21 +168,21 @@ public void testNewByteChannelRead_seekBeyondSize_reportsEofOnNextRead() throws
   }
 
   @Test
-  public void testNewByteChannelRead_trailingSlash() throws Exception {
+  public void testNewByteChannelRead_trailingSlash() throws IOException {
     Path path = Paths.get(URI.create("gs://bucket/wat/"));
     thrown.expect(CloudStoragePseudoDirectoryException.class);
     Files.newByteChannel(path);
   }
 
   @Test
-  public void testNewByteChannelRead_notFound() throws Exception {
+  public void testNewByteChannelRead_notFound() throws IOException {
     Path path = Paths.get(URI.create("gs://bucket/wednesday"));
     thrown.expect(NoSuchFileException.class);
     Files.newByteChannel(path);
   }
 
   @Test
-  public void testNewByteChannelWrite() throws Exception {
+  public void testNewByteChannelWrite() throws IOException {
     Path path = Paths.get(URI.create("gs://bucket/tests"));
     try (SeekableByteChannel output = Files.newByteChannel(path, WRITE)) {
       assertThat(output.position()).isEqualTo(0);
@@ -199,7 +200,7 @@ public void testNewByteChannelWrite() throws Exception {
   }
 
   @Test
-  public void testNewInputStream() throws Exception {
+  public void testNewInputStream() throws IOException {
     Path path = Paths.get(URI.create("gs://bucket/wat"));
     Files.write(path, SINGULARITY.getBytes(UTF_8));
     try (InputStream input = Files.newInputStream(path)) {
@@ -210,7 +211,7 @@ public void testNewInputStream() throws Exception {
   }
 
   @Test
-  public void testNewInputStream_trailingSlash() throws Exception {
+  public void testNewInputStream_trailingSlash() throws IOException {
     Path path = Paths.get(URI.create("gs://bucket/wat/"));
     thrown.expect(CloudStoragePseudoDirectoryException.class);
     try (InputStream input = Files.newInputStream(path)) {
@@ -219,7 +220,7 @@ public void testNewInputStream_trailingSlash() throws Exception {
   }
 
   @Test
-  public void testNewInputStream_notFound() throws Exception {
+  public void testNewInputStream_notFound() throws IOException {
     Path path = Paths.get(URI.create("gs://cry/wednesday"));
     thrown.expect(NoSuchFileException.class);
     try (InputStream input = Files.newInputStream(path)) {
@@ -228,7 +229,7 @@ public void testNewInputStream_notFound() throws Exception {
   }
 
   @Test
-  public void testNewOutputStream() throws Exception {
+  public void testNewOutputStream() throws IOException {
     Path path = Paths.get(URI.create("gs://bucket/wat"));
     Files.write(path, SINGULARITY.getBytes(UTF_8));
     try (OutputStream output = Files.newOutputStream(path)) {
@@ -238,7 +239,7 @@ public void testNewOutputStream() throws Exception {
   }
 
   @Test
-  public void testNewOutputStream_truncateByDefault() throws Exception {
+  public void testNewOutputStream_truncateByDefault() throws IOException {
     Path path = Paths.get(URI.create("gs://bucket/wat"));
     Files.write(path, SINGULARITY.getBytes(UTF_8));
     Files.write(path, "hello".getBytes(UTF_8));
@@ -249,7 +250,7 @@ public void testNewOutputStream_truncateByDefault() throws Exception {
   }
 
   @Test
-  public void testNewOutputStream_truncateExplicitly() throws Exception {
+  public void testNewOutputStream_truncateExplicitly() throws IOException {
     Path path = Paths.get(URI.create("gs://bucket/wat"));
     Files.write(path, SINGULARITY.getBytes(UTF_8));
     Files.write(path, "hello".getBytes(UTF_8));
@@ -260,20 +261,20 @@ public void testNewOutputStream_truncateExplicitly() throws Exception {
   }
 
   @Test
-  public void testNewOutputStream_trailingSlash() throws Exception {
+  public void testNewOutputStream_trailingSlash() throws IOException {
     Path path = Paths.get(URI.create("gs://bucket/wat/"));
     thrown.expect(CloudStoragePseudoDirectoryException.class);
     Files.newOutputStream(path);
   }
 
   @Test
-  public void testNewOutputStream_createNew() throws Exception {
+  public void testNewOutputStream_createNew() throws IOException {
     Path path = Paths.get(URI.create("gs://cry/wednesday"));
     Files.newOutputStream(path, CREATE_NEW);
   }
 
   @Test
-  public void testNewOutputStream_createNew_alreadyExists() throws Exception {
+  public void testNewOutputStream_createNew_alreadyExists() throws IOException {
     Path path = Paths.get(URI.create("gs://cry/wednesday"));
     Files.write(path, SINGULARITY.getBytes(UTF_8));
     thrown.expect(FileAlreadyExistsException.class);
@@ -281,14 +282,14 @@ public void testNewOutputStream_createNew_alreadyExists() throws Exception {
   }
 
   @Test
-  public void testWrite_objectNameWithExtraSlashes_throwsIae() throws Exception {
+  public void testWrite_objectNameWithExtraSlashes_throwsIae() throws IOException {
     Path path = Paths.get(URI.create("gs://double/slash//yep"));
     thrown.expect(IllegalArgumentException.class);
     Files.write(path, FILE_CONTENTS, UTF_8);
   }
 
   @Test
-  public void testWrite_objectNameWithExtraSlashes_canBeNormalized() throws Exception {
+  public void testWrite_objectNameWithExtraSlashes_canBeNormalized() throws IOException {
     try (CloudStorageFileSystem fs = forBucket("greenbean", permitEmptyPathComponents(false))) {
       Path path = fs.getPath("adipose//yep").normalize();
       Files.write(path, FILE_CONTENTS, UTF_8);
@@ -298,7 +299,7 @@ public void testWrite_objectNameWithExtraSlashes_canBeNormalized() throws Except
   }
 
   @Test
-  public void testWrite_objectNameWithExtraSlashes_permitEmptyPathComponents() throws Exception {
+  public void testWrite_objectNameWithExtraSlashes_permitEmptyPathComponents() throws IOException {
     try (CloudStorageFileSystem fs = forBucket("greenbean", permitEmptyPathComponents(true))) {
       Path path = fs.getPath("adipose//yep");
       Files.write(path, FILE_CONTENTS, UTF_8);
@@ -308,7 +309,7 @@ public void testWrite_objectNameWithExtraSlashes_permitEmptyPathComponents() thr
   }
 
   @Test
-  public void testWrite_absoluteObjectName_prefixSlashGetsRemoved() throws Exception {
+  public void testWrite_absoluteObjectName_prefixSlashGetsRemoved() throws IOException {
     Path path = Paths.get(URI.create("gs://greenbean/adipose/yep"));
     Files.write(path, FILE_CONTENTS, UTF_8);
     assertThat(Files.readAllLines(path, UTF_8)).isEqualTo(FILE_CONTENTS);
@@ -316,7 +317,7 @@ public void testWrite_absoluteObjectName_prefixSlashGetsRemoved() throws Excepti
   }
 
   @Test
-  public void testWrite_absoluteObjectName_disableStrip_slashGetsPreserved() throws Exception {
+  public void testWrite_absoluteObjectName_disableStrip_slashGetsPreserved() throws IOException {
     try (CloudStorageFileSystem fs =
             forBucket(
                 "greenbean", CloudStorageConfiguration.builder().stripPrefixSlash(false).build())) {
@@ -328,14 +329,14 @@ public void testWrite_absoluteObjectName_disableStrip_slashGetsPreserved() throw
   }
 
   @Test
-  public void testWrite() throws Exception {
+  public void testWrite() throws IOException {
     Path path = Paths.get(URI.create("gs://greenbean/adipose"));
     Files.write(path, FILE_CONTENTS, UTF_8);
     assertThat(Files.readAllLines(path, UTF_8)).isEqualTo(FILE_CONTENTS);
   }
 
   @Test
-  public void testWriteOnClose() throws Exception {
+  public void testWriteOnClose() throws IOException {
     Path path = Paths.get(URI.create("gs://greenbean/adipose"));
     try (SeekableByteChannel chan = Files.newByteChannel(path, StandardOpenOption.WRITE)) {
       // writing lots of contents to defeat channel-internal buffering.
@@ -359,34 +360,34 @@ public void testWriteOnClose() throws Exception {
   }
 
   @Test
-  public void testWrite_trailingSlash() throws Exception {
+  public void testWrite_trailingSlash() throws IOException {
     thrown.expect(CloudStoragePseudoDirectoryException.class);
     Files.write(Paths.get(URI.create("gs://greenbean/adipose/")), FILE_CONTENTS, UTF_8);
   }
 
   @Test
-  public void testExists() throws Exception {
+  public void testExists() throws IOException {
     assertThat(Files.exists(Paths.get(URI.create("gs://military/fashion")))).isFalse();
     Files.write(Paths.get(URI.create("gs://military/fashion")), "(✿◕ ‿◕ )ノ".getBytes(UTF_8));
     assertThat(Files.exists(Paths.get(URI.create("gs://military/fashion")))).isTrue();
   }
 
   @Test
-  public void testExists_trailingSlash() throws Exception {
+  public void testExists_trailingSlash() {
     assertThat(Files.exists(Paths.get(URI.create("gs://military/fashion/")))).isTrue();
     assertThat(Files.exists(Paths.get(URI.create("gs://military/fashion/.")))).isTrue();
     assertThat(Files.exists(Paths.get(URI.create("gs://military/fashion/..")))).isTrue();
   }
 
   @Test
-  public void testExists_trailingSlash_disablePseudoDirectories() throws Exception {
+  public void testExists_trailingSlash_disablePseudoDirectories() {
     try (CloudStorageFileSystem fs = forBucket("military", usePseudoDirectories(false))) {
       assertThat(Files.exists(fs.getPath("fashion/"))).isFalse();
     }
   }
 
   @Test
-  public void testDelete() throws Exception {
+  public void testDelete() throws IOException {
     Files.write(Paths.get(URI.create("gs://love/fashion")), "(✿◕ ‿◕ )ノ".getBytes(UTF_8));
     assertThat(Files.exists(Paths.get(URI.create("gs://love/fashion")))).isTrue();
     Files.delete(Paths.get(URI.create("gs://love/fashion")));
@@ -394,19 +395,19 @@ public void testDelete() throws Exception {
   }
 
   @Test
-  public void testDelete_dotDirNotNormalized_throwsIae() throws Exception {
+  public void testDelete_dotDirNotNormalized_throwsIae() throws IOException {
     thrown.expect(IllegalArgumentException.class);
     Files.delete(Paths.get(URI.create("gs://love/fly/../passion")));
   }
 
   @Test
-  public void testDelete_trailingSlash() throws Exception {
+  public void testDelete_trailingSlash() throws IOException {
     thrown.expect(CloudStoragePseudoDirectoryException.class);
     Files.delete(Paths.get(URI.create("gs://love/passion/")));
   }
 
   @Test
-  public void testDelete_trailingSlash_disablePseudoDirectories() throws Exception {
+  public void testDelete_trailingSlash_disablePseudoDirectories() throws IOException {
     try (CloudStorageFileSystem fs = forBucket("pumpkin", usePseudoDirectories(false))) {
       Path path = fs.getPath("wat/");
       Files.write(path, FILE_CONTENTS, UTF_8);
@@ -417,25 +418,25 @@ public void testDelete_trailingSlash_disablePseudoDirectories() throws Exception
   }
 
   @Test
-  public void testDelete_notFound() throws Exception {
+  public void testDelete_notFound() throws IOException {
     thrown.expect(NoSuchFileException.class);
     Files.delete(Paths.get(URI.create("gs://loveh/passionehu")));
   }
 
   @Test
-  public void testDeleteIfExists() throws Exception {
+  public void testDeleteIfExists() throws IOException {
     Files.write(Paths.get(URI.create("gs://love/passionz")), "(✿◕ ‿◕ )ノ".getBytes(UTF_8));
     assertThat(Files.deleteIfExists(Paths.get(URI.create("gs://love/passionz")))).isTrue();
   }
 
   @Test
-  public void testDeleteIfExists_trailingSlash() throws Exception {
+  public void testDeleteIfExists_trailingSlash() throws IOException {
     thrown.expect(CloudStoragePseudoDirectoryException.class);
     Files.deleteIfExists(Paths.get(URI.create("gs://love/passion/")));
   }
 
   @Test
-  public void testCopy() throws Exception {
+  public void testCopy() throws IOException {
     Path source = Paths.get(URI.create("gs://military/fashion.show"));
     Path target = Paths.get(URI.create("gs://greenbean/adipose"));
     Files.write(source, "(✿◕ ‿◕ )ノ".getBytes(UTF_8));
@@ -446,7 +447,7 @@ public void testCopy() throws Exception {
   }
 
   @Test
-  public void testCopy_sourceMissing_throwsNoSuchFileException() throws Exception {
+  public void testCopy_sourceMissing_throwsNoSuchFileException() throws IOException {
     thrown.expect(NoSuchFileException.class);
     Files.copy(
         Paths.get(URI.create("gs://military/fashion.show")),
@@ -454,7 +455,7 @@ public void testCopy_sourceMissing_throwsNoSuchFileException() throws Exception
   }
 
   @Test
-  public void testCopy_targetExists_throwsFileAlreadyExistsException() throws Exception {
+  public void testCopy_targetExists_throwsFileAlreadyExistsException() throws IOException {
     Path source = Paths.get(URI.create("gs://military/fashion.show"));
     Path target = Paths.get(URI.create("gs://greenbean/adipose"));
     Files.write(source, "(✿◕ ‿◕ )ノ".getBytes(UTF_8));
@@ -464,7 +465,7 @@ public void testCopy_targetExists_throwsFileAlreadyExistsException() throws Exce
   }
 
   @Test
-  public void testCopyReplace_targetExists_works() throws Exception {
+  public void testCopyReplace_targetExists_works() throws IOException {
     Path source = Paths.get(URI.create("gs://military/fashion.show"));
     Path target = Paths.get(URI.create("gs://greenbean/adipose"));
     Files.write(source, "(✿◕ ‿◕ )ノ".getBytes(UTF_8));
@@ -473,14 +474,14 @@ public void testCopyReplace_targetExists_works() throws Exception {
   }
 
   @Test
-  public void testCopy_directory_doesNothing() throws Exception {
+  public void testCopy_directory_doesNothing() throws IOException {
     Path source = Paths.get(URI.create("gs://military/fundir/"));
     Path target = Paths.get(URI.create("gs://greenbean/loldir/"));
     Files.copy(source, target);
   }
 
   @Test
-  public void testCopy_atomic_throwsUnsupported() throws Exception {
+  public void testCopy_atomic_throwsUnsupported() throws IOException {
     Path source = Paths.get(URI.create("gs://military/fashion.show"));
     Path target = Paths.get(URI.create("gs://greenbean/adipose"));
     Files.write(source, "(✿◕ ‿◕ )ノ".getBytes(UTF_8));
@@ -489,7 +490,7 @@ public void testCopy_atomic_throwsUnsupported() throws Exception {
   }
 
   @Test
-  public void testMove() throws Exception {
+  public void testMove() throws IOException {
     Path source = Paths.get(URI.create("gs://military/fashion.show"));
     Path target = Paths.get(URI.create("gs://greenbean/adipose"));
     Files.write(source, "(✿◕ ‿◕ )ノ".getBytes(UTF_8));
@@ -500,14 +501,14 @@ public void testMove() throws Exception {
   }
 
   @Test
-  public void testCreateDirectory() throws Exception {
+  public void testCreateDirectory() throws IOException {
     Path path = Paths.get(URI.create("gs://greenbean/dir/"));
     Files.createDirectory(path);
     assertThat(Files.exists(path)).isTrue();
   }
 
   @Test
-  public void testMove_atomicMove_notSupported() throws Exception {
+  public void testMove_atomicMove_notSupported() throws IOException {
     Path source = Paths.get(URI.create("gs://military/fashion.show"));
     Path target = Paths.get(URI.create("gs://greenbean/adipose"));
     Files.write(source, "(✿◕ ‿◕ )ノ".getBytes(UTF_8));
@@ -516,7 +517,7 @@ public void testMove_atomicMove_notSupported() throws Exception {
   }
 
   @Test
-  public void testIsDirectory() throws Exception {
+  public void testIsDirectory() throws IOException {
     try (FileSystem fs = FileSystems.getFileSystem(URI.create("gs://doodle"))) {
       assertThat(Files.isDirectory(fs.getPath(""))).isTrue();
       assertThat(Files.isDirectory(fs.getPath("/"))).isTrue();
@@ -530,19 +531,19 @@ public void testIsDirectory() throws Exception {
   }
 
   @Test
-  public void testIsDirectory_trailingSlash_alwaysTrue() throws Exception {
+  public void testIsDirectory_trailingSlash_alwaysTrue() {
     assertThat(Files.isDirectory(Paths.get(URI.create("gs://military/fundir/")))).isTrue();
   }
 
   @Test
-  public void testIsDirectory_trailingSlash_pseudoDirectoriesDisabled_false() throws Exception {
+  public void testIsDirectory_trailingSlash_pseudoDirectoriesDisabled_false() {
     try (CloudStorageFileSystem fs = forBucket("doodle", usePseudoDirectories(false))) {
       assertThat(Files.isDirectory(fs.getPath("fundir/"))).isFalse();
     }
   }
 
   @Test
-  public void testCopy_withCopyAttributes_preservesAttributes() throws Exception {
+  public void testCopy_withCopyAttributes_preservesAttributes() throws IOException {
     Path source = Paths.get(URI.create("gs://military/fashion.show"));
     Path target = Paths.get(URI.create("gs://greenbean/adipose"));
     Files.write(
@@ -566,7 +567,7 @@ public void testCopy_withCopyAttributes_preservesAttributes() throws Exception {
   }
 
   @Test
-  public void testCopy_withoutOptions_doesntPreservesAttributes() throws Exception {
+  public void testCopy_withoutOptions_doesntPreservesAttributes() throws IOException {
     Path source = Paths.get(URI.create("gs://military/fashion.show"));
     Path target = Paths.get(URI.create("gs://greenbean/adipose"));
     Files.write(
@@ -587,7 +588,7 @@ public void testCopy_withoutOptions_doesntPreservesAttributes() throws Exception
   }
 
   @Test
-  public void testCopy_overwriteAttributes() throws Exception {
+  public void testCopy_overwriteAttributes() throws IOException {
     Path source = Paths.get(URI.create("gs://military/fashion.show"));
     Path target1 = Paths.get(URI.create("gs://greenbean/adipose"));
     Path target2 = Paths.get(URI.create("gs://greenbean/round"));
@@ -610,14 +611,14 @@ public void testCopy_overwriteAttributes() throws Exception {
   }
 
   @Test
-  public void testNullness() throws Exception {
+  public void testNullness() throws IOException, NoSuchMethodException, SecurityException {
     try (FileSystem fs = FileSystems.getFileSystem(URI.create("gs://blood"))) {
-      NullPointerTester tester =
-          new NullPointerTester()
-              .setDefault(URI.class, URI.create("gs://blood"))
-              .setDefault(Path.class, fs.getPath("and/one"))
-              .setDefault(OpenOption.class, StandardOpenOption.CREATE)
-              .setDefault(CopyOption.class, StandardCopyOption.COPY_ATTRIBUTES);
+      NullPointerTester tester = new NullPointerTester();
+      tester.ignore(CloudStorageFileSystemProvider.class.getMethod("equals", Object.class));
+      tester.setDefault(URI.class, URI.create("gs://blood"));
+      tester.setDefault(Path.class, fs.getPath("and/one"));
+      tester.setDefault(OpenOption.class, StandardOpenOption.CREATE);
+      tester.setDefault(CopyOption.class, StandardCopyOption.COPY_ATTRIBUTES);
       // can't do that, setGCloudOptions accepts a null argument.
       // TODO(jart): Figure out how to re-enable this.
       // tester.testAllPublicStaticMethods(CloudStorageFileSystemProvider.class);
@@ -626,7 +627,7 @@ public void testNullness() throws Exception {
   }
 
   @Test
-  public void testProviderEquals() throws Exception {
+  public void testProviderEquals() {
     Path path1 = Paths.get(URI.create("gs://bucket/tuesday"));
     Path path2 = Paths.get(URI.create("gs://blood/wednesday"));
     Path path3 = Paths.get("tmp");
diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemTest.java
index ce819c1216c7..4b8a17df785b 100644
--- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemTest.java
+++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemTest.java
@@ -12,6 +12,7 @@
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
+import java.io.IOException;
 import java.net.URI;
 import java.nio.file.FileSystem;
 import java.nio.file.FileSystems;
@@ -40,7 +41,7 @@ public void before() {
   }
 
   @Test
-  public void testGetPath() throws Exception {
+  public void testGetPath() throws IOException {
     try (FileSystem fs = CloudStorageFileSystem.forBucket("bucket")) {
       assertThat(fs.getPath("/angel").toString()).isEqualTo("/angel");
       assertThat(fs.getPath("/angel").toUri().toString()).isEqualTo("gs://bucket/angel");
@@ -48,7 +49,7 @@ public void testGetPath() throws Exception {
   }
 
   @Test
-  public void testWrite() throws Exception {
+  public void testWrite() throws IOException {
     try (FileSystem fs = CloudStorageFileSystem.forBucket("bucket")) {
       Files.write(fs.getPath("/angel"), ALONE.getBytes(UTF_8));
     }
@@ -57,7 +58,7 @@ public void testWrite() throws Exception {
   }
 
   @Test
-  public void testRead() throws Exception {
+  public void testRead() throws IOException {
     Files.write(Paths.get(URI.create("gs://bucket/angel")), ALONE.getBytes(UTF_8));
     try (FileSystem fs = CloudStorageFileSystem.forBucket("bucket")) {
       assertThat(new String(Files.readAllBytes(fs.getPath("/angel")), UTF_8)).isEqualTo(ALONE);
@@ -65,14 +66,14 @@ public void testRead() throws Exception {
   }
 
   @Test
-  public void testExists_false() throws Exception {
+  public void testExists_false() throws IOException {
     try (FileSystem fs = FileSystems.getFileSystem(URI.create("gs://bucket"))) {
       assertThat(Files.exists(fs.getPath("/angel"))).isFalse();
     }
   }
 
   @Test
-  public void testExists_true() throws Exception {
+  public void testExists_true() throws IOException {
     Files.write(Paths.get(URI.create("gs://bucket/angel")), ALONE.getBytes(UTF_8));
     try (FileSystem fs = CloudStorageFileSystem.forBucket("bucket")) {
       assertThat(Files.exists(fs.getPath("/angel"))).isTrue();
@@ -80,7 +81,7 @@ public void testExists_true() throws Exception {
   }
 
   @Test
-  public void testGetters() throws Exception {
+  public void testGetters() throws IOException {
     try (FileSystem fs = CloudStorageFileSystem.forBucket("bucket")) {
       assertThat(fs.isOpen()).isTrue();
       assertThat(fs.isReadOnly()).isFalse();
@@ -92,7 +93,7 @@ public void testGetters() throws Exception {
   }
 
   @Test
-  public void testEquals() throws Exception {
+  public void testEquals() throws IOException {
     try (FileSystem bucket1 = CloudStorageFileSystem.forBucket("bucket");
         FileSystem bucket2 = FileSystems.getFileSystem(URI.create("gs://bucket"));
         FileSystem doge1 = CloudStorageFileSystem.forBucket("doge");
@@ -105,10 +106,11 @@ public void testEquals() throws Exception {
   }
 
   @Test
-  public void testNullness() throws Exception {
+  public void testNullness() throws IOException, NoSuchMethodException, SecurityException {
     try (FileSystem fs = FileSystems.getFileSystem(URI.create("gs://bucket"))) {
       NullPointerTester tester =
           new NullPointerTester()
+              .ignore(CloudStorageFileSystem.class.getMethod("equals", Object.class))
               .setDefault(CloudStorageConfiguration.class, CloudStorageConfiguration.DEFAULT);
       tester.testAllPublicStaticMethods(CloudStorageFileSystem.class);
       tester.testAllPublicInstanceMethods(fs);
diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageOptionsTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageOptionsTest.java
index 9f7006e73fb5..a6f47a3dc734 100644
--- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageOptionsTest.java
+++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageOptionsTest.java
@@ -19,6 +19,7 @@
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
+import java.io.IOException;
 import java.net.URI;
 import java.nio.file.Files;
 import java.nio.file.Path;
@@ -36,7 +37,7 @@ public void before() {
   }
 
   @Test
-  public void testWithoutCaching() throws Exception {
+  public void testWithoutCaching() throws IOException {
     Path path = Paths.get(URI.create("gs://bucket/path"));
     Files.write(path, "(✿◕ ‿◕ )ノ".getBytes(UTF_8), withoutCaching());
     assertThat(Files.readAttributes(path, CloudStorageFileAttributes.class).cacheControl().get())
@@ -44,7 +45,7 @@ public void testWithoutCaching() throws Exception {
   }
 
   @Test
-  public void testCacheControl() throws Exception {
+  public void testCacheControl() throws IOException {
     Path path = Paths.get(URI.create("gs://bucket/path"));
     Files.write(path, "(✿◕ ‿◕ )ノ".getBytes(UTF_8), withCacheControl("potato"));
     assertThat(Files.readAttributes(path, CloudStorageFileAttributes.class).cacheControl().get())
@@ -52,7 +53,7 @@ public void testCacheControl() throws Exception {
   }
 
   @Test
-  public void testWithAcl() throws Exception {
+  public void testWithAcl() throws IOException {
     Path path = Paths.get(URI.create("gs://bucket/path"));
     Acl acl = Acl.of(new Acl.User("king@example.com"), Acl.Role.OWNER);
     Files.write(path, "(✿◕ ‿◕ )ノ".getBytes(UTF_8), withAcl(acl));
@@ -61,7 +62,7 @@ public void testWithAcl() throws Exception {
   }
 
   @Test
-  public void testWithContentDisposition() throws Exception {
+  public void testWithContentDisposition() throws IOException {
     Path path = Paths.get(URI.create("gs://bucket/path"));
     Files.write(path, "(✿◕ ‿◕ )ノ".getBytes(UTF_8), withContentDisposition("bubbly fun"));
     assertThat(
@@ -70,7 +71,7 @@ public void testWithContentDisposition() throws Exception {
   }
 
   @Test
-  public void testWithContentEncoding() throws Exception {
+  public void testWithContentEncoding() throws IOException {
     Path path = Paths.get(URI.create("gs://bucket/path"));
     Files.write(path, "(✿◕ ‿◕ )ノ".getBytes(UTF_8), withContentEncoding("gzip"));
     assertThat(Files.readAttributes(path, CloudStorageFileAttributes.class).contentEncoding().get())
@@ -78,7 +79,7 @@ public void testWithContentEncoding() throws Exception {
   }
 
   @Test
-  public void testWithUserMetadata() throws Exception {
+  public void testWithUserMetadata() throws IOException {
     Path path = Paths.get(URI.create("gs://bucket/path"));
     Files.write(
         path,
@@ -96,7 +97,7 @@ public void testWithUserMetadata() throws Exception {
   }
 
   @Test
-  public void testWithMimeType_string() throws Exception {
+  public void testWithMimeType_string() throws IOException {
     Path path = Paths.get(URI.create("gs://bucket/path"));
     Files.write(path, "(✿◕ ‿◕ )ノ".getBytes(UTF_8), withMimeType("text/plain"));
     assertThat(Files.readAttributes(path, CloudStorageFileAttributes.class).mimeType().get())
@@ -104,7 +105,7 @@ public void testWithMimeType_string() throws Exception {
   }
 
   @Test
-  public void testNullness() throws Exception {
+  public void testNullness() {
     NullPointerTester tester = new NullPointerTester();
     tester.testAllPublicStaticMethods(CloudStorageOptions.class);
   }
diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStoragePathTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStoragePathTest.java
index 0235bcadec87..423a2f35d621 100644
--- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStoragePathTest.java
+++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStoragePathTest.java
@@ -15,11 +15,11 @@
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
+import java.io.IOException;
 import java.net.URI;
 import java.nio.file.FileSystem;
 import java.nio.file.FileSystems;
 import java.nio.file.Path;
-import java.nio.file.Paths;
 import java.nio.file.ProviderMismatchException;
 
 /**
@@ -269,7 +269,6 @@ public void testNormalize_preserveTrailingSlash() {
   }
 
   @Test
-  @SuppressWarnings("null")
   public void testGetParent_preserveTrailingSlash() {
     try (CloudStorageFileSystem fs = forBucket("doodle")) {
       assertThat(fs.getPath("a/b/c").getParent().toString()).isEqualTo("a/b/");
@@ -282,7 +281,6 @@ public void testGetParent_preserveTrailingSlash() {
   }
 
   @Test
-  @SuppressWarnings("null")
   public void testGetRoot() {
     try (CloudStorageFileSystem fs = forBucket("doodle")) {
       assertThat(fs.getPath("/hello").getRoot().toString()).isEqualTo("/");
@@ -301,18 +299,18 @@ public void testRelativize() {
 
   @Test
   public void testRelativize_providerMismatch() {
-    try (CloudStorageFileSystem fs = forBucket("doodle")) {
+    try (CloudStorageFileSystem gcs = forBucket("doodle")) {
       thrown.expect(ProviderMismatchException.class);
-      fs.getPath("/etc").relativize(Paths.get("/dog"));
+      gcs.getPath("/etc").relativize(FileSystems.getDefault().getPath("/dog"));
     }
   }
 
   @Test
   @SuppressWarnings("ReturnValueIgnored") // testing that an Exception is thrown
   public void testRelativize_providerMismatch2() {
-    try (CloudStorageFileSystem fs = forBucket("doodle")) {
+    try (CloudStorageFileSystem gcs = forBucket("doodle")) {
       thrown.expect(ProviderMismatchException.class);
-      Paths.get("/dog").relativize(fs.getPath("/etc"));
+      gcs.getPath("/dog").relativize(FileSystems.getDefault().getPath("/etc"));
     }
   }
 
@@ -326,9 +324,9 @@ public void testResolve() {
 
   @Test
   public void testResolve_providerMismatch() {
-    try (CloudStorageFileSystem fs = forBucket("doodle")) {
+    try (CloudStorageFileSystem gcs = forBucket("doodle")) {
       thrown.expect(ProviderMismatchException.class);
-      fs.getPath("etc").resolve(Paths.get("/dog"));
+      gcs.getPath("etc").resolve(FileSystems.getDefault().getPath("/dog"));
     }
   }
 
@@ -356,7 +354,6 @@ public void testToAbsolutePath_withWorkingDirectory() {
   }
 
   @Test
-  @SuppressWarnings("null")
   public void testGetFileName() {
     try (CloudStorageFileSystem fs = forBucket("doodle")) {
       assertThat(fs.getPath("/hi/there").getFileName().toString()).isEqualTo("there");
@@ -402,10 +399,9 @@ public void testEndsWith() {
     }
   }
 
-  /** @see "http://stackoverflow.com/a/10068306".
-   */
   @Test
-  public void testResolve_willWorkWithRecursiveCopy() throws Exception {
+  public void testResolve_willWorkWithRecursiveCopy() throws IOException {
+    // See: http://stackoverflow.com/a/10068306
     try (FileSystem fsSource = FileSystems.getFileSystem(URI.create("gs://hello"));
         FileSystem fsTarget = FileSystems.getFileSystem(URI.create("gs://cat"))) {
       Path targetPath = fsTarget.getPath("/some/folder/");
@@ -415,10 +411,9 @@ public void testResolve_willWorkWithRecursiveCopy() throws Exception {
     }
   }
 
-  /** @see "http://stackoverflow.com/a/10068306".
-   */
   @Test
-  public void testRelativize_willWorkWithRecursiveCopy() throws Exception {
+  public void testRelativize_willWorkWithRecursiveCopy() throws IOException {
+    // See: http://stackoverflow.com/a/10068306
     try (FileSystem fsSource = FileSystems.getFileSystem(URI.create("gs://hello"));
         FileSystem fsTarget = FileSystems.getFileSystem(URI.create("gs://cat"))) {
       Path targetPath = fsTarget.getPath("/some/folder/");
@@ -465,9 +460,11 @@ public void testEquals_currentDirectoryIsTakenIntoConsideration() {
   }
 
   @Test
-  public void testNullness() {
+  public void testNullness() throws NoSuchMethodException, SecurityException {
     try (CloudStorageFileSystem fs = forBucket("doodle")) {
-      NullPointerTester tester = new NullPointerTester().setDefault(Path.class, fs.getPath("sup"));
+      NullPointerTester tester = new NullPointerTester();
+      tester.ignore(CloudStoragePath.class.getMethod("equals", Object.class));
+      tester.setDefault(Path.class, fs.getPath("sup"));
       tester.testAllPublicStaticMethods(CloudStoragePath.class);
       tester.testAllPublicInstanceMethods(fs.getPath("sup"));
     }
diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageWriteChannelTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageWriteChannelTest.java
index 2f529940450f..9c4a7ade6196 100644
--- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageWriteChannelTest.java
+++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageWriteChannelTest.java
@@ -19,6 +19,7 @@
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
+import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.nio.channels.ClosedChannelException;
 import java.nio.channels.NonReadableChannelException;
@@ -40,13 +41,13 @@ public void before() {
   }
 
   @Test
-  public void testRead_throwsNonReadableChannelException() throws Exception {
+  public void testRead_throwsNonReadableChannelException() throws IOException {
     thrown.expect(NonReadableChannelException.class);
     chan.read(ByteBuffer.allocate(1));
   }
 
   @Test
-  public void testWrite() throws Exception {
+  public void testWrite() throws IOException {
     ByteBuffer buffer = ByteBuffer.allocate(1);
     buffer.put((byte) 'B');
     assertThat(chan.position()).isEqualTo(0L);
@@ -61,14 +62,14 @@ public void testWrite() throws Exception {
   }
 
   @Test
-  public void testWrite_whenClosed_throwsCce() throws Exception {
+  public void testWrite_whenClosed_throwsCce() throws IOException {
     when(gcsChannel.isOpen()).thenReturn(false);
     thrown.expect(ClosedChannelException.class);
     chan.write(ByteBuffer.allocate(1));
   }
 
   @Test
-  public void testIsOpen() throws Exception {
+  public void testIsOpen() throws IOException {
     when(gcsChannel.isOpen()).thenReturn(true).thenReturn(false);
     assertThat(chan.isOpen()).isTrue();
     chan.close();
@@ -79,28 +80,28 @@ public void testIsOpen() throws Exception {
   }
 
   @Test
-  public void testSize() throws Exception {
+  public void testSize() throws IOException {
     assertThat(chan.size()).isEqualTo(0L);
     verify(gcsChannel).isOpen();
     verifyZeroInteractions(gcsChannel);
   }
 
   @Test
-  public void testSize_whenClosed_throwsCce() throws Exception {
+  public void testSize_whenClosed_throwsCce() throws IOException {
     when(gcsChannel.isOpen()).thenReturn(false);
     thrown.expect(ClosedChannelException.class);
     chan.size();
   }
 
   @Test
-  public void testPosition_whenClosed_throwsCce() throws Exception {
+  public void testPosition_whenClosed_throwsCce() throws IOException {
     when(gcsChannel.isOpen()).thenReturn(false);
     thrown.expect(ClosedChannelException.class);
     chan.position();
   }
 
   @Test
-  public void testClose_calledMultipleTimes_doesntThrowAnError() throws Exception {
+  public void testClose_calledMultipleTimes_doesntThrowAnError() throws IOException {
     chan.close();
     chan.close();
     chan.close();
diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/UnixPathTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/UnixPathTest.java
index 9a83105d1550..66ada17e44fe 100644
--- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/UnixPathTest.java
+++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/UnixPathTest.java
@@ -360,7 +360,7 @@ public void testSeemsLikeADirectory() {
   }
 
   @Test
-  public void testEquals_equalsTester() throws Exception {
+  public void testEquals_equalsTester() {
     new EqualsTester()
         .addEqualityGroup(p("/lol"), p("/lol"))
         .addEqualityGroup(p("/lol//"), p("/lol//"))
@@ -369,8 +369,9 @@ public void testEquals_equalsTester() throws Exception {
   }
 
   @Test
-  public void testNullness() {
+  public void testNullness() throws Exception {
     NullPointerTester tester = new NullPointerTester();
+    tester.ignore(UnixPath.class.getMethod("equals", Object.class));
     tester.testAllPublicStaticMethods(UnixPath.class);
     tester.testAllPublicInstanceMethods(p("solo"));
   }

From b5733fb2cff05daa6899c452af607e20fe8efa8a Mon Sep 17 00:00:00 2001
From: JP Martin 
Date: Fri, 11 Mar 2016 15:44:36 -0800
Subject: [PATCH 05/38] Add FileSystemProvider, tests now pass

---
 .../META-INF/services/java.nio.file.spi.FileSystemProvider       | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 gcloud-java-contrib/gcloud-java-nio/src/main/resources/META-INF/services/java.nio.file.spi.FileSystemProvider

diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/resources/META-INF/services/java.nio.file.spi.FileSystemProvider b/gcloud-java-contrib/gcloud-java-nio/src/main/resources/META-INF/services/java.nio.file.spi.FileSystemProvider
new file mode 100644
index 000000000000..8c1c48ce55a0
--- /dev/null
+++ b/gcloud-java-contrib/gcloud-java-nio/src/main/resources/META-INF/services/java.nio.file.spi.FileSystemProvider
@@ -0,0 +1 @@
+com.google.gcloud.storage.contrib.nio.CloudStorageFileSystemProvider
\ No newline at end of file

From 9b0da031a63840955a7d6e284b7671beb002fc5a Mon Sep 17 00:00:00 2001
From: JP Martin 
Date: Thu, 17 Mar 2016 15:48:55 -0700
Subject: [PATCH 06/38] Remove spi.FileSystemProvider

---
 .../META-INF/services/java.nio.file.spi.FileSystemProvider       | 1 -
 1 file changed, 1 deletion(-)
 delete mode 100644 gcloud-java-contrib/gcloud-java-nio/src/main/resources/META-INF/services/java.nio.file.spi.FileSystemProvider

diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/resources/META-INF/services/java.nio.file.spi.FileSystemProvider b/gcloud-java-contrib/gcloud-java-nio/src/main/resources/META-INF/services/java.nio.file.spi.FileSystemProvider
deleted file mode 100644
index 8c1c48ce55a0..000000000000
--- a/gcloud-java-contrib/gcloud-java-nio/src/main/resources/META-INF/services/java.nio.file.spi.FileSystemProvider
+++ /dev/null
@@ -1 +0,0 @@
-com.google.gcloud.storage.contrib.nio.CloudStorageFileSystemProvider
\ No newline at end of file

From 7ca9fcb7261741f156c49a911c97654220678c4b Mon Sep 17 00:00:00 2001
From: Justine Tunney 
Date: Tue, 22 Mar 2016 15:04:57 -0400
Subject: [PATCH 07/38] Add license headers

---
 .../contrib/nio/CloudStorageConfiguration.java   | 16 ++++++++++++++++
 .../nio/CloudStorageFileAttributeView.java       | 16 ++++++++++++++++
 .../contrib/nio/CloudStorageFileAttributes.java  | 16 ++++++++++++++++
 .../contrib/nio/CloudStorageFileSystem.java      | 16 ++++++++++++++++
 .../nio/CloudStorageFileSystemProvider.java      | 16 ++++++++++++++++
 .../nio/CloudStorageObjectAttributes.java        | 16 ++++++++++++++++
 .../CloudStorageObjectImmutableException.java    | 16 ++++++++++++++++
 .../storage/contrib/nio/CloudStorageOption.java  | 16 ++++++++++++++++
 .../storage/contrib/nio/CloudStorageOptions.java | 16 ++++++++++++++++
 .../storage/contrib/nio/CloudStoragePath.java    | 16 ++++++++++++++++
 .../CloudStoragePseudoDirectoryAttributes.java   | 16 ++++++++++++++++
 .../CloudStoragePseudoDirectoryException.java    | 16 ++++++++++++++++
 .../contrib/nio/CloudStorageReadChannel.java     | 16 ++++++++++++++++
 .../storage/contrib/nio/CloudStorageUtil.java    | 16 ++++++++++++++++
 .../contrib/nio/CloudStorageWriteChannel.java    | 16 ++++++++++++++++
 .../cloud/storage/contrib/nio/OptionAcl.java     | 16 ++++++++++++++++
 .../storage/contrib/nio/OptionBlockSize.java     | 16 ++++++++++++++++
 .../storage/contrib/nio/OptionCacheControl.java  | 16 ++++++++++++++++
 .../contrib/nio/OptionContentDisposition.java    | 16 ++++++++++++++++
 .../contrib/nio/OptionContentEncoding.java       | 16 ++++++++++++++++
 .../storage/contrib/nio/OptionMimeType.java      | 16 ++++++++++++++++
 .../storage/contrib/nio/OptionUserMetadata.java  | 16 ++++++++++++++++
 .../cloud/storage/contrib/nio/UnixPath.java      | 16 ++++++++++++++++
 .../cloud/storage/contrib/nio/package-info.java  | 16 ++++++++++++++++
 .../nio/CloudStorageConfigurationTest.java       | 16 ++++++++++++++++
 .../nio/CloudStorageFileAttributeViewTest.java   | 16 ++++++++++++++++
 .../nio/CloudStorageFileAttributesTest.java      | 16 ++++++++++++++++
 .../nio/CloudStorageFileSystemProviderTest.java  | 16 ++++++++++++++++
 .../contrib/nio/CloudStorageFileSystemTest.java  | 16 ++++++++++++++++
 .../contrib/nio/CloudStorageOptionsTest.java     | 16 ++++++++++++++++
 .../contrib/nio/CloudStoragePathTest.java        | 16 ++++++++++++++++
 .../nio/CloudStorageWriteChannelTest.java        | 16 ++++++++++++++++
 .../cloud/storage/contrib/nio/UnixPathTest.java  | 16 ++++++++++++++++
 .../cloud/storage/testing/FakeStorageRpc.java    | 16 ++++++++++++++++
 34 files changed, 544 insertions(+)

diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageConfiguration.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageConfiguration.java
index 26c6b1063812..b5166c143f62 100644
--- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageConfiguration.java
+++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageConfiguration.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * 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 com.google.cloud.storage.contrib.nio;
 
 import static com.google.common.base.Preconditions.checkArgument;
diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributeView.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributeView.java
index baec035af0a2..11b92116b4c3 100644
--- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributeView.java
+++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributeView.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * 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 com.google.cloud.storage.contrib.nio;
 
 import static com.google.common.base.Preconditions.checkNotNull;
diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributes.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributes.java
index 49d4169d6228..ff77f0b96ab6 100644
--- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributes.java
+++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributes.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * 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 com.google.cloud.storage.contrib.nio;
 
 import com.google.cloud.storage.Acl;
diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystem.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystem.java
index e0143f099ef4..1dcf61666f30 100644
--- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystem.java
+++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystem.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * 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 com.google.cloud.storage.contrib.nio;
 
 import static com.google.common.base.Preconditions.checkArgument;
diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProvider.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProvider.java
index 3a90f20d48e2..0011d5ea3521 100644
--- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProvider.java
+++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProvider.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * 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 com.google.cloud.storage.contrib.nio;
 
 import static com.google.cloud.storage.contrib.nio.CloudStorageUtil.checkBucket;
diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageObjectAttributes.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageObjectAttributes.java
index 19e6cb96ab4f..56f02749c727 100644
--- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageObjectAttributes.java
+++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageObjectAttributes.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * 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 com.google.cloud.storage.contrib.nio;
 
 import static com.google.common.base.Preconditions.checkNotNull;
diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageObjectImmutableException.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageObjectImmutableException.java
index 1e4e348f4036..523e5dae7ed4 100644
--- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageObjectImmutableException.java
+++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageObjectImmutableException.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * 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 com.google.cloud.storage.contrib.nio;
 
 /**
diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageOption.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageOption.java
index 4e65069c8cbc..266bd67c2c84 100644
--- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageOption.java
+++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageOption.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * 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 com.google.cloud.storage.contrib.nio;
 
 import java.nio.file.CopyOption;
diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageOptions.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageOptions.java
index ec1421f06b6a..6ee762eff0ea 100644
--- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageOptions.java
+++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageOptions.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * 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 com.google.cloud.storage.contrib.nio;
 
 import com.google.cloud.storage.Acl;
diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStoragePath.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStoragePath.java
index d31a3e0af1a1..b44984e2be6a 100644
--- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStoragePath.java
+++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStoragePath.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * 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 com.google.cloud.storage.contrib.nio;
 
 import static com.google.cloud.storage.contrib.nio.CloudStorageUtil.checkNotNullArray;
diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStoragePseudoDirectoryAttributes.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStoragePseudoDirectoryAttributes.java
index de94d27588c1..b034eab0e8a1 100644
--- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStoragePseudoDirectoryAttributes.java
+++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStoragePseudoDirectoryAttributes.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * 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 com.google.cloud.storage.contrib.nio;
 
 import static com.google.cloud.storage.contrib.nio.CloudStorageFileSystem.FILE_TIME_UNKNOWN;
diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStoragePseudoDirectoryException.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStoragePseudoDirectoryException.java
index dfad2ed1f503..5a63bc790c6c 100644
--- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStoragePseudoDirectoryException.java
+++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStoragePseudoDirectoryException.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * 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 com.google.cloud.storage.contrib.nio;
 
 import java.nio.file.InvalidPathException;
diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageReadChannel.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageReadChannel.java
index 023ef04175f8..5b5e2b649e0d 100644
--- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageReadChannel.java
+++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageReadChannel.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * 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 com.google.cloud.storage.contrib.nio;
 
 import static com.google.common.base.Preconditions.checkArgument;
diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageUtil.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageUtil.java
index e86bad9f9946..78a602e407b3 100644
--- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageUtil.java
+++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageUtil.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * 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 com.google.cloud.storage.contrib.nio;
 
 import static com.google.common.base.Preconditions.checkArgument;
diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageWriteChannel.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageWriteChannel.java
index 7eb9f7b612a1..e3aad30b3579 100644
--- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageWriteChannel.java
+++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageWriteChannel.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * 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 com.google.cloud.storage.contrib.nio;
 
 import com.google.cloud.WriteChannel;
diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/OptionAcl.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/OptionAcl.java
index 74c58db70b19..ea9b4b0a62a6 100644
--- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/OptionAcl.java
+++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/OptionAcl.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * 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 com.google.cloud.storage.contrib.nio;
 
 import com.google.auto.value.AutoValue;
diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/OptionBlockSize.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/OptionBlockSize.java
index 0616f407429b..61d3ba6e7d8e 100644
--- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/OptionBlockSize.java
+++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/OptionBlockSize.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * 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 com.google.cloud.storage.contrib.nio;
 
 import com.google.auto.value.AutoValue;
diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/OptionCacheControl.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/OptionCacheControl.java
index 2c4bb7b8b8aa..25b18ac96162 100644
--- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/OptionCacheControl.java
+++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/OptionCacheControl.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * 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 com.google.cloud.storage.contrib.nio;
 
 import com.google.auto.value.AutoValue;
diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/OptionContentDisposition.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/OptionContentDisposition.java
index 5a3b7c8cd4bf..8df320d687d7 100644
--- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/OptionContentDisposition.java
+++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/OptionContentDisposition.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * 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 com.google.cloud.storage.contrib.nio;
 
 import com.google.auto.value.AutoValue;
diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/OptionContentEncoding.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/OptionContentEncoding.java
index 4beee9fef349..8d599fb11d52 100644
--- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/OptionContentEncoding.java
+++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/OptionContentEncoding.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * 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 com.google.cloud.storage.contrib.nio;
 
 import com.google.auto.value.AutoValue;
diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/OptionMimeType.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/OptionMimeType.java
index 62789727714d..759cb00bee5b 100644
--- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/OptionMimeType.java
+++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/OptionMimeType.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * 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 com.google.cloud.storage.contrib.nio;
 
 import com.google.auto.value.AutoValue;
diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/OptionUserMetadata.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/OptionUserMetadata.java
index f99a0abed434..3c45e384c964 100644
--- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/OptionUserMetadata.java
+++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/OptionUserMetadata.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * 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 com.google.cloud.storage.contrib.nio;
 
 import com.google.auto.value.AutoValue;
diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/UnixPath.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/UnixPath.java
index d4949addb9ff..3d8c6ae77299 100644
--- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/UnixPath.java
+++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/UnixPath.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * 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 com.google.cloud.storage.contrib.nio;
 
 import static com.google.common.base.Preconditions.checkArgument;
diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/package-info.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/package-info.java
index c956c122e121..bee99002b82f 100644
--- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/package-info.java
+++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/package-info.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * 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.
+ */
+
 /**
  * Java 7 nio FileSystem client library for Google Cloud Storage.
  *
diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageConfigurationTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageConfigurationTest.java
index 9b887ffff833..a2f155d2fd2c 100644
--- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageConfigurationTest.java
+++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageConfigurationTest.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * 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 com.google.cloud.storage.contrib.nio;
 
 import static com.google.common.truth.Truth.assertThat;
diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributeViewTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributeViewTest.java
index f0e082dca04f..e85da0446c6b 100644
--- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributeViewTest.java
+++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributeViewTest.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * 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 com.google.cloud.storage.contrib.nio;
 
 import static com.google.common.truth.Truth.assertThat;
diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributesTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributesTest.java
index d5bbcdf7dbb8..2d8be2c2dc2d 100644
--- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributesTest.java
+++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributesTest.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * 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 com.google.cloud.storage.contrib.nio;
 
 import static com.google.cloud.storage.contrib.nio.CloudStorageOptions.withAcl;
diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProviderTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProviderTest.java
index 67b8f6b89263..9340070c9bf8 100644
--- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProviderTest.java
+++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProviderTest.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * 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 com.google.cloud.storage.contrib.nio;
 
 import static com.google.cloud.storage.contrib.nio.CloudStorageFileSystem.forBucket;
diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemTest.java
index 4b8a17df785b..579cc6bc1054 100644
--- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemTest.java
+++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemTest.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * 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 com.google.cloud.storage.contrib.nio;
 
 import static com.google.common.truth.Truth.assertThat;
diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageOptionsTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageOptionsTest.java
index a6f47a3dc734..7d14193066aa 100644
--- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageOptionsTest.java
+++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageOptionsTest.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * 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 com.google.cloud.storage.contrib.nio;
 
 import static com.google.cloud.storage.contrib.nio.CloudStorageOptions.withAcl;
diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStoragePathTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStoragePathTest.java
index 423a2f35d621..62fc011f7b18 100644
--- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStoragePathTest.java
+++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStoragePathTest.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * 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 com.google.cloud.storage.contrib.nio;
 
 import static com.google.cloud.storage.contrib.nio.CloudStorageFileSystem.forBucket;
diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageWriteChannelTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageWriteChannelTest.java
index 9c4a7ade6196..f652812a13d6 100644
--- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageWriteChannelTest.java
+++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageWriteChannelTest.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * 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 com.google.cloud.storage.contrib.nio;
 
 import static com.google.common.truth.Truth.assertThat;
diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/UnixPathTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/UnixPathTest.java
index 66ada17e44fe..7bdf5b9cc248 100644
--- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/UnixPathTest.java
+++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/UnixPathTest.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * 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 com.google.cloud.storage.contrib.nio;
 
 import static com.google.common.truth.Truth.assertThat;
diff --git a/gcloud-java-storage/src/main/java/com/google/cloud/storage/testing/FakeStorageRpc.java b/gcloud-java-storage/src/main/java/com/google/cloud/storage/testing/FakeStorageRpc.java
index 3e3c0d11eeec..5ef212b10715 100644
--- a/gcloud-java-storage/src/main/java/com/google/cloud/storage/testing/FakeStorageRpc.java
+++ b/gcloud-java-storage/src/main/java/com/google/cloud/storage/testing/FakeStorageRpc.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file exctain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unept in compliance with the License.
+ * You may obless 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 com.google.cloud.storage.testing;
 
 import com.google.api.services.storage.model.Bucket;

From d5cf00629c7e448d2a02847012e887b10970bdfb Mon Sep 17 00:00:00 2001
From: JP Martin 
Date: Tue, 5 Apr 2016 15:07:13 -0700
Subject: [PATCH 08/38] Split off the "normal case" example from the "legacy
 case" one.

* Split off the "normal case" example from the "legacy case" one.

(and create a "normal case" example, first)

* Correct damage from "helpful" IDE renaming help.

* Switch gcloud-java-examples to appassembler

This has the following benefits:

 * Stat example now works
 * Easier to see what is available (list the bin folder)
 * Easier to see what running an example entails (read the script)
 * Simpler command line (no more -Dexec.args)

* Rename example to examples and switch a Stat to ListFilesystems

* Clean up pom

Fix indent to 2, remove redundant groupId line that's inherited from the
parent.

* Reviewer comments and update_docs_version.sh

* Added blank line

* Sweating the small stuff

Adding a \n at the end of the last line.
---
 gcloud-java-contrib/README.md                 | 11 +++
 .../gcloud-java-nio-examples/README.md        | 41 +++++++++
 .../gcloud-java-nio-examples/pom.xml          | 91 +++++++++++++++++++
 .../gcloud/nio/examples/ListFilesystems.java  | 47 ++++++++++
 gcloud-java-contrib/gcloud-java-nio/pom.xml   | 46 ++++++----
 gcloud-java-contrib/pom.xml                   |  1 +
 gcloud-java-examples/README.md                | 52 +++++++----
 gcloud-java-examples/pom.xml                  | 43 +++++++++
 .../com/google/cloud/examples/nio/Stat.java   |  9 +-
 utilities/update_docs_version.sh              |  3 +
 10 files changed, 307 insertions(+), 37 deletions(-)
 create mode 100644 gcloud-java-contrib/gcloud-java-nio-examples/README.md
 create mode 100644 gcloud-java-contrib/gcloud-java-nio-examples/pom.xml
 create mode 100644 gcloud-java-contrib/gcloud-java-nio-examples/src/main/java/com/google/gcloud/nio/examples/ListFilesystems.java

diff --git a/gcloud-java-contrib/README.md b/gcloud-java-contrib/README.md
index 4b93f6fafa9f..4df9f83129c4 100644
--- a/gcloud-java-contrib/README.md
+++ b/gcloud-java-contrib/README.md
@@ -9,6 +9,12 @@ Packages that provide higher-level abstraction/functionality for common gcloud-j
 [![Codacy Badge](https://api.codacy.com/project/badge/grade/9da006ad7c3a4fe1abd142e77c003917)](https://www.codacy.com/app/mziccard/gcloud-java)
 [![Dependency Status](https://www.versioneye.com/user/projects/56bd8ee72a29ed002d2b0969/badge.svg?style=flat)](https://www.versioneye.com/user/projects/56bd8ee72a29ed002d2b0969)
 
+Contents
+--------
+
+ * [gcloud-java-nio](./gcloud-java-nio/): NIO Filesystem Provider for Google Cloud Storage.
+ * [gcloud-java-nio-examples](./gcloud-java-nio-examples/): How to add GCS NIO after the fact.
+
 Quickstart
 ----------
 If you are using Maven, add this to your pom.xml file
@@ -42,6 +48,11 @@ It is currently in major version zero (``0.y.z``), which means that anything
 may change at any time and the public API should not be considered
 stable.
 
+See also
+--------
+
+ * [gcloud-java-examples](../gcloud-java-examples) for an example of how to use NIO normally.
+
 Contributing
 ------------
 
diff --git a/gcloud-java-contrib/gcloud-java-nio-examples/README.md b/gcloud-java-contrib/gcloud-java-nio-examples/README.md
new file mode 100644
index 000000000000..206ca93759e5
--- /dev/null
+++ b/gcloud-java-contrib/gcloud-java-nio-examples/README.md
@@ -0,0 +1,41 @@
+Example of adding the Google Cloud Storage NIO Provider to a legacy jar
+=======================================================================
+
+This project shows how to add GCS capabilities to a jar file for a Java 7 application
+that uses Java NIO without the need to recompile.
+
+Note that whenever possible, you instead want to recompile the app and use the normal
+dependency mechanism to add a dependency to gcloud-java-nio. You can see examples of
+this in the [gcloud-java-examples](../../gcloud-java-examples) project.
+
+To run this example:
+
+1. Before running the example, go to the [Google Developers Console][developers-console] to ensure that Google Cloud Storage API is enabled.
+
+2. Log in using gcloud SDK (`gcloud auth login` in command line)
+
+3. Compile the JAR with:
+    ```
+    mvn package -DskipTests -Dmaven.javadoc.skip=true  -Dmaven.source.skip=true
+    ```
+
+4.    Run the sample with:
+
+    ```
+    java -cp gcloud-java-contrib/gcloud-java-nio/target/gcloud-java-nio-0.1.6-SNAPSHOT-shaded.jar:gcloud-java-contrib/gcloud-java-nio-examples/target/gcloud-java-nio-examples-0.1.6-SNAPSHOT.jar com.google.gcloud.nio.examples.ListFilesystems
+    ```
+
+    Notice that it lists gcs, which it wouldn't if you ran it without the nio jar:
+    ```
+    java -cp gcloud-java-contrib/gcloud-java-nio-examples/target/gcloud-java-nio-examples-0.1.6-SNAPSHOT.jar com.google.gcloud.nio.examples.ListFilesystems
+    ```
+
+The sample doesn't have anything about GCS in it. It gets that ability from the nio jar that
+we're adding to the classpath. You can use the nio "fat shaded" jar for this purpose as it also
+includes the dependencies for gcloud-java-nio.
+The underlying mechanism is Java's standard [ServiceLoader](https://docs.oracle.com/javase/7/docs/api/java/util/ServiceLoader.html)
+facility, the [standard way](http://docs.oracle.com/javase/7/docs/technotes/guides/io/fsp/filesystemprovider.html) to plug in NIO providers like this one.
+
+If you have access to a project's source code you can also simply add gcloud-java-nio as
+a dependency and let Maven pull in the required dependencies (this is what the nio unit tests do).
+This approach is preferable as the fat jar approach may waste memory on multiple copies of dependencies.
diff --git a/gcloud-java-contrib/gcloud-java-nio-examples/pom.xml b/gcloud-java-contrib/gcloud-java-nio-examples/pom.xml
new file mode 100644
index 000000000000..1ae7a6c1e190
--- /dev/null
+++ b/gcloud-java-contrib/gcloud-java-nio-examples/pom.xml
@@ -0,0 +1,91 @@
+
+
+  4.0.0
+  gcloud-java-nio-examples
+  jar
+  GCloud Java NIO example
+  
+    Demonstrates how to use the gcloud-java-nio jar to add GCS functionality to legacy code.
+  
+  
+    com.google.gcloud
+    gcloud-java-contrib
+    0.1.6-SNAPSHOT
+  
+  
+    nio
+  
+  
+    
+      ${project.groupId}
+      gcloud-java-storage
+      ${project.version}
+    
+    
+      com.google.guava
+      guava
+      19.0
+    
+    
+      com.google.code.findbugs
+      jsr305
+      2.0.1
+    
+    
+      javax.inject
+      javax.inject
+      1
+    
+    
+      com.google.auto.service
+      auto-service
+      1.0-rc2
+      provided 
+    
+    
+      com.google.auto.value
+      auto-value
+      1.1
+      provided 
+    
+    
+      junit
+      junit
+      4.12
+      test
+    
+    
+      com.google.guava
+      guava-testlib
+      19.0
+      test
+    
+    
+      com.google.truth
+      truth
+      0.27
+      test
+    
+    
+      org.mockito
+      mockito-core
+      1.9.5
+    
+    
+      org.apache.maven.plugins
+      maven-assembly-plugin
+      2.5.4
+    
+  
+  
+    
+      
+        org.codehaus.mojo
+        exec-maven-plugin
+        
+          false
+        
+      
+    
+  
+
diff --git a/gcloud-java-contrib/gcloud-java-nio-examples/src/main/java/com/google/gcloud/nio/examples/ListFilesystems.java b/gcloud-java-contrib/gcloud-java-nio-examples/src/main/java/com/google/gcloud/nio/examples/ListFilesystems.java
new file mode 100644
index 000000000000..5e92768d1cf6
--- /dev/null
+++ b/gcloud-java-contrib/gcloud-java-nio-examples/src/main/java/com/google/gcloud/nio/examples/ListFilesystems.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * 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 com.google.gcloud.nio.examples;
+
+import java.io.IOException;
+import java.net.URI;
+import java.nio.file.FileSystem;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.spi.FileSystemProvider;
+
+/**
+ * ListFilesystems is a super-simple program that lists the available NIO filesystems.
+ */
+public class ListFilesystems {
+
+  /**
+   * See the class documentation.
+   */
+  public static void main(String[] args) throws IOException {
+    listFilesystems();
+  }
+
+  private static void listFilesystems() {
+    System.out.println("Installed filesystem providers:");
+    for (FileSystemProvider p : FileSystemProvider.installedProviders()) {
+      System.out.println("  " + p.getScheme());
+    }
+  }
+
+}
diff --git a/gcloud-java-contrib/gcloud-java-nio/pom.xml b/gcloud-java-contrib/gcloud-java-nio/pom.xml
index d8940a720418..5eeda451dc50 100644
--- a/gcloud-java-contrib/gcloud-java-nio/pom.xml
+++ b/gcloud-java-contrib/gcloud-java-nio/pom.xml
@@ -91,25 +91,39 @@
           -Xlint:unchecked
         
       
+      
       
-        maven-jar-plugin
-        2.6
+        org.apache.maven.plugins
+        maven-shade-plugin
+        2.4.3
         
-          
-            true
-            true
-            
-              true
-              true
-            
-            
-              ${project.artifactId}
-              ${project.groupId}
-              ${project.version}
-              ${buildNumber}
-            
-          
+          true
+          
+            
+              com
+              shaded.gcloud-nio.com
+              
+                com.google.gcloud.**
+              
+            
+            
+              org
+              shaded.gcloud-nio.org
+            
+            
+              google
+              shaded.gcloud-nio.google
+            
+          
         
+        
+          
+            package
+            
+              shade
+            
+          
+        
       
     
   
diff --git a/gcloud-java-contrib/pom.xml b/gcloud-java-contrib/pom.xml
index e24787969ace..37ddb20e708d 100644
--- a/gcloud-java-contrib/pom.xml
+++ b/gcloud-java-contrib/pom.xml
@@ -18,6 +18,7 @@
   
   
     gcloud-java-nio
+    gcloud-java-nio-examples
   
   
     
diff --git a/gcloud-java-examples/README.md b/gcloud-java-examples/README.md
index 75947a6dcca3..6a2903eb2e9b 100644
--- a/gcloud-java-examples/README.md
+++ b/gcloud-java-examples/README.md
@@ -37,9 +37,9 @@ To run examples from your command line:
 
 2. Set your current project using `gcloud config set project PROJECT_ID`. This step is not necessary for `ResourceManagerExample`.
 
-3. Compile using Maven (`mvn compile` in command line from your base project directory)
+3. Compile using Maven: `cd gcloud-java-examples` in command line from your base project directory and then `mvn package appassembler:assemble -DskipTests -Dmaven.javadoc.skip=true  -Dmaven.source.skip=true`.
 
-4. Run an example using Maven from command line.
+4. Run an example from the command line using the Maven-generated scripts.
 
   * Here's an example run of `BigQueryExample`.
 
@@ -55,11 +55,11 @@ To run examples from your command line:
     ```
     Then you are ready to run the following example:
     ```
-    mvn exec:java -Dexec.mainClass="com.google.cloud.examples.bigquery.BigQueryExample" -Dexec.args="create dataset new_dataset_id"
-    mvn exec:java -Dexec.mainClass="com.google.cloud.examples.bigquery.BigQueryExample" -Dexec.args="create table new_dataset_id new_table_id field_name:string"
-    mvn exec:java -Dexec.mainClass="com.google.cloud.examples.bigquery.BigQueryExample" -Dexec.args="list tables new_dataset_id"
-    mvn exec:java -Dexec.mainClass="com.google.cloud.examples.bigquery.BigQueryExample" -Dexec.args="load new_dataset_id new_table_id CSV gs://my_bucket/my_csv_file"
-    mvn exec:java -Dexec.mainClass="com.google.cloud.examples.bigquery.BigQueryExample" -Dexec.args="query 'select * from new_dataset_id.new_table_id'"
+    target/appassembler/bin/BigQueryExample create dataset new_dataset_id
+    target/appassembler/bin/BigQueryExample create table new_dataset_id new_table_id field_name:string
+    target/appassembler/bin/BigQueryExample list tables new_dataset_id
+    target/appassembler/bin/BigQueryExample load new_dataset_id new_table_id CSV gs://my_bucket/my_csv_file
+    target/appassembler/bin/BigQueryExample query 'select * from new_dataset_id.new_table_id'
     ```
 
   * Here's an example run of `ComputeExample`.
@@ -78,10 +78,9 @@ To run examples from your command line:
 
     Be sure to change the placeholder project ID "your-project-id" with your own project ID. Also note that you have to enable the Google Cloud Datastore API on the [Google Developers Console][developers-console] before running the following commands.
     ```
-    mvn exec:java -Dexec.mainClass="com.google.cloud.examples.datastore.DatastoreExample" -Dexec.args="your-project-id my_name add my\ comment"
-    mvn exec:java -Dexec.mainClass="com.google.cloud.examples.datastore.DatastoreExample" -Dexec.args="your-project-id my_name display"
-    mvn exec:java -Dexec.mainClass="com.google.cloud.examples.datastore.DatastoreExample" -Dexec.args="your-project-id my_name delete"
-    mvn exec:java -Dexec.mainClass="com.google.cloud.examples.datastore.DatastoreExample" -Dexec.args="your-project-id my_name set myname@mydomain.com 1234"
+    target/appassembler/bin/DatastoreExample your-project-id my_name add my\ comment
+    target/appassembler/bin/DatastoreExample your-project-id my_name display
+    target/appassembler/bin/DatastoreExample your-project-id my_name delete
     ```
 
   * Here's an example run of `DnsExample`.
@@ -114,21 +113,38 @@ To run examples from your command line:
 
     Be sure to change the placeholder project ID "your-project-id" with your own globally unique project ID.
     ```
-    mvn exec:java -Dexec.mainClass="com.google.cloud.examples.resourcemanager.ResourceManagerExample" -Dexec.args="create your-project-id"
-    mvn exec:java -Dexec.mainClass="com.google.cloud.examples.resourcemanager.ResourceManagerExample" -Dexec.args="list"
-    mvn exec:java -Dexec.mainClass="com.google.cloud.examples.resourcemanager.ResourceManagerExample" -Dexec.args="get your-project-id"
+    target/appassembler/bin/ResourceManagerExample create your-project-id
+    target/appassembler/bin/ResourceManagerExample list
+    target/appassembler/bin/ResourceManagerExample get your-project-id
     ```
 
   * Here's an example run of `StorageExample`.
 
     Before running the example, go to the [Google Developers Console][developers-console] to ensure that "Google Cloud Storage" and "Google Cloud Storage JSON API" are enabled and that you have a bucket.  Also ensure that you have a test file (`test.txt` is chosen here) to upload to Cloud Storage stored locally on your machine.
     ```
-    mvn exec:java -Dexec.mainClass="com.google.cloud.examples.storage.StorageExample" -Dexec.args="upload /path/to/test.txt "
-    mvn exec:java -Dexec.mainClass="com.google.cloud.examples.storage.StorageExample" -Dexec.args="list "
-    mvn exec:java -Dexec.mainClass="com.google.cloud.examples.storage.StorageExample" -Dexec.args="download  test.txt"
-    mvn exec:java -Dexec.mainClass="com.google.cloud.examples.storage.StorageExample" -Dexec.args="delete  test.txt"
+    target/appassembler/bin/StorageExample upload /path/to/test.txt 
+    target/appassembler/bin/StorageExample list 
+    target/appassembler/bin/StorageExample download  test.txt
+    target/appassembler/bin/StorageExample delete  test.txt
     ```
 
+ * Here's an example run of `Stat`, illustrating the use of gcloud-java-nio.
+
+    Before running the example, go to the [Google Developers Console][developers-console] to ensure that Google Cloud Storage API is enabled and that you have a bucket with a file in it.
+
+    Run the sample with (from the gcloud-java-examples folder):
+    ```
+    target/appassembler/bin/Stat --check
+
+    ```
+    Or, if you have a file in `gs://mybucket/myfile.txt`, you can run:
+    ```
+    target/appassembler/bin/Stat gs://mybucket/myfile.txt
+    ```
+
+    The sample doesn't have anything special about GCS in it, it just opens files via the NIO API.
+    It lists gcloud-java-nio as a dependency, and that enables it to interpret `gs://` paths.
+
 Troubleshooting
 ---------------
 
diff --git a/gcloud-java-examples/pom.xml b/gcloud-java-examples/pom.xml
index 4b6885d5d057..cc2aa1a8d423 100644
--- a/gcloud-java-examples/pom.xml
+++ b/gcloud-java-examples/pom.xml
@@ -22,6 +22,16 @@
       gcloud-java
       ${project.version}
     
+    
+      ${project.groupId}
+      gcloud-java-nio
+      ${project.version}
+    
+    
+      org.apache.maven.plugins
+      maven-assembly-plugin
+      2.5.4
+    
   
   
     
@@ -33,6 +43,39 @@
           false
         
       
+        
+      
+        org.codehaus.mojo
+        appassembler-maven-plugin
+        
+          
+            
+              com.google.gcloud.examples.bigquery.BigQueryExample
+              BigQueryExample
+            
+            
+              com.google.gcloud.examples.datastore.DatastoreExample
+              DatastoreExample
+            
+            
+              com.google.gcloud.examples.nio.Stat
+              Stat
+            
+            
+              
+                com.google.gcloud.examples.resourcemanager.ResourceManagerExample
+              
+              ResourceManagerExample
+            
+            
+              com.google.gcloud.examples.storage.StorageExample
+              StorageExample
+            
+          
+        
+      
     
   
 
diff --git a/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/Stat.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/Stat.java
index 7a83706124e6..39b4df39e89c 100644
--- a/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/Stat.java
+++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/Stat.java
@@ -20,6 +20,9 @@
  * be logged in (using e.g. the gcloud auth command).
  *
  * 

See the README for a command line to run this example. + * + *

In short, this version (in gcloud-java-examples) is in a package that lists gcloud-java-nio + * as a dependency, so it will work directly without having to do any special work. */ public class Stat { @@ -77,8 +80,8 @@ private static void help() { "", "This tool normally knows nothing of Google Cloud Storage. If you pass it --check", "or a GCS file name (e.g. gs://mybucket/myfile), it will show an error.", - "However, by just adding the gcloud-nio jar in your classpath, this tool is made", - "aware of gs:// paths and can access files on the cloud.", + "However, by just adding the gcloud-nio jar as a dependency and recompiling, this tool is", + "made aware of gs:// paths and can access files on the cloud.", "", "The gcloud NIO filesystem provider can similarly enable existing Java 7 programs", "to read and write cloud files, even if they have no special built-in cloud support." @@ -97,7 +100,7 @@ private static void listFilesystems() { private static void checkGcs() { FileSystem fs = FileSystems.getFileSystem(URI.create("gs://domain-registry-alpha")); - System.out.println("We seem to be able to instantiate a gs:// filesystem."); + System.out.println("Success! We can instantiate a gs:// filesystem."); System.out.println("isOpen: " + fs.isOpen()); System.out.println("isReadOnly: " + fs.isReadOnly()); } diff --git a/utilities/update_docs_version.sh b/utilities/update_docs_version.sh index c9305a157bab..bec6813d1610 100755 --- a/utilities/update_docs_version.sh +++ b/utilities/update_docs_version.sh @@ -21,6 +21,9 @@ if [ "${RELEASED_VERSION##*-}" != "SNAPSHOT" ]; then readmes="$readmes ${item}/README.md" fi done + NEW_VERSION=${RELEASED_VERSION%.*}.$((${RELEASED_VERSION##*.}+1))-SNAPSHOT + echo "Changing version to $NEW_VERSION in gcloud-java-nio-example README" + sed -ri "s/gcloud-java-nio-examples-[0-9]+\.[0-9]+\.[0-9]+-SNAPSHOT/gcloud-java-nio-examples-$NEW_VERSION/g" gcloud-java-contrib/gcloud-java-nio-examples/README.md git add $readmes git config --global user.name "travis-ci" git config --global user.email "travis@travis-ci.org" From 46596d67ec0d67d10b37c92ea9a59653a021d147 Mon Sep 17 00:00:00 2001 From: JP Martin Date: Wed, 6 Apr 2016 11:13:49 -0700 Subject: [PATCH 09/38] Fix javadoc in examples to match the README Fix javadoc in examples to match the README Also use "log in" instead of "login" where a verb is needed instead of a noun. --- gcloud-java-examples/README.md | 2 +- .../cloud/examples/bigquery/BigQueryExample.java | 11 ++++++----- .../cloud/examples/datastore/DatastoreExample.java | 12 ++++-------- .../java/com/google/cloud/examples/nio/Stat.java | 3 ++- .../resourcemanager/ResourceManagerExample.java | 13 +++++++------ .../cloud/examples/storage/StorageExample.java | 11 ++++++----- 6 files changed, 26 insertions(+), 26 deletions(-) diff --git a/gcloud-java-examples/README.md b/gcloud-java-examples/README.md index 6a2903eb2e9b..57a35d7ef9d5 100644 --- a/gcloud-java-examples/README.md +++ b/gcloud-java-examples/README.md @@ -33,7 +33,7 @@ libraryDependencies += "com.google.cloud" % "gcloud-java-examples" % "0.2.4" To run examples from your command line: -1. Login using gcloud SDK (`gcloud auth login` in command line) +1. Log in using gcloud SDK (`gcloud auth login` in command line) 2. Set your current project using `gcloud config set project PROJECT_ID`. This step is not necessary for `ResourceManagerExample`. diff --git a/gcloud-java-examples/src/main/java/com/google/cloud/examples/bigquery/BigQueryExample.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/bigquery/BigQueryExample.java index 3ac1335cca7a..3dc14f13a07c 100644 --- a/gcloud-java-examples/src/main/java/com/google/cloud/examples/bigquery/BigQueryExample.java +++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/bigquery/BigQueryExample.java @@ -60,11 +60,12 @@ * *

Steps needed for running the example: *

    - *
  1. login using gcloud SDK - {@code gcloud auth login}.
  2. - *
  3. compile using maven - {@code mvn compile}
  4. - *
  5. run using maven - - *
    {@code mvn exec:java -Dexec.mainClass="com.google.cloud.examples.bigquery.BigQueryExample"
    - *  -Dexec.args="[]
    + * 
  6. log in using gcloud SDK - {@code gcloud auth login}.
  7. + *
  8. compile using maven - {@code cd gcloud-java-examples; + * mvn package appassembler:assemble -DskipTests -Dmaven.javadoc.skip=true -Dmaven.source.skip=true} + *
  9. + *
  10. run - + *
    {@code target/appassembler/bin/BigQueryExample []
      *  list datasets |
      *  list tables  |
      *  list jobs |
    diff --git a/gcloud-java-examples/src/main/java/com/google/cloud/examples/datastore/DatastoreExample.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/datastore/DatastoreExample.java
    index 4b83f04e7165..15381ab05ba3 100644
    --- a/gcloud-java-examples/src/main/java/com/google/cloud/examples/datastore/DatastoreExample.java
    +++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/datastore/DatastoreExample.java
    @@ -43,15 +43,11 @@
      *
      * 

    Steps needed for running the example:

      *
    1. login using gcloud SDK - {@code gcloud auth login}.
    2. - *
    3. compile using maven - {@code mvn compile}
    4. - *
    5. run using maven - - *
      {@code mvn exec:java -Dexec.mainClass="com.google.cloud.examples.datastore.DatastoreExample"
      - *  -Dexec.args=" 
      - *  delete |
      - *  display |
      - *  add  |
      - *  set  }
      + *
    6. compile using maven - {@code cd gcloud-java-examples; + * mvn package appassembler:assemble -DskipTests -Dmaven.javadoc.skip=true -Dmaven.source.skip=true} *
    7. + *
    8. run - {@code target/appassembler/bin/DatastoreExample + * [projectId] [user] [delete|display|add comment|set ]}
    9. *
    * *

    If no action is provided {@code display} is executed. diff --git a/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/Stat.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/Stat.java index 39b4df39e89c..ee9c4387a71a 100644 --- a/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/Stat.java +++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/Stat.java @@ -19,7 +19,8 @@ * or you can directly pass in a GCS file name to use. In that case you have to * be logged in (using e.g. the gcloud auth command). * - *

    See the README for a command line to run this example. + *

    See the README for compilation instructions. Run this code with + * {@code target/appassembler/bin/Stat --help | --check | --list | } * *

    In short, this version (in gcloud-java-examples) is in a package that lists gcloud-java-nio * as a dependency, so it will work directly without having to do any special work. diff --git a/gcloud-java-examples/src/main/java/com/google/cloud/examples/resourcemanager/ResourceManagerExample.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/resourcemanager/ResourceManagerExample.java index fd2eb3ed6d91..57dc81f44e50 100644 --- a/gcloud-java-examples/src/main/java/com/google/cloud/examples/resourcemanager/ResourceManagerExample.java +++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/resourcemanager/ResourceManagerExample.java @@ -32,12 +32,13 @@ * *

    This example creates, deletes, gets, and lists projects. * - *

    Steps needed for running the example:

      - *
    1. login using gcloud SDK - {@code gcloud auth login}.
    2. - *
    3. compile using maven - {@code mvn compile}
    4. - *
    5. run using maven - {@code mvn exec:java - * -Dexec.mainClass="com.google.cloud.examples.resourcemanager.ResourceManagerExample" - * -Dexec.args="[list | [create | delete | get] projectId]"}
    6. + *

      Steps needed for running the example:

        + *
      1. log in using gcloud SDK - {@code gcloud auth login}.
      2. + *
      3. compile using maven - {@code cd gcloud-java-examples; + * mvn package appassembler:assemble -DskipTests -Dmaven.javadoc.skip=true -Dmaven.source.skip=true} + *
      4. + *
      5. run - {@code target/appassembler/bin/ResourceManagerExample + * [list | [create | delete | get] projectId]}
      6. *
      */ public class ResourceManagerExample { diff --git a/gcloud-java-examples/src/main/java/com/google/cloud/examples/storage/StorageExample.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/storage/StorageExample.java index d8273e65797d..aea9dffd3f6b 100644 --- a/gcloud-java-examples/src/main/java/com/google/cloud/examples/storage/StorageExample.java +++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/storage/StorageExample.java @@ -66,11 +66,12 @@ * *

      Steps needed for running the example: *

        - *
      1. login using gcloud SDK - {@code gcloud auth login}.
      2. - *
      3. compile using maven - {@code mvn compile}
      4. - *
      5. run using maven - - *
        {@code mvn exec:java -Dexec.mainClass="com.google.cloud.examples.storage.StorageExample"
        - *  -Dexec.args="[]
        + * 
      6. log in using gcloud SDK - {@code gcloud auth login}.
      7. + *
      8. compile using maven - {@code cd gcloud-java-examples; + * mvn package appassembler:assemble -DskipTests -Dmaven.javadoc.skip=true -Dmaven.source.skip=true} + *
      9. + *
      10. run - + *
        {@code target/appassembler/bin/StorageExample []
          *  list [] |
          *  info [ []] |
          *  download   [local_file] |
        
        From 2125f8a3d394cf64d2fe2e4d8764f80ae8372e62 Mon Sep 17 00:00:00 2001
        From: JP Martin 
        Date: Tue, 12 Apr 2016 15:37:38 -0700
        Subject: [PATCH 10/38] Add url to the pom for nio and nio-examples (#909)
        
        * Add url to the pom for nio and nio-examples
        
        To mirror the changes in master.
        
        * Remove already-updated line
        ---
         gcloud-java-contrib/gcloud-java-nio-examples/pom.xml | 3 ++-
         gcloud-java-contrib/gcloud-java-nio/pom.xml          | 1 +
         2 files changed, 3 insertions(+), 1 deletion(-)
        
        diff --git a/gcloud-java-contrib/gcloud-java-nio-examples/pom.xml b/gcloud-java-contrib/gcloud-java-nio-examples/pom.xml
        index 1ae7a6c1e190..cea1f7b4be5a 100644
        --- a/gcloud-java-contrib/gcloud-java-nio-examples/pom.xml
        +++ b/gcloud-java-contrib/gcloud-java-nio-examples/pom.xml
        @@ -3,7 +3,8 @@
           4.0.0
           gcloud-java-nio-examples
           jar
        -  GCloud Java NIO example
        +  GCloud Java NIO examples
        +  https://github.com/GoogleCloudPlatform/gcloud-java/tree/master/gcloud-java-contrib/gcloud-java-nio-examples
           
             Demonstrates how to use the gcloud-java-nio jar to add GCS functionality to legacy code.
           
        diff --git a/gcloud-java-contrib/gcloud-java-nio/pom.xml b/gcloud-java-contrib/gcloud-java-nio/pom.xml
        index 5eeda451dc50..f366b86c33b0 100644
        --- a/gcloud-java-contrib/gcloud-java-nio/pom.xml
        +++ b/gcloud-java-contrib/gcloud-java-nio/pom.xml
        @@ -4,6 +4,7 @@
           gcloud-java-nio
           jar
           GCloud Java NIO
        +  https://github.com/GoogleCloudPlatform/gcloud-java/tree/master/gcloud-java-contrib/gcloud-java-nio
           
             FileSystemProvider for Java NIO to access GCS transparently.
           
        
        From 73b2917d4ec8cc6c7520bd27e67e22d005be8c4c Mon Sep 17 00:00:00 2001
        From: JP Martin 
        Date: Thu, 14 Apr 2016 13:24:53 -0700
        Subject: [PATCH 11/38] Implement bare-bones blob listing
        
        ---
         .../nio/CloudStorageFileSystemProvider.java   | 54 ++++++++++++++++---
         .../nio/CloudStorageFileSystemTest.java       | 26 +++++++++
         .../cloud/storage/testing/FakeStorageRpc.java | 49 +++++++++++++++--
         3 files changed, 119 insertions(+), 10 deletions(-)
        
        diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProvider.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProvider.java
        index 0011d5ea3521..ad590bbc5f41 100644
        --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProvider.java
        +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProvider.java
        @@ -26,6 +26,7 @@
         
         import com.google.auto.service.AutoService;
         import com.google.cloud.storage.Acl;
        +import com.google.cloud.storage.Blob;
         import com.google.cloud.storage.BlobId;
         import com.google.cloud.storage.BlobInfo;
         import com.google.cloud.storage.CopyWriter;
        @@ -35,6 +36,7 @@
         import com.google.common.annotations.VisibleForTesting;
         import com.google.common.base.MoreObjects;
         import com.google.common.base.Throwables;
        +import com.google.common.collect.AbstractIterator;
         import com.google.common.primitives.Ints;
         
         import java.io.BufferedInputStream;
        @@ -45,6 +47,7 @@
         import java.nio.file.AccessMode;
         import java.nio.file.AtomicMoveNotSupportedException;
         import java.nio.file.CopyOption;
        +import java.nio.file.DirectoryIteratorException;
         import java.nio.file.DirectoryStream;
         import java.nio.file.DirectoryStream.Filter;
         import java.nio.file.FileAlreadyExistsException;
        @@ -63,11 +66,11 @@
         import java.util.ArrayList;
         import java.util.Collections;
         import java.util.HashMap;
        +import java.util.Iterator;
         import java.util.List;
         import java.util.Map;
         import java.util.Objects;
         import java.util.Set;
        -
         import javax.annotation.Nullable;
         import javax.annotation.concurrent.ThreadSafe;
         
        @@ -83,6 +86,33 @@ public final class CloudStorageFileSystemProvider extends FileSystemProvider {
           // used only when we create a new instance of CloudStorageFileSystemProvider.
           private static StorageOptions storageOptions;
         
        +  private static class LazyPathIterator extends AbstractIterator {
        +    private final Iterator blobIterator;
        +    private final Filter filter;
        +    private final CloudStorageFileSystem fileSystem;
        +
        +    LazyPathIterator(CloudStorageFileSystem fileSystem, Iterator blobIterator, Filter filter) {
        +      this.blobIterator = blobIterator;
        +      this.filter = filter;
        +      this.fileSystem = fileSystem;
        +    }
        +
        +    @Override
        +    protected Path computeNext() {
        +      while (blobIterator.hasNext()) {
        +        Path path = fileSystem.getPath(blobIterator.next().name());
        +        try {
        +          if (filter.accept(path)) {
        +            return path;
        +          }
        +        } catch (IOException ex) {
        +          throw new DirectoryIteratorException(ex);
        +        }
        +      }
        +      return endOfData();
        +    }
        +  }
        +
           /**
            * Sets options that are only used by the constructor.
            */
        @@ -527,13 +557,23 @@ public void createDirectory(Path dir, FileAttribute... attrs) {
             checkNotNullArray(attrs);
           }
         
        -  /**
        -   * Throws {@link UnsupportedOperationException} because this feature hasn't been implemented yet.
        -   */
           @Override
        -  public DirectoryStream newDirectoryStream(Path dir, Filter filter) {
        -    // TODO: Implement me.
        -    throw new UnsupportedOperationException();
        +  public DirectoryStream newDirectoryStream(Path dir, final Filter filter) {
        +    final CloudStoragePath cloudPath = checkPath(dir);
        +    checkNotNull(filter);
        +    String prefix = cloudPath.toString();
        +    final Iterator blobIterator = storage.list(cloudPath.bucket(), Storage.BlobListOption.prefix(prefix), Storage.BlobListOption.fields()).iterateAll();
        +    return new DirectoryStream() {
        +      @Override
        +      public Iterator iterator() {
        +        return new LazyPathIterator(cloudPath.getFileSystem(), blobIterator, filter);
        +      }
        +
        +      @Override
        +      public void close() throws IOException {
        +        // Does nothing since there's nothing to close. Commenting this method to quiet codacy.
        +      }
        +    };
           }
         
           /**
        diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemTest.java
        index 579cc6bc1054..49e8d5cee4eb 100644
        --- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemTest.java
        +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemTest.java
        @@ -33,7 +33,10 @@
         import java.nio.file.FileSystem;
         import java.nio.file.FileSystems;
         import java.nio.file.Files;
        +import java.nio.file.Path;
         import java.nio.file.Paths;
        +import java.util.ArrayList;
        +import java.util.List;
         
         /**
          * Unit tests for {@link CloudStorageFileSystem}.
        @@ -132,4 +135,27 @@ public void testNullness() throws IOException, NoSuchMethodException, SecurityEx
               tester.testAllPublicInstanceMethods(fs);
             }
           }
        +
        +  @Test
        +  public void testListFiles() throws IOException {
        +    try (FileSystem fs = CloudStorageFileSystem.forBucket("bucket")) {
        +      List goodPaths = new ArrayList<>();
        +      List paths = new ArrayList<>();
        +      goodPaths.add(fs.getPath("dir/angel"));
        +      goodPaths.add(fs.getPath("dir/alone"));
        +      paths.add(fs.getPath("dir/dir2/another_angel"));
        +      paths.add(fs.getPath("atroot"));
        +      paths.addAll(goodPaths);
        +      goodPaths.add(fs.getPath("dir/dir2/"));
        +      for (Path path : paths) {
        +        Files.write(path, ALONE.getBytes(UTF_8));
        +      }
        +
        +      List got = new ArrayList<>();
        +      for (Path path : Files.newDirectoryStream(fs.getPath("/dir/"))) {
        +        got.add(path);
        +      }
        +      assertThat(got).containsExactlyElementsIn(goodPaths);
        +    }
        +  }
         }
        diff --git a/gcloud-java-storage/src/main/java/com/google/cloud/storage/testing/FakeStorageRpc.java b/gcloud-java-storage/src/main/java/com/google/cloud/storage/testing/FakeStorageRpc.java
        index 5ef212b10715..9d1ee654e070 100644
        --- a/gcloud-java-storage/src/main/java/com/google/cloud/storage/testing/FakeStorageRpc.java
        +++ b/gcloud-java-storage/src/main/java/com/google/cloud/storage/testing/FakeStorageRpc.java
        @@ -28,8 +28,10 @@
         import java.io.InputStream;
         import java.math.BigInteger;
         import java.nio.file.FileAlreadyExistsException;
        +import java.util.ArrayList;
         import java.util.Arrays;
         import java.util.HashMap;
        +import java.util.List;
         import java.util.Map;
         
         import javax.annotation.concurrent.NotThreadSafe;
        @@ -93,8 +95,49 @@ public Tuple> list(Map options) throws Stora
           @Override
           public Tuple> list(String bucket, Map options)
               throws StorageException {
        -    potentiallyThrow(options);
        -    return null;
        +    String preprefix = "";
        +    for (Map.Entry e : options.entrySet()) {
        +      switch (e.getKey()) {
        +        case PREFIX:
        +          preprefix = (String) e.getValue();
        +          if (preprefix.startsWith("/")) {
        +            preprefix = preprefix.substring(1);
        +          }
        +          break;
        +        case FIELDS:
        +          // ignore and return all the fields
        +          break;
        +        default:
        +          throw new UnsupportedOperationException("Unknown option: " + e.getKey());
        +      }
        +    }
        +    final String prefix = preprefix;
        +
        +    List values = new ArrayList<>();
        +    Map folders = new HashMap<>();
        +    for (StorageObject so : stuff.values()) {
        +      if (!so.getName().startsWith(prefix)) {
        +        continue;
        +      }
        +      int nextSlash = so.getName().indexOf("/", prefix.length());
        +      if (nextSlash >= 0) {
        +        String folderName = so.getName().substring(0, nextSlash + 1);
        +        if (folders.containsKey(folderName)) {
        +          continue;
        +        }
        +        StorageObject fakeFolder = new StorageObject();
        +        fakeFolder.setName(folderName);
        +        fakeFolder.setBucket(so.getBucket());
        +        fakeFolder.setGeneration(so.getGeneration());
        +        folders.put(folderName, fakeFolder);
        +        continue;
        +      }
        +      values.add(so);
        +    }
        +    values.addAll(folders.values());
        +    // null cursor to indicate there is no more data (empty string would cause us to be called again).
        +    // The type cast seems to be necessary to help Java's typesystem remember that collections are iterable.
        +    return Tuple.of(null, (Iterable) values);
           }
         
           /**
        @@ -113,7 +156,7 @@ public Bucket get(Bucket bucket, Map options) throws StorageException
           public StorageObject get(StorageObject object, Map options) throws StorageException {
             // we allow the "ID" option because we need to, but then we give a whole answer anyways
             // because the caller won't mind the extra fields.
        -    if (throwIfOption && !options.isEmpty() && options.size()>1
        +    if (throwIfOption && !options.isEmpty() && options.size() > 1
                 && options.keySet().toArray()[0] != Storage.BlobGetOption.fields(Storage.BlobField.ID)) {
               throw new UnsupportedOperationException();
             }
        
        From e874b76d5dd6b8b8767d4f35abae9b8912758db6 Mon Sep 17 00:00:00 2001
        From: JP Martin 
        Date: Mon, 18 Apr 2016 12:45:02 -0700
        Subject: [PATCH 12/38] Move gcloud->cloud (issue #877) and update version
         number
        
        ---
         .../gcloud-java-nio-examples/README.md        |  4 +-
         .../gcloud-java-nio-examples/pom.xml          |  4 +-
         .../nio/examples/ListFilesystems.java         |  2 +-
         gcloud-java-contrib/gcloud-java-nio/pom.xml   |  8 +-
         .../nio/CloudStorageFileSystemProvider.java   | 45 +++++----
         .../storage/contrib/nio/CloudStoragePath.java | 10 +-
         ...CloudStoragePseudoDirectoryAttributes.java |  8 +-
         .../nio/CloudStorageFileAttributesTest.java   | 26 ++----
         .../CloudStorageFileSystemProviderTest.java   | 27 +++---
         .../nio/CloudStorageFileSystemTest.java       |  2 +-
         .../contrib/nio/CloudStorageOptionsTest.java  | 23 ++---
         .../contrib/nio/CloudStoragePathTest.java     | 93 +++++++++----------
         gcloud-java-examples/pom.xml                  | 10 +-
         .../cloud/storage/testing/FakeStorageRpc.java |  5 +-
         14 files changed, 120 insertions(+), 147 deletions(-)
         rename gcloud-java-contrib/gcloud-java-nio-examples/src/main/java/com/google/{gcloud => cloud}/nio/examples/ListFilesystems.java (97%)
        
        diff --git a/gcloud-java-contrib/gcloud-java-nio-examples/README.md b/gcloud-java-contrib/gcloud-java-nio-examples/README.md
        index 206ca93759e5..bc9b5060aa76 100644
        --- a/gcloud-java-contrib/gcloud-java-nio-examples/README.md
        +++ b/gcloud-java-contrib/gcloud-java-nio-examples/README.md
        @@ -22,12 +22,12 @@ To run this example:
         4.    Run the sample with:
         
             ```
        -    java -cp gcloud-java-contrib/gcloud-java-nio/target/gcloud-java-nio-0.1.6-SNAPSHOT-shaded.jar:gcloud-java-contrib/gcloud-java-nio-examples/target/gcloud-java-nio-examples-0.1.6-SNAPSHOT.jar com.google.gcloud.nio.examples.ListFilesystems
        +    java -cp gcloud-java-contrib/gcloud-java-nio/target/gcloud-java-nio-0.2.1-SNAPSHOT-shaded.jar:gcloud-java-contrib/gcloud-java-nio-examples/target/gcloud-java-nio-examples-0.2.1-SNAPSHOT.jar com.google.cloud.nio.examples.ListFilesystems
             ```
         
             Notice that it lists gcs, which it wouldn't if you ran it without the nio jar:
             ```
        -    java -cp gcloud-java-contrib/gcloud-java-nio-examples/target/gcloud-java-nio-examples-0.1.6-SNAPSHOT.jar com.google.gcloud.nio.examples.ListFilesystems
        +    java -cp gcloud-java-contrib/gcloud-java-nio-examples/target/gcloud-java-nio-examples-0.2.1-SNAPSHOT.jar com.google.cloud.nio.examples.ListFilesystems
             ```
         
         The sample doesn't have anything about GCS in it. It gets that ability from the nio jar that
        diff --git a/gcloud-java-contrib/gcloud-java-nio-examples/pom.xml b/gcloud-java-contrib/gcloud-java-nio-examples/pom.xml
        index cea1f7b4be5a..cb674a04d9ba 100644
        --- a/gcloud-java-contrib/gcloud-java-nio-examples/pom.xml
        +++ b/gcloud-java-contrib/gcloud-java-nio-examples/pom.xml
        @@ -9,9 +9,9 @@
             Demonstrates how to use the gcloud-java-nio jar to add GCS functionality to legacy code.
           
           
        -    com.google.gcloud
        +    com.google.cloud
             gcloud-java-contrib
        -    0.1.6-SNAPSHOT
        +    0.2.1-SNAPSHOT
           
           
             nio
        diff --git a/gcloud-java-contrib/gcloud-java-nio-examples/src/main/java/com/google/gcloud/nio/examples/ListFilesystems.java b/gcloud-java-contrib/gcloud-java-nio-examples/src/main/java/com/google/cloud/nio/examples/ListFilesystems.java
        similarity index 97%
        rename from gcloud-java-contrib/gcloud-java-nio-examples/src/main/java/com/google/gcloud/nio/examples/ListFilesystems.java
        rename to gcloud-java-contrib/gcloud-java-nio-examples/src/main/java/com/google/cloud/nio/examples/ListFilesystems.java
        index 5e92768d1cf6..b03dc13763d1 100644
        --- a/gcloud-java-contrib/gcloud-java-nio-examples/src/main/java/com/google/gcloud/nio/examples/ListFilesystems.java
        +++ b/gcloud-java-contrib/gcloud-java-nio-examples/src/main/java/com/google/cloud/nio/examples/ListFilesystems.java
        @@ -14,7 +14,7 @@
          * limitations under the License.
          */
         
        -package com.google.gcloud.nio.examples;
        +package com.google.cloud.nio.examples;
         
         import java.io.IOException;
         import java.net.URI;
        diff --git a/gcloud-java-contrib/gcloud-java-nio/pom.xml b/gcloud-java-contrib/gcloud-java-nio/pom.xml
        index f366b86c33b0..3c988e768bbc 100644
        --- a/gcloud-java-contrib/gcloud-java-nio/pom.xml
        +++ b/gcloud-java-contrib/gcloud-java-nio/pom.xml
        @@ -102,18 +102,18 @@
                   
                     
                       com
        -              shaded.gcloud-nio.com
        +              shaded.cloud-nio.com
                       
        -                com.google.gcloud.**
        +                com.google.cloud.**
                       
                     
                     
                       org
        -              shaded.gcloud-nio.org
        +              shaded.cloud-nio.org
                     
                     
                       google
        -              shaded.gcloud-nio.google
        +              shaded.cloud-nio.google
                     
                   
                 
        diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProvider.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProvider.java
        index ad590bbc5f41..b9a226b7875a 100644
        --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProvider.java
        +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProvider.java
        @@ -16,10 +16,6 @@
         
         package com.google.cloud.storage.contrib.nio;
         
        -import static com.google.cloud.storage.contrib.nio.CloudStorageUtil.checkBucket;
        -import static com.google.cloud.storage.contrib.nio.CloudStorageUtil.checkNotNullArray;
        -import static com.google.cloud.storage.contrib.nio.CloudStorageUtil.checkPath;
        -import static com.google.cloud.storage.contrib.nio.CloudStorageUtil.stripPathFromUri;
         import static com.google.common.base.Preconditions.checkArgument;
         import static com.google.common.base.Preconditions.checkNotNull;
         import static com.google.common.base.Strings.isNullOrEmpty;
        @@ -71,6 +67,7 @@
         import java.util.Map;
         import java.util.Objects;
         import java.util.Set;
        +
         import javax.annotation.Nullable;
         import javax.annotation.concurrent.ThreadSafe;
         
        @@ -173,20 +170,20 @@ && isNullOrEmpty(uri.getFragment())
                     && isNullOrEmpty(uri.getUserInfo()),
                 "GCS FileSystem URIs mustn't have: port, userinfo, path, query, or fragment: %s",
                 uri);
        -    checkBucket(uri.getHost());
        +    CloudStorageUtil.checkBucket(uri.getHost());
             return new CloudStorageFileSystem(this, uri.getHost(), CloudStorageConfiguration.fromMap(env));
           }
         
           @Override
           public CloudStoragePath getPath(URI uri) {
        -    return CloudStoragePath.getPath(getFileSystem(stripPathFromUri(uri)), uri.getPath());
        +    return CloudStoragePath.getPath(getFileSystem(CloudStorageUtil.stripPathFromUri(uri)), uri.getPath());
           }
         
           @Override
           public SeekableByteChannel newByteChannel(
               Path path, Set options, FileAttribute... attrs) throws IOException {
             checkNotNull(path);
        -    checkNotNullArray(attrs);
        +    CloudStorageUtil.checkNotNullArray(attrs);
             if (options.contains(StandardOpenOption.WRITE)) {
               // TODO: Make our OpenOptions implement FileAttribute. Also remove buffer option.
               return newWriteChannel(path, options);
        @@ -222,7 +219,7 @@ private SeekableByteChannel newReadChannel(Path path, Set
                 throw new UnsupportedOperationException(option.toString());
               }
             }
        -    CloudStoragePath cloudPath = checkPath(path);
        +    CloudStoragePath cloudPath = CloudStorageUtil.checkPath(path);
             if (cloudPath.seemsLikeADirectoryAndUsePseudoDirectories()) {
               throw new CloudStoragePseudoDirectoryException(cloudPath);
             }
        @@ -232,7 +229,7 @@ private SeekableByteChannel newReadChannel(Path path, Set
           private SeekableByteChannel newWriteChannel(Path path, Set options)
               throws IOException {
         
        -    CloudStoragePath cloudPath = checkPath(path);
        +    CloudStoragePath cloudPath = CloudStorageUtil.checkPath(path);
             if (cloudPath.seemsLikeADirectoryAndUsePseudoDirectories()) {
               throw new CloudStoragePseudoDirectoryException(cloudPath);
             }
        @@ -306,7 +303,7 @@ private SeekableByteChannel newWriteChannel(Path path, Set
           @Override
           public InputStream newInputStream(Path path, OpenOption... options) throws IOException {
             InputStream result = super.newInputStream(path, options);
        -    CloudStoragePath cloudPath = checkPath(path);
        +    CloudStoragePath cloudPath = CloudStorageUtil.checkPath(path);
             int blockSize = cloudPath.getFileSystem().config().blockSize();
             for (OpenOption option : options) {
               if (option instanceof OptionBlockSize) {
        @@ -318,7 +315,7 @@ public InputStream newInputStream(Path path, OpenOption... options) throws IOExc
         
           @Override
           public boolean deleteIfExists(Path path) throws IOException {
        -    CloudStoragePath cloudPath = checkPath(path);
        +    CloudStoragePath cloudPath = CloudStorageUtil.checkPath(path);
             if (cloudPath.seemsLikeADirectoryAndUsePseudoDirectories()) {
               throw new CloudStoragePseudoDirectoryException(cloudPath);
             }
        @@ -327,7 +324,7 @@ public boolean deleteIfExists(Path path) throws IOException {
         
           @Override
           public void delete(Path path) throws IOException {
        -    CloudStoragePath cloudPath = checkPath(path);
        +    CloudStoragePath cloudPath = CloudStorageUtil.checkPath(path);
             if (!deleteIfExists(cloudPath)) {
               throw new NoSuchFileException(cloudPath.toString());
             }
        @@ -356,7 +353,7 @@ public void copy(Path source, Path target, CopyOption... options) throws IOExcep
             boolean setContentEncoding = false;
             boolean setContentDisposition = false;
         
        -    CloudStoragePath toPath = checkPath(target);
        +    CloudStoragePath toPath = CloudStorageUtil.checkPath(target);
             BlobInfo.Builder tgtInfoBuilder = BlobInfo.builder(toPath.getBlobId()).contentType("");
         
             int blockSize = -1;
        @@ -397,7 +394,7 @@ public void copy(Path source, Path target, CopyOption... options) throws IOExcep
               }
             }
         
        -    CloudStoragePath fromPath = checkPath(source);
        +    CloudStoragePath fromPath = CloudStorageUtil.checkPath(source);
         
             blockSize =
                 blockSize != -1
        @@ -465,7 +462,7 @@ public void copy(Path source, Path target, CopyOption... options) throws IOExcep
         
           @Override
           public boolean isSameFile(Path path, Path path2) {
        -    return checkPath(path).equals(checkPath(path2));
        +    return CloudStorageUtil.checkPath(path).equals(CloudStorageUtil.checkPath(path2));
           }
         
           /**
        @@ -473,7 +470,7 @@ public boolean isSameFile(Path path, Path path2) {
            */
           @Override
           public boolean isHidden(Path path) {
        -    checkPath(path);
        +    CloudStorageUtil.checkPath(path);
             return false;
           }
         
        @@ -489,7 +486,7 @@ public void checkAccess(Path path, AccessMode... modes) throws IOException {
                   throw new UnsupportedOperationException(mode.toString());
               }
             }
        -    CloudStoragePath cloudPath = checkPath(path);
        +    CloudStoragePath cloudPath = CloudStorageUtil.checkPath(path);
             if (cloudPath.seemsLikeADirectoryAndUsePseudoDirectories()) {
               return;
             }
        @@ -503,11 +500,11 @@ public void checkAccess(Path path, AccessMode... modes) throws IOException {
           public  A readAttributes(
               Path path, Class type, LinkOption... options) throws IOException {
             checkNotNull(type);
        -    checkNotNullArray(options);
        +    CloudStorageUtil.checkNotNullArray(options);
             if (type != CloudStorageFileAttributes.class && type != BasicFileAttributes.class) {
               throw new UnsupportedOperationException(type.getSimpleName());
             }
        -    CloudStoragePath cloudPath = checkPath(path);
        +    CloudStoragePath cloudPath = CloudStorageUtil.checkPath(path);
             if (cloudPath.seemsLikeADirectoryAndUsePseudoDirectories()) {
               @SuppressWarnings("unchecked")
               A result = (A) new CloudStoragePseudoDirectoryAttributes(cloudPath);
        @@ -538,11 +535,11 @@ public Map readAttributes(Path path, String attributes, LinkOpti
           public  V getFileAttributeView(
               Path path, Class type, LinkOption... options) {
             checkNotNull(type);
        -    checkNotNullArray(options);
        +    CloudStorageUtil.checkNotNullArray(options);
             if (type != CloudStorageFileAttributeView.class && type != BasicFileAttributeView.class) {
               throw new UnsupportedOperationException(type.getSimpleName());
             }
        -    CloudStoragePath cloudPath = checkPath(path);
        +    CloudStoragePath cloudPath = CloudStorageUtil.checkPath(path);
             @SuppressWarnings("unchecked")
             V result = (V) new CloudStorageFileAttributeView(storage, cloudPath);
             return result;
        @@ -553,13 +550,13 @@ public  V getFileAttributeView(
            */
           @Override
           public void createDirectory(Path dir, FileAttribute... attrs) {
        -    checkPath(dir);
        -    checkNotNullArray(attrs);
        +    CloudStorageUtil.checkPath(dir);
        +    CloudStorageUtil.checkNotNullArray(attrs);
           }
         
           @Override
           public DirectoryStream newDirectoryStream(Path dir, final Filter filter) {
        -    final CloudStoragePath cloudPath = checkPath(dir);
        +    final CloudStoragePath cloudPath = CloudStorageUtil.checkPath(dir);
             checkNotNull(filter);
             String prefix = cloudPath.toString();
             final Iterator blobIterator = storage.list(cloudPath.bucket(), Storage.BlobListOption.prefix(prefix), Storage.BlobListOption.fields()).iterateAll();
        diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStoragePath.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStoragePath.java
        index b44984e2be6a..3f954efd3bee 100644
        --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStoragePath.java
        +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStoragePath.java
        @@ -16,8 +16,6 @@
         
         package com.google.cloud.storage.contrib.nio;
         
        -import static com.google.cloud.storage.contrib.nio.CloudStorageUtil.checkNotNullArray;
        -import static com.google.cloud.storage.contrib.nio.CloudStorageUtil.checkPath;
         import static com.google.common.base.Preconditions.checkArgument;
         import static com.google.common.base.Preconditions.checkNotNull;
         
        @@ -125,7 +123,7 @@ public CloudStoragePath toAbsolutePath() {
            */
           @Override
           public CloudStoragePath toRealPath(LinkOption... options) {
        -    checkNotNullArray(options);
        +    CloudStorageUtil.checkNotNullArray(options);
             return newPath(toRealPathInternal(true));
           }
         
        @@ -155,7 +153,7 @@ public CloudStoragePath normalize() {
         
           @Override
           public CloudStoragePath resolve(Path object) {
        -    return newPath(path.resolve(checkPath(object).path));
        +    return newPath(path.resolve(CloudStorageUtil.checkPath(object).path));
           }
         
           @Override
        @@ -165,7 +163,7 @@ public CloudStoragePath resolve(String other) {
         
           @Override
           public CloudStoragePath resolveSibling(Path other) {
        -    return newPath(path.resolveSibling(checkPath(other).path));
        +    return newPath(path.resolveSibling(CloudStorageUtil.checkPath(other).path));
           }
         
           @Override
        @@ -175,7 +173,7 @@ public CloudStoragePath resolveSibling(String other) {
         
           @Override
           public CloudStoragePath relativize(Path object) {
        -    return newPath(path.relativize(checkPath(object).path));
        +    return newPath(path.relativize(CloudStorageUtil.checkPath(object).path));
           }
         
           @Nullable
        diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStoragePseudoDirectoryAttributes.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStoragePseudoDirectoryAttributes.java
        index b034eab0e8a1..a7a8c7293688 100644
        --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStoragePseudoDirectoryAttributes.java
        +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStoragePseudoDirectoryAttributes.java
        @@ -16,8 +16,6 @@
         
         package com.google.cloud.storage.contrib.nio;
         
        -import static com.google.cloud.storage.contrib.nio.CloudStorageFileSystem.FILE_TIME_UNKNOWN;
        -
         import com.google.cloud.storage.Acl;
         import com.google.common.base.Optional;
         import com.google.common.collect.ImmutableMap;
        @@ -68,17 +66,17 @@ public long size() {
         
           @Override
           public FileTime lastModifiedTime() {
        -    return FILE_TIME_UNKNOWN;
        +    return CloudStorageFileSystem.FILE_TIME_UNKNOWN;
           }
         
           @Override
           public FileTime creationTime() {
        -    return FILE_TIME_UNKNOWN;
        +    return CloudStorageFileSystem.FILE_TIME_UNKNOWN;
           }
         
           @Override
           public FileTime lastAccessTime() {
        -    return FILE_TIME_UNKNOWN;
        +    return CloudStorageFileSystem.FILE_TIME_UNKNOWN;
           }
         
           @Override
        diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributesTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributesTest.java
        index 2d8be2c2dc2d..4b64e96da3cb 100644
        --- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributesTest.java
        +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributesTest.java
        @@ -16,12 +16,6 @@
         
         package com.google.cloud.storage.contrib.nio;
         
        -import static com.google.cloud.storage.contrib.nio.CloudStorageOptions.withAcl;
        -import static com.google.cloud.storage.contrib.nio.CloudStorageOptions.withCacheControl;
        -import static com.google.cloud.storage.contrib.nio.CloudStorageOptions.withContentDisposition;
        -import static com.google.cloud.storage.contrib.nio.CloudStorageOptions.withContentEncoding;
        -import static com.google.cloud.storage.contrib.nio.CloudStorageOptions.withMimeType;
        -import static com.google.cloud.storage.contrib.nio.CloudStorageOptions.withUserMetadata;
         import static com.google.common.truth.Truth.assertThat;
         import static java.nio.charset.StandardCharsets.UTF_8;
         
        @@ -61,14 +55,14 @@ public void before() {
         
           @Test
           public void testCacheControl() throws IOException {
        -    Files.write(path, HAPPY, withCacheControl("potato"));
        +    Files.write(path, HAPPY, CloudStorageOptions.withCacheControl("potato"));
             assertThat(Files.readAttributes(path, CloudStorageFileAttributes.class).cacheControl().get())
                 .isEqualTo("potato");
           }
         
           @Test
           public void testMimeType() throws IOException {
        -    Files.write(path, HAPPY, withMimeType("text/potato"));
        +    Files.write(path, HAPPY, CloudStorageOptions.withMimeType("text/potato"));
             assertThat(Files.readAttributes(path, CloudStorageFileAttributes.class).mimeType().get())
                 .isEqualTo("text/potato");
           }
        @@ -76,14 +70,14 @@ public void testMimeType() throws IOException {
           @Test
           public void testAcl() throws IOException {
             Acl acl = Acl.of(new Acl.User("serf@example.com"), Acl.Role.READER);
        -    Files.write(path, HAPPY, withAcl(acl));
        +    Files.write(path, HAPPY, CloudStorageOptions.withAcl(acl));
             assertThat(Files.readAttributes(path, CloudStorageFileAttributes.class).acl().get())
                 .contains(acl);
           }
         
           @Test
           public void testContentDisposition() throws IOException {
        -    Files.write(path, HAPPY, withContentDisposition("crash call"));
        +    Files.write(path, HAPPY, CloudStorageOptions.withContentDisposition("crash call"));
             assertThat(
                     Files.readAttributes(path, CloudStorageFileAttributes.class).contentDisposition().get())
                 .isEqualTo("crash call");
        @@ -91,14 +85,14 @@ public void testContentDisposition() throws IOException {
         
           @Test
           public void testContentEncoding() throws IOException {
        -    Files.write(path, HAPPY, withContentEncoding("my content encoding"));
        +    Files.write(path, HAPPY, CloudStorageOptions.withContentEncoding("my content encoding"));
             assertThat(Files.readAttributes(path, CloudStorageFileAttributes.class).contentEncoding().get())
                 .isEqualTo("my content encoding");
           }
         
           @Test
           public void testUserMetadata() throws IOException {
        -    Files.write(path, HAPPY, withUserMetadata("green", "bean"));
        +    Files.write(path, HAPPY, CloudStorageOptions.withUserMetadata("green", "bean"));
             assertThat(
                     Files.readAttributes(path, CloudStorageFileAttributes.class)
                         .userMetadata()
        @@ -141,10 +135,10 @@ public void testIsSymbolicLink() throws IOException {
         
           @Test
           public void testEquals_equalsTester() throws IOException {
        -    Files.write(path, HAPPY, withMimeType("text/plain"));
        +    Files.write(path, HAPPY, CloudStorageOptions.withMimeType("text/plain"));
             CloudStorageFileAttributes a1 = Files.readAttributes(path, CloudStorageFileAttributes.class);
             CloudStorageFileAttributes a2 = Files.readAttributes(path, CloudStorageFileAttributes.class);
        -    Files.write(path, HAPPY, withMimeType("text/potato"));
        +    Files.write(path, HAPPY, CloudStorageOptions.withMimeType("text/potato"));
             CloudStorageFileAttributes b1 = Files.readAttributes(path, CloudStorageFileAttributes.class);
             CloudStorageFileAttributes b2 = Files.readAttributes(path, CloudStorageFileAttributes.class);
             new EqualsTester().addEqualityGroup(a1, a2).addEqualityGroup(b1, b2).testEquals();
        @@ -152,9 +146,9 @@ public void testEquals_equalsTester() throws IOException {
         
           @Test
           public void testFilekey() throws IOException {
        -    Files.write(path, HAPPY, withMimeType("text/plain"));
        +    Files.write(path, HAPPY, CloudStorageOptions.withMimeType("text/plain"));
             Path path2 = Paths.get(URI.create("gs://bucket/anotherrandompath"));
        -    Files.write(path2, HAPPY, withMimeType("text/plain"));
        +    Files.write(path2, HAPPY, CloudStorageOptions.withMimeType("text/plain"));
         
             // diff files cannot have same filekey
             CloudStorageFileAttributes a1 = Files.readAttributes(path, CloudStorageFileAttributes.class);
        diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProviderTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProviderTest.java
        index 9340070c9bf8..e1dfe7bceebd 100644
        --- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProviderTest.java
        +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProviderTest.java
        @@ -17,11 +17,6 @@
         package com.google.cloud.storage.contrib.nio;
         
         import static com.google.cloud.storage.contrib.nio.CloudStorageFileSystem.forBucket;
        -import static com.google.cloud.storage.contrib.nio.CloudStorageOptions.withCacheControl;
        -import static com.google.cloud.storage.contrib.nio.CloudStorageOptions.withContentDisposition;
        -import static com.google.cloud.storage.contrib.nio.CloudStorageOptions.withContentEncoding;
        -import static com.google.cloud.storage.contrib.nio.CloudStorageOptions.withMimeType;
        -import static com.google.cloud.storage.contrib.nio.CloudStorageOptions.withUserMetadata;
         import static com.google.common.truth.Truth.assertThat;
         import static java.nio.charset.StandardCharsets.UTF_8;
         import static java.nio.file.StandardCopyOption.ATOMIC_MOVE;
        @@ -565,11 +560,11 @@ public void testCopy_withCopyAttributes_preservesAttributes() throws IOException
             Files.write(
                 source,
                 "(✿◕ ‿◕ )ノ".getBytes(UTF_8),
        -        withMimeType("text/lolcat"),
        -        withCacheControl("public; max-age=666"),
        -        withContentEncoding("foobar"),
        -        withContentDisposition("my-content-disposition"),
        -        withUserMetadata("answer", "42"));
        +        CloudStorageOptions.withMimeType("text/lolcat"),
        +        CloudStorageOptions.withCacheControl("public; max-age=666"),
        +        CloudStorageOptions.withContentEncoding("foobar"),
        +        CloudStorageOptions.withContentDisposition("my-content-disposition"),
        +        CloudStorageOptions.withUserMetadata("answer", "42"));
             Files.copy(source, target, COPY_ATTRIBUTES);
         
             CloudStorageFileAttributes attributes =
        @@ -589,9 +584,9 @@ public void testCopy_withoutOptions_doesntPreservesAttributes() throws IOExcepti
             Files.write(
                 source,
                 "(✿◕ ‿◕ )ノ".getBytes(UTF_8),
        -        withMimeType("text/lolcat"),
        -        withCacheControl("public; max-age=666"),
        -        withUserMetadata("answer", "42"));
        +        CloudStorageOptions.withMimeType("text/lolcat"),
        +        CloudStorageOptions.withCacheControl("public; max-age=666"),
        +        CloudStorageOptions.withUserMetadata("answer", "42"));
             Files.copy(source, target);
         
             CloudStorageFileAttributes attributes =
        @@ -611,10 +606,10 @@ public void testCopy_overwriteAttributes() throws IOException {
             Files.write(
                 source,
                 "(✿◕ ‿◕ )ノ".getBytes(UTF_8),
        -        withMimeType("text/lolcat"),
        -        withCacheControl("public; max-age=666"));
        +        CloudStorageOptions.withMimeType("text/lolcat"),
        +        CloudStorageOptions.withCacheControl("public; max-age=666"));
             Files.copy(source, target1, COPY_ATTRIBUTES);
        -    Files.copy(source, target2, COPY_ATTRIBUTES, withMimeType("text/palfun"));
        +    Files.copy(source, target2, COPY_ATTRIBUTES, CloudStorageOptions.withMimeType("text/palfun"));
         
             CloudStorageFileAttributes attributes =
                 Files.readAttributes(target1, CloudStorageFileAttributes.class);
        diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemTest.java
        index 49e8d5cee4eb..bfb3a5bc7be9 100644
        --- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemTest.java
        +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemTest.java
        @@ -19,9 +19,9 @@
         import static com.google.common.truth.Truth.assertThat;
         import static java.nio.charset.StandardCharsets.UTF_8;
         
        +import com.google.cloud.storage.testing.LocalGcsHelper;
         import com.google.common.testing.EqualsTester;
         import com.google.common.testing.NullPointerTester;
        -import com.google.cloud.storage.testing.LocalGcsHelper;
         
         import org.junit.Before;
         import org.junit.Test;
        diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageOptionsTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageOptionsTest.java
        index 7d14193066aa..ddf5b46f0c17 100644
        --- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageOptionsTest.java
        +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageOptionsTest.java
        @@ -16,13 +16,6 @@
         
         package com.google.cloud.storage.contrib.nio;
         
        -import static com.google.cloud.storage.contrib.nio.CloudStorageOptions.withAcl;
        -import static com.google.cloud.storage.contrib.nio.CloudStorageOptions.withCacheControl;
        -import static com.google.cloud.storage.contrib.nio.CloudStorageOptions.withContentDisposition;
        -import static com.google.cloud.storage.contrib.nio.CloudStorageOptions.withContentEncoding;
        -import static com.google.cloud.storage.contrib.nio.CloudStorageOptions.withMimeType;
        -import static com.google.cloud.storage.contrib.nio.CloudStorageOptions.withUserMetadata;
        -import static com.google.cloud.storage.contrib.nio.CloudStorageOptions.withoutCaching;
         import static com.google.common.truth.Truth.assertThat;
         import static java.nio.charset.StandardCharsets.UTF_8;
         
        @@ -55,7 +48,7 @@ public void before() {
           @Test
           public void testWithoutCaching() throws IOException {
             Path path = Paths.get(URI.create("gs://bucket/path"));
        -    Files.write(path, "(✿◕ ‿◕ )ノ".getBytes(UTF_8), withoutCaching());
        +    Files.write(path, "(✿◕ ‿◕ )ノ".getBytes(UTF_8), CloudStorageOptions.withoutCaching());
             assertThat(Files.readAttributes(path, CloudStorageFileAttributes.class).cacheControl().get())
                 .isEqualTo("no-cache");
           }
        @@ -63,7 +56,7 @@ public void testWithoutCaching() throws IOException {
           @Test
           public void testCacheControl() throws IOException {
             Path path = Paths.get(URI.create("gs://bucket/path"));
        -    Files.write(path, "(✿◕ ‿◕ )ノ".getBytes(UTF_8), withCacheControl("potato"));
        +    Files.write(path, "(✿◕ ‿◕ )ノ".getBytes(UTF_8), CloudStorageOptions.withCacheControl("potato"));
             assertThat(Files.readAttributes(path, CloudStorageFileAttributes.class).cacheControl().get())
                 .isEqualTo("potato");
           }
        @@ -72,7 +65,7 @@ public void testCacheControl() throws IOException {
           public void testWithAcl() throws IOException {
             Path path = Paths.get(URI.create("gs://bucket/path"));
             Acl acl = Acl.of(new Acl.User("king@example.com"), Acl.Role.OWNER);
        -    Files.write(path, "(✿◕ ‿◕ )ノ".getBytes(UTF_8), withAcl(acl));
        +    Files.write(path, "(✿◕ ‿◕ )ノ".getBytes(UTF_8), CloudStorageOptions.withAcl(acl));
             assertThat(Files.readAttributes(path, CloudStorageFileAttributes.class).acl().get())
                 .contains(acl);
           }
        @@ -80,7 +73,7 @@ public void testWithAcl() throws IOException {
           @Test
           public void testWithContentDisposition() throws IOException {
             Path path = Paths.get(URI.create("gs://bucket/path"));
        -    Files.write(path, "(✿◕ ‿◕ )ノ".getBytes(UTF_8), withContentDisposition("bubbly fun"));
        +    Files.write(path, "(✿◕ ‿◕ )ノ".getBytes(UTF_8), CloudStorageOptions.withContentDisposition("bubbly fun"));
             assertThat(
                     Files.readAttributes(path, CloudStorageFileAttributes.class).contentDisposition().get())
                 .isEqualTo("bubbly fun");
        @@ -89,7 +82,7 @@ public void testWithContentDisposition() throws IOException {
           @Test
           public void testWithContentEncoding() throws IOException {
             Path path = Paths.get(URI.create("gs://bucket/path"));
        -    Files.write(path, "(✿◕ ‿◕ )ノ".getBytes(UTF_8), withContentEncoding("gzip"));
        +    Files.write(path, "(✿◕ ‿◕ )ノ".getBytes(UTF_8), CloudStorageOptions.withContentEncoding("gzip"));
             assertThat(Files.readAttributes(path, CloudStorageFileAttributes.class).contentEncoding().get())
                 .isEqualTo("gzip");
           }
        @@ -100,8 +93,8 @@ public void testWithUserMetadata() throws IOException {
             Files.write(
                 path,
                 "(✿◕ ‿◕ )ノ".getBytes(UTF_8),
        -        withUserMetadata("nolo", "contendere"),
        -        withUserMetadata("eternal", "sadness"));
        +        CloudStorageOptions.withUserMetadata("nolo", "contendere"),
        +        CloudStorageOptions.withUserMetadata("eternal", "sadness"));
             assertThat(
                     Files.readAttributes(path, CloudStorageFileAttributes.class).userMetadata().get("nolo"))
                 .isEqualTo("contendere");
        @@ -115,7 +108,7 @@ public void testWithUserMetadata() throws IOException {
           @Test
           public void testWithMimeType_string() throws IOException {
             Path path = Paths.get(URI.create("gs://bucket/path"));
        -    Files.write(path, "(✿◕ ‿◕ )ノ".getBytes(UTF_8), withMimeType("text/plain"));
        +    Files.write(path, "(✿◕ ‿◕ )ノ".getBytes(UTF_8), CloudStorageOptions.withMimeType("text/plain"));
             assertThat(Files.readAttributes(path, CloudStorageFileAttributes.class).mimeType().get())
                 .isEqualTo("text/plain");
           }
        diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStoragePathTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStoragePathTest.java
        index 62fc011f7b18..852aca4d07a2 100644
        --- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStoragePathTest.java
        +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStoragePathTest.java
        @@ -16,7 +16,6 @@
         
         package com.google.cloud.storage.contrib.nio;
         
        -import static com.google.cloud.storage.contrib.nio.CloudStorageFileSystem.forBucket;
         import static com.google.common.truth.Truth.assertThat;
         
         import com.google.cloud.storage.testing.LocalGcsHelper;
        @@ -53,7 +52,7 @@ public void before() {
         
           @Test
           public void testCreate_neverRemoveExtraSlashes() {
        -    try (CloudStorageFileSystem fs = forBucket("doodle")) {
        +    try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle")) {
               assertThat(fs.getPath("lol//cat").toString()).isEqualTo("lol//cat");
               assertThat((Object) fs.getPath("lol//cat")).isEqualTo(fs.getPath("lol//cat"));
             }
        @@ -61,7 +60,7 @@ public void testCreate_neverRemoveExtraSlashes() {
         
           @Test
           public void testCreate_preservesTrailingSlash() {
        -    try (CloudStorageFileSystem fs = forBucket("doodle")) {
        +    try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle")) {
               assertThat(fs.getPath("lol/cat/").toString()).isEqualTo("lol/cat/");
               assertThat((Object) fs.getPath("lol/cat/")).isEqualTo(fs.getPath("lol/cat/"));
             }
        @@ -69,7 +68,7 @@ public void testCreate_preservesTrailingSlash() {
         
           @Test
           public void testGetGcsFilename_empty_notAllowed() {
        -    try (CloudStorageFileSystem fs = forBucket("doodle")) {
        +    try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle")) {
               thrown.expect(IllegalArgumentException.class);
               fs.getPath("").getBlobId();
             }
        @@ -77,21 +76,21 @@ public void testGetGcsFilename_empty_notAllowed() {
         
           @Test
           public void testGetGcsFilename_stripsPrefixSlash() {
        -    try (CloudStorageFileSystem fs = forBucket("doodle")) {
        +    try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle")) {
               assertThat(fs.getPath("/hi").getBlobId().name()).isEqualTo("hi");
             }
           }
         
           @Test
           public void testGetGcsFilename_overrideStripPrefixSlash_doesntStripPrefixSlash() {
        -    try (CloudStorageFileSystem fs = forBucket("doodle", stripPrefixSlash(false))) {
        +    try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle", stripPrefixSlash(false))) {
               assertThat(fs.getPath("/hi").getBlobId().name()).isEqualTo("/hi");
             }
           }
         
           @Test
           public void testGetGcsFilename_extraSlashes_throwsIae() {
        -    try (CloudStorageFileSystem fs = forBucket("doodle")) {
        +    try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle")) {
               thrown.expect(IllegalArgumentException.class);
               fs.getPath("a//b").getBlobId().name();
             }
        @@ -99,14 +98,14 @@ public void testGetGcsFilename_extraSlashes_throwsIae() {
         
           @Test
           public void testGetGcsFilename_overridepermitEmptyPathComponents() {
        -    try (CloudStorageFileSystem fs = forBucket("doodle", permitEmptyPathComponents(true))) {
        +    try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle", permitEmptyPathComponents(true))) {
               assertThat(fs.getPath("a//b").getBlobId().name()).isEqualTo("a//b");
             }
           }
         
           @Test
           public void testGetGcsFilename_freaksOutOnExtraSlashesAndDotDirs() {
        -    try (CloudStorageFileSystem fs = forBucket("doodle")) {
        +    try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle")) {
               thrown.expect(IllegalArgumentException.class);
               fs.getPath("a//b/..").getBlobId().name();
             }
        @@ -114,7 +113,7 @@ public void testGetGcsFilename_freaksOutOnExtraSlashesAndDotDirs() {
         
           @Test
           public void testNameCount() {
        -    try (CloudStorageFileSystem fs = forBucket("doodle")) {
        +    try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle")) {
               assertThat(fs.getPath("").getNameCount()).isEqualTo(1);
               assertThat(fs.getPath("/").getNameCount()).isEqualTo(0);
               assertThat(fs.getPath("/hi/").getNameCount()).isEqualTo(1);
        @@ -125,7 +124,7 @@ public void testNameCount() {
         
           @Test
           public void testGetName() {
        -    try (CloudStorageFileSystem fs = forBucket("doodle")) {
        +    try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle")) {
               assertThat(fs.getPath("").getName(0).toString()).isEqualTo("");
               assertThat(fs.getPath("/hi").getName(0).toString()).isEqualTo("hi");
               assertThat(fs.getPath("hi/there").getName(1).toString()).isEqualTo("there");
        @@ -134,7 +133,7 @@ public void testGetName() {
         
           @Test
           public void testGetName_negative_throwsIae() {
        -    try (CloudStorageFileSystem fs = forBucket("doodle")) {
        +    try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle")) {
               thrown.expect(IllegalArgumentException.class);
               fs.getPath("angel").getName(-1);
             }
        @@ -142,7 +141,7 @@ public void testGetName_negative_throwsIae() {
         
           @Test
           public void testGetName_overflow_throwsIae() {
        -    try (CloudStorageFileSystem fs = forBucket("doodle")) {
        +    try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle")) {
               thrown.expect(IllegalArgumentException.class);
               fs.getPath("angel").getName(1);
             }
        @@ -150,7 +149,7 @@ public void testGetName_overflow_throwsIae() {
         
           @Test
           public void testIterator() {
        -    try (CloudStorageFileSystem fs = forBucket("doodle")) {
        +    try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle")) {
               assertThat(Iterables.get(fs.getPath("/dog/mog"), 0).toString()).isEqualTo("dog");
               assertThat(Iterables.get(fs.getPath("/dog/mog"), 1).toString()).isEqualTo("mog");
               assertThat(Iterables.size(fs.getPath("/"))).isEqualTo(0);
        @@ -161,7 +160,7 @@ public void testIterator() {
         
           @Test
           public void testNormalize() {
        -    try (CloudStorageFileSystem fs = forBucket("doodle")) {
        +    try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle")) {
               assertThat(fs.getPath("/").normalize().toString()).isEqualTo("/");
               assertThat(fs.getPath("a/x/../b/x/..").normalize().toString()).isEqualTo("a/b/");
               assertThat(fs.getPath("/x/x/../../♡").normalize().toString()).isEqualTo("/♡");
        @@ -171,7 +170,7 @@ public void testNormalize() {
         
           @Test
           public void testNormalize_dot_becomesBlank() {
        -    try (CloudStorageFileSystem fs = forBucket("doodle")) {
        +    try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle")) {
               assertThat(fs.getPath("").normalize().toString()).isEqualTo("");
               assertThat(fs.getPath(".").normalize().toString()).isEqualTo("");
             }
        @@ -179,14 +178,14 @@ public void testNormalize_dot_becomesBlank() {
         
           @Test
           public void testNormalize_trailingSlash_isPreserved() {
        -    try (CloudStorageFileSystem fs = forBucket("doodle")) {
        +    try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle")) {
               assertThat(fs.getPath("o/").normalize().toString()).isEqualTo("o/");
             }
           }
         
           @Test
           public void testNormalize_doubleDot_becomesBlank() {
        -    try (CloudStorageFileSystem fs = forBucket("doodle")) {
        +    try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle")) {
               assertThat(fs.getPath("..").normalize().toString()).isEqualTo("");
               assertThat(fs.getPath("../..").normalize().toString()).isEqualTo("");
             }
        @@ -194,14 +193,14 @@ public void testNormalize_doubleDot_becomesBlank() {
         
           @Test
           public void testNormalize_extraSlashes_getRemoved() {
        -    try (CloudStorageFileSystem fs = forBucket("doodle")) {
        +    try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle")) {
               assertThat(fs.getPath("//life///b/good//").normalize().toString()).isEqualTo("/life/b/good/");
             }
           }
         
           @Test
           public void testToRealPath_hasDotDir_throwsIae() {
        -    try (CloudStorageFileSystem fs = forBucket("doodle")) {
        +    try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle")) {
               fs.getPath("a/hi./b").toRealPath();
               fs.getPath("a/.hi/b").toRealPath();
               thrown.expect(IllegalArgumentException.class);
        @@ -212,7 +211,7 @@ public void testToRealPath_hasDotDir_throwsIae() {
         
           @Test
           public void testToRealPath_hasDotDotDir_throwsIae() {
        -    try (CloudStorageFileSystem fs = forBucket("doodle")) {
        +    try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle")) {
               fs.getPath("a/hi../b").toRealPath();
               fs.getPath("a/..hi/b").toRealPath();
               thrown.expect(IllegalArgumentException.class);
        @@ -223,7 +222,7 @@ public void testToRealPath_hasDotDotDir_throwsIae() {
         
           @Test
           public void testToRealPath_extraSlashes_throwsIae() {
        -    try (CloudStorageFileSystem fs = forBucket("doodle")) {
        +    try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle")) {
               thrown.expect(IllegalArgumentException.class);
               thrown.expectMessage("extra slashes");
               fs.getPath("a//b").toRealPath();
        @@ -232,7 +231,7 @@ public void testToRealPath_extraSlashes_throwsIae() {
         
           @Test
           public void testToRealPath_overridePermitEmptyPathComponents_extraSlashes_slashesRemain() {
        -    try (CloudStorageFileSystem fs = forBucket("doodle", permitEmptyPathComponents(true))) {
        +    try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle", permitEmptyPathComponents(true))) {
               assertThat(fs.getPath("/life///b/./good/").toRealPath().toString())
                   .isEqualTo("life///b/./good/");
             }
        @@ -240,7 +239,7 @@ public void testToRealPath_overridePermitEmptyPathComponents_extraSlashes_slashe
         
           @Test
           public void testToRealPath_permitEmptyPathComponents_doesNotNormalize() {
        -    try (CloudStorageFileSystem fs = forBucket("doodle", permitEmptyPathComponents(true))) {
        +    try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle", permitEmptyPathComponents(true))) {
               assertThat(fs.getPath("a").toRealPath().toString()).isEqualTo("a");
               assertThat(fs.getPath("a//b").toRealPath().toString()).isEqualTo("a//b");
               assertThat(fs.getPath("a//./b//..").toRealPath().toString()).isEqualTo("a//./b//..");
        @@ -249,14 +248,14 @@ public void testToRealPath_permitEmptyPathComponents_doesNotNormalize() {
         
           @Test
           public void testToRealPath_withWorkingDirectory_makesAbsolute() {
        -    try (CloudStorageFileSystem fs = forBucket("doodle", workingDirectory("/lol"))) {
        +    try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle", workingDirectory("/lol"))) {
               assertThat(fs.getPath("a").toRealPath().toString()).isEqualTo("lol/a");
             }
           }
         
           @Test
           public void testToRealPath_disableStripPrefixSlash_makesPathAbsolute() {
        -    try (CloudStorageFileSystem fs = forBucket("doodle", stripPrefixSlash(false))) {
        +    try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle", stripPrefixSlash(false))) {
               assertThat(fs.getPath("a").toRealPath().toString()).isEqualTo("/a");
               assertThat(fs.getPath("/a").toRealPath().toString()).isEqualTo("/a");
             }
        @@ -264,21 +263,21 @@ public void testToRealPath_disableStripPrefixSlash_makesPathAbsolute() {
         
           @Test
           public void testToRealPath_trailingSlash_getsPreserved() {
        -    try (CloudStorageFileSystem fs = forBucket("doodle")) {
        +    try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle")) {
               assertThat(fs.getPath("a/b/").toRealPath().toString()).isEqualTo("a/b/");
             }
           }
         
           @Test
           public void testNormalize_empty_returnsEmpty() {
        -    try (CloudStorageFileSystem fs = forBucket("doodle")) {
        +    try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle")) {
               assertThat(fs.getPath("").normalize().toString()).isEqualTo("");
             }
           }
         
           @Test
           public void testNormalize_preserveTrailingSlash() {
        -    try (CloudStorageFileSystem fs = forBucket("doodle")) {
        +    try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle")) {
               assertThat(fs.getPath("a/b/../c/").normalize().toString()).isEqualTo("a/c/");
               assertThat(fs.getPath("a/b/./c/").normalize().toString()).isEqualTo("a/b/c/");
             }
        @@ -286,7 +285,7 @@ public void testNormalize_preserveTrailingSlash() {
         
           @Test
           public void testGetParent_preserveTrailingSlash() {
        -    try (CloudStorageFileSystem fs = forBucket("doodle")) {
        +    try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle")) {
               assertThat(fs.getPath("a/b/c").getParent().toString()).isEqualTo("a/b/");
               assertThat(fs.getPath("a/b/c/").getParent().toString()).isEqualTo("a/b/");
               assertThat((Object) fs.getPath("").getParent()).isNull();
        @@ -298,7 +297,7 @@ public void testGetParent_preserveTrailingSlash() {
         
           @Test
           public void testGetRoot() {
        -    try (CloudStorageFileSystem fs = forBucket("doodle")) {
        +    try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle")) {
               assertThat(fs.getPath("/hello").getRoot().toString()).isEqualTo("/");
               assertThat((Object) fs.getPath("hello").getRoot()).isNull();
             }
        @@ -306,7 +305,7 @@ public void testGetRoot() {
         
           @Test
           public void testRelativize() {
        -    try (CloudStorageFileSystem fs = forBucket("doodle")) {
        +    try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle")) {
               assertThat(
                       fs.getPath("/foo/bar/lol/cat").relativize(fs.getPath("/foo/a/b/../../c")).toString())
                   .isEqualTo("../../../a/b/../../c");
        @@ -315,7 +314,7 @@ public void testRelativize() {
         
           @Test
           public void testRelativize_providerMismatch() {
        -    try (CloudStorageFileSystem gcs = forBucket("doodle")) {
        +    try (CloudStorageFileSystem gcs = CloudStorageFileSystem.forBucket("doodle")) {
               thrown.expect(ProviderMismatchException.class);
               gcs.getPath("/etc").relativize(FileSystems.getDefault().getPath("/dog"));
             }
        @@ -324,7 +323,7 @@ public void testRelativize_providerMismatch() {
           @Test
           @SuppressWarnings("ReturnValueIgnored") // testing that an Exception is thrown
           public void testRelativize_providerMismatch2() {
        -    try (CloudStorageFileSystem gcs = forBucket("doodle")) {
        +    try (CloudStorageFileSystem gcs = CloudStorageFileSystem.forBucket("doodle")) {
               thrown.expect(ProviderMismatchException.class);
               gcs.getPath("/dog").relativize(FileSystems.getDefault().getPath("/etc"));
             }
        @@ -332,7 +331,7 @@ public void testRelativize_providerMismatch2() {
         
           @Test
           public void testResolve() {
        -    try (CloudStorageFileSystem fs = forBucket("doodle")) {
        +    try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle")) {
               assertThat(fs.getPath("/hi").resolve("there").toString()).isEqualTo("/hi/there");
               assertThat(fs.getPath("hi").resolve("there").toString()).isEqualTo("hi/there");
             }
        @@ -340,7 +339,7 @@ public void testResolve() {
         
           @Test
           public void testResolve_providerMismatch() {
        -    try (CloudStorageFileSystem gcs = forBucket("doodle")) {
        +    try (CloudStorageFileSystem gcs = CloudStorageFileSystem.forBucket("doodle")) {
               thrown.expect(ProviderMismatchException.class);
               gcs.getPath("etc").resolve(FileSystems.getDefault().getPath("/dog"));
             }
        @@ -348,7 +347,7 @@ public void testResolve_providerMismatch() {
         
           @Test
           public void testIsAbsolute() {
        -    try (CloudStorageFileSystem fs = forBucket("doodle")) {
        +    try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle")) {
               assertThat(fs.getPath("/hi").isAbsolute()).isTrue();
               assertThat(fs.getPath("hi").isAbsolute()).isFalse();
             }
        @@ -356,7 +355,7 @@ public void testIsAbsolute() {
         
           @Test
           public void testToAbsolutePath() {
        -    try (CloudStorageFileSystem fs = forBucket("doodle")) {
        +    try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle")) {
               assertThat((Object) fs.getPath("/hi").toAbsolutePath()).isEqualTo(fs.getPath("/hi"));
               assertThat((Object) fs.getPath("hi").toAbsolutePath()).isEqualTo(fs.getPath("/hi"));
             }
        @@ -364,14 +363,14 @@ public void testToAbsolutePath() {
         
           @Test
           public void testToAbsolutePath_withWorkingDirectory() {
        -    try (CloudStorageFileSystem fs = forBucket("doodle", workingDirectory("/lol"))) {
        +    try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle", workingDirectory("/lol"))) {
               assertThat(fs.getPath("a").toAbsolutePath().toString()).isEqualTo("/lol/a");
             }
           }
         
           @Test
           public void testGetFileName() {
        -    try (CloudStorageFileSystem fs = forBucket("doodle")) {
        +    try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle")) {
               assertThat(fs.getPath("/hi/there").getFileName().toString()).isEqualTo("there");
               assertThat(fs.getPath("military/fashion/show").getFileName().toString()).isEqualTo("show");
             }
        @@ -379,7 +378,7 @@ public void testGetFileName() {
         
           @Test
           public void testCompareTo() {
        -    try (CloudStorageFileSystem fs = forBucket("doodle")) {
        +    try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle")) {
               assertThat(fs.getPath("/hi/there").compareTo(fs.getPath("/hi/there"))).isEqualTo(0);
               assertThat(fs.getPath("/hi/there").compareTo(fs.getPath("/hi/therf"))).isEqualTo(-1);
               assertThat(fs.getPath("/hi/there").compareTo(fs.getPath("/hi/therd"))).isEqualTo(1);
        @@ -388,7 +387,7 @@ public void testCompareTo() {
         
           @Test
           public void testStartsWith() {
        -    try (CloudStorageFileSystem fs = forBucket("doodle")) {
        +    try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle")) {
               assertThat(fs.getPath("/hi/there").startsWith(fs.getPath("/hi/there"))).isTrue();
               assertThat(fs.getPath("/hi/there").startsWith(fs.getPath("/hi/therf"))).isFalse();
               assertThat(fs.getPath("/hi/there").startsWith(fs.getPath("/hi"))).isTrue();
        @@ -401,7 +400,7 @@ public void testStartsWith() {
         
           @Test
           public void testEndsWith() {
        -    try (CloudStorageFileSystem fs = forBucket("doodle")) {
        +    try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle")) {
               assertThat(fs.getPath("/hi/there").endsWith(fs.getPath("there"))).isTrue();
               assertThat(fs.getPath("/hi/there").endsWith(fs.getPath("therf"))).isFalse();
               assertThat(fs.getPath("/hi/there").endsWith(fs.getPath("/blag/therf"))).isFalse();
        @@ -442,7 +441,7 @@ public void testRelativize_willWorkWithRecursiveCopy() throws IOException {
         
           @Test
           public void testToFile_unsupported() {
        -    try (CloudStorageFileSystem fs = forBucket("doodle")) {
        +    try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle")) {
               Path path = fs.getPath("/lol");
               thrown.expect(UnsupportedOperationException.class);
               path.toFile();
        @@ -451,7 +450,7 @@ public void testToFile_unsupported() {
         
           @Test
           public void testEquals() {
        -    try (CloudStorageFileSystem fs = forBucket("doodle")) {
        +    try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle")) {
               new EqualsTester()
                   // These are obviously equal.
                   .addEqualityGroup(fs.getPath("/hello/cat"), fs.getPath("/hello/cat"))
        @@ -467,7 +466,7 @@ public void testEquals() {
         
           @Test
           public void testEquals_currentDirectoryIsTakenIntoConsideration() {
        -    try (CloudStorageFileSystem fs = forBucket("doodle", workingDirectory("/hello"))) {
        +    try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle", workingDirectory("/hello"))) {
               new EqualsTester()
                   .addEqualityGroup(fs.getPath("cat"), fs.getPath("/hello/cat"))
                   .addEqualityGroup(fs.getPath(""), fs.getPath("/hello"))
        @@ -477,7 +476,7 @@ public void testEquals_currentDirectoryIsTakenIntoConsideration() {
         
           @Test
           public void testNullness() throws NoSuchMethodException, SecurityException {
        -    try (CloudStorageFileSystem fs = forBucket("doodle")) {
        +    try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle")) {
               NullPointerTester tester = new NullPointerTester();
               tester.ignore(CloudStoragePath.class.getMethod("equals", Object.class));
               tester.setDefault(Path.class, fs.getPath("sup"));
        diff --git a/gcloud-java-examples/pom.xml b/gcloud-java-examples/pom.xml
        index cc2aa1a8d423..c9a16bee21d6 100644
        --- a/gcloud-java-examples/pom.xml
        +++ b/gcloud-java-examples/pom.xml
        @@ -52,25 +52,25 @@
                 
                   
                     
        -              com.google.gcloud.examples.bigquery.BigQueryExample
        +              com.google.cloud.examples.bigquery.BigQueryExample
                       BigQueryExample
                     
                     
        -              com.google.gcloud.examples.datastore.DatastoreExample
        +              com.google.cloud.examples.datastore.DatastoreExample
                       DatastoreExample
                     
                     
        -              com.google.gcloud.examples.nio.Stat
        +              com.google.cloud.examples.nio.Stat
                       Stat
                     
                     
                       
        -                com.google.gcloud.examples.resourcemanager.ResourceManagerExample
        +                com.google.cloud.examples.resourcemanager.ResourceManagerExample
                       
                       ResourceManagerExample
                     
                     
        -              com.google.gcloud.examples.storage.StorageExample
        +              com.google.cloud.examples.storage.StorageExample
                       StorageExample
                     
                   
        diff --git a/gcloud-java-storage/src/main/java/com/google/cloud/storage/testing/FakeStorageRpc.java b/gcloud-java-storage/src/main/java/com/google/cloud/storage/testing/FakeStorageRpc.java
        index 9d1ee654e070..3b372d33a09a 100644
        --- a/gcloud-java-storage/src/main/java/com/google/cloud/storage/testing/FakeStorageRpc.java
        +++ b/gcloud-java-storage/src/main/java/com/google/cloud/storage/testing/FakeStorageRpc.java
        @@ -18,11 +18,10 @@
         
         import com.google.api.services.storage.model.Bucket;
         import com.google.api.services.storage.model.StorageObject;
        -import com.google.cloud.storage.StorageBatch;
        -import com.google.cloud.storage.spi.RpcBatch;
        -import com.google.cloud.storage.spi.StorageRpc;
         import com.google.cloud.storage.Storage;
         import com.google.cloud.storage.StorageException;
        +import com.google.cloud.storage.spi.RpcBatch;
        +import com.google.cloud.storage.spi.StorageRpc;
         
         import java.io.IOException;
         import java.io.InputStream;
        
        From 10180ebd9409ef37346325da86ee89892000162d Mon Sep 17 00:00:00 2001
        From: JP Martin 
        Date: Tue, 19 Apr 2016 13:56:54 -0700
        Subject: [PATCH 13/38] Integration test for ls (#931)
        
        ---
         .../nio/CloudStorageFileSystemProvider.java   | 10 +++--
         .../storage/contrib/nio/CloudStoragePath.java |  3 +-
         .../storage/contrib/nio/it/ITGcsNio.java      | 25 +++++++++++++
         .../cloud/storage/testing/FakeStorageRpc.java | 37 +++++++++++++------
         4 files changed, 60 insertions(+), 15 deletions(-)
        
        diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProvider.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProvider.java
        index b9a226b7875a..f7ded91ecc61 100644
        --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProvider.java
        +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProvider.java
        @@ -88,7 +88,8 @@ private static class LazyPathIterator extends AbstractIterator {
             private final Filter filter;
             private final CloudStorageFileSystem fileSystem;
         
        -    LazyPathIterator(CloudStorageFileSystem fileSystem, Iterator blobIterator, Filter filter) {
        +    LazyPathIterator(CloudStorageFileSystem fileSystem, Iterator blobIterator,
        +                     Filter filter) {
               this.blobIterator = blobIterator;
               this.filter = filter;
               this.fileSystem = fileSystem;
        @@ -176,7 +177,8 @@ && isNullOrEmpty(uri.getUserInfo()),
         
           @Override
           public CloudStoragePath getPath(URI uri) {
        -    return CloudStoragePath.getPath(getFileSystem(CloudStorageUtil.stripPathFromUri(uri)), uri.getPath());
        +    return CloudStoragePath.getPath(
        +        getFileSystem(CloudStorageUtil.stripPathFromUri(uri)), uri.getPath());
           }
         
           @Override
        @@ -559,7 +561,9 @@ public DirectoryStream newDirectoryStream(Path dir, final Filter blobIterator = storage.list(cloudPath.bucket(), Storage.BlobListOption.prefix(prefix), Storage.BlobListOption.fields()).iterateAll();
        +    final Iterator blobIterator = storage.list(cloudPath.bucket(),
        +        Storage.BlobListOption.prefix(prefix), Storage.BlobListOption.currentDirectory(),
        +        Storage.BlobListOption.fields()).iterateAll();
             return new DirectoryStream() {
               @Override
               public Iterator iterator() {
        diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStoragePath.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStoragePath.java
        index 3f954efd3bee..fdb407ba1852 100644
        --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStoragePath.java
        +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStoragePath.java
        @@ -308,7 +308,8 @@ public String toString() {
           @Override
           public URI toUri() {
             try {
        -      return new URI(CloudStorageFileSystem.URI_SCHEME, bucket(), path.toAbsolutePath().toString(), null);
        +      return new URI(
        +          CloudStorageFileSystem.URI_SCHEME, bucket(), path.toAbsolutePath().toString(), null);
             } catch (URISyntaxException e) {
               throw new AssertionError(e);
             }
        diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/it/ITGcsNio.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/it/ITGcsNio.java
        index e5ead7d6ce37..0f2667ad2cad 100644
        --- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/it/ITGcsNio.java
        +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/it/ITGcsNio.java
        @@ -27,10 +27,12 @@
         import java.nio.ByteBuffer;
         import java.nio.channels.ReadableByteChannel;
         import java.nio.channels.SeekableByteChannel;
        +import java.nio.file.FileSystem;
         import java.nio.file.Files;
         import java.nio.file.NoSuchFileException;
         import java.nio.file.Path;
         import java.nio.file.StandardOpenOption;
        +import java.util.ArrayList;
         import java.util.Arrays;
         import java.util.List;
         import java.util.Random;
        @@ -316,6 +318,29 @@ public void testCopy() throws IOException {
             }
           }
         
        +  @Test
        +  public void testListFiles() throws IOException {
        +    try (FileSystem fs = getTestBucket()) {
        +      List goodPaths = new ArrayList<>();
        +      List paths = new ArrayList<>();
        +      goodPaths.add(fs.getPath("dir/angel"));
        +      goodPaths.add(fs.getPath("dir/alone"));
        +      paths.add(fs.getPath("dir/dir2/another_angel"));
        +      paths.add(fs.getPath("atroot"));
        +      paths.addAll(goodPaths);
        +      goodPaths.add(fs.getPath("dir/dir2/"));
        +      for (Path path : paths) {
        +        fillFile(storage, path.toString(), SML_SIZE);
        +      }
        +
        +      List got = new ArrayList<>();
        +      for (Path path : Files.newDirectoryStream(fs.getPath("dir/"))) {
        +        got.add(path);
        +      }
        +      assertThat(got).containsExactlyElementsIn(goodPaths);
        +    }
        +  }
        +
           private int readFully(ReadableByteChannel chan, byte[] outputBuf) throws IOException {
             ByteBuffer buf = ByteBuffer.wrap(outputBuf);
             int sofar = 0;
        diff --git a/gcloud-java-storage/src/main/java/com/google/cloud/storage/testing/FakeStorageRpc.java b/gcloud-java-storage/src/main/java/com/google/cloud/storage/testing/FakeStorageRpc.java
        index 3b372d33a09a..24b38155368a 100644
        --- a/gcloud-java-storage/src/main/java/com/google/cloud/storage/testing/FakeStorageRpc.java
        +++ b/gcloud-java-storage/src/main/java/com/google/cloud/storage/testing/FakeStorageRpc.java
        @@ -94,6 +94,7 @@ public Tuple> list(Map options) throws Stora
           @Override
           public Tuple> list(String bucket, Map options)
               throws StorageException {
        +    String delimiter = null;
             String preprefix = "";
             for (Map.Entry e : options.entrySet()) {
               switch (e.getKey()) {
        @@ -103,6 +104,9 @@ public Tuple> list(String bucket, Map
                     preprefix = preprefix.substring(1);
                   }
                   break;
        +        case DELIMITER:
        +          delimiter = (String) e.getValue();
        +          break;
                 case FIELDS:
                   // ignore and return all the fields
                   break;
        @@ -118,17 +122,7 @@ public Tuple> list(String bucket, Map
               if (!so.getName().startsWith(prefix)) {
                 continue;
               }
        -      int nextSlash = so.getName().indexOf("/", prefix.length());
        -      if (nextSlash >= 0) {
        -        String folderName = so.getName().substring(0, nextSlash + 1);
        -        if (folders.containsKey(folderName)) {
        -          continue;
        -        }
        -        StorageObject fakeFolder = new StorageObject();
        -        fakeFolder.setName(folderName);
        -        fakeFolder.setBucket(so.getBucket());
        -        fakeFolder.setGeneration(so.getGeneration());
        -        folders.put(folderName, fakeFolder);
        +      if (processedAsFolder(so, delimiter, prefix, folders)) {
                 continue;
               }
               values.add(so);
        @@ -334,4 +328,25 @@ private void potentiallyThrow(Map options) throws UnsupportedOperatio
               throw new UnsupportedOperationException();
             }
           }
        +  
        +  // Returns true if this is a folder. Adds it to folders if it isn't already there.
        +  private static boolean processedAsFolder(StorageObject so, String delimiter, String prefix, /* inout */ Map folders) {
        +    if (delimiter == null) {
        +      return false;
        +    }
        +    int nextSlash = so.getName().indexOf(delimiter, prefix.length());
        +    if (nextSlash < 0) {
        +      return false;
        +    }
        +    String folderName = so.getName().substring(0, nextSlash + 1);
        +    if (folders.containsKey(folderName)) {
        +      return true;
        +    }
        +    StorageObject fakeFolder = new StorageObject();
        +    fakeFolder.setName(folderName);
        +    fakeFolder.setBucket(so.getBucket());
        +    fakeFolder.setGeneration(so.getGeneration());
        +    folders.put(folderName, fakeFolder);
        +    return true;
        +  }
         }
        
        From dc71290ec882424e96f5d80a6016528484e83cc6 Mon Sep 17 00:00:00 2001
        From: JP Martin 
        Date: Tue, 26 Apr 2016 16:33:26 -0700
        Subject: [PATCH 14/38] Move Stat and clarify example command line
        
        - Stat is now in com.google.cloud.examples.nio (following the gcloud ->
        cloud move)
        - Added version for appassembler to remove warning
        - added 'mvn install' to instructions because it's necessary.
        ---
         gcloud-java-examples/README.md | 3 ++-
         gcloud-java-examples/pom.xml   | 1 +
         2 files changed, 3 insertions(+), 1 deletion(-)
        
        diff --git a/gcloud-java-examples/README.md b/gcloud-java-examples/README.md
        index 57a35d7ef9d5..3882505563fa 100644
        --- a/gcloud-java-examples/README.md
        +++ b/gcloud-java-examples/README.md
        @@ -37,7 +37,8 @@ To run examples from your command line:
         
         2. Set your current project using `gcloud config set project PROJECT_ID`. This step is not necessary for `ResourceManagerExample`.
         
        -3. Compile using Maven: `cd gcloud-java-examples` in command line from your base project directory and then `mvn package appassembler:assemble -DskipTests -Dmaven.javadoc.skip=true  -Dmaven.source.skip=true`.
        +3. Compile using Maven: `mvn install -DskipTests -Dmaven.javadoc.skip=true -Dmaven.source.skip=true` in command line from your base project directory
        +   then `cd gcloud-java-examples` and finally `mvn package appassembler:assemble -DskipTests -Dmaven.javadoc.skip=true -Dmaven.source.skip=true`.
         
         4. Run an example from the command line using the Maven-generated scripts.
         
        diff --git a/gcloud-java-examples/pom.xml b/gcloud-java-examples/pom.xml
        index c9a16bee21d6..c49034d54569 100644
        --- a/gcloud-java-examples/pom.xml
        +++ b/gcloud-java-examples/pom.xml
        @@ -49,6 +49,7 @@
               
                 org.codehaus.mojo
                 appassembler-maven-plugin
        +        1.10
                 
                   
                     
        
        From a538f94e59500fc0ee2349b7751074df528d4f4f Mon Sep 17 00:00:00 2001
        From: JP Martin 
        Date: Wed, 27 Apr 2016 10:10:12 -0700
        Subject: [PATCH 15/38] Fix markdown and simplify command
        
        ---
         gcloud-java-examples/README.md | 10 +++++-----
         1 file changed, 5 insertions(+), 5 deletions(-)
        
        diff --git a/gcloud-java-examples/README.md b/gcloud-java-examples/README.md
        index 3882505563fa..a4cb2c0471c0 100644
        --- a/gcloud-java-examples/README.md
        +++ b/gcloud-java-examples/README.md
        @@ -9,8 +9,8 @@ Examples for gcloud-java (Java idiomatic client for [Google Cloud Platform][clou
         [![Codacy Badge](https://api.codacy.com/project/badge/grade/9da006ad7c3a4fe1abd142e77c003917)](https://www.codacy.com/app/mziccard/gcloud-java)
         [![Dependency Status](https://www.versioneye.com/user/projects/56bd8ee72a29ed002d2b0969/badge.svg?style=flat)](https://www.versioneye.com/user/projects/56bd8ee72a29ed002d2b0969)
         
        --  [Homepage] (https://googlecloudplatform.github.io/gcloud-java/)
        --  [Examples] (http://googlecloudplatform.github.io/gcloud-java/apidocs/index.html?com/google/cloud/examples/package-summary.html)
        +-  [Homepage](https://googlecloudplatform.github.io/gcloud-java/)
        +-  [Examples](http://googlecloudplatform.github.io/gcloud-java/apidocs/index.html?com/google/cloud/examples/package-summary.html)
         
         Quickstart
         ----------
        @@ -37,8 +37,8 @@ To run examples from your command line:
         
         2. Set your current project using `gcloud config set project PROJECT_ID`. This step is not necessary for `ResourceManagerExample`.
         
        -3. Compile using Maven: `mvn install -DskipTests -Dmaven.javadoc.skip=true -Dmaven.source.skip=true` in command line from your base project directory
        -   then `cd gcloud-java-examples` and finally `mvn package appassembler:assemble -DskipTests -Dmaven.javadoc.skip=true -Dmaven.source.skip=true`.
        +3. Compile using Maven: `mvn install -DskipTests` in command line from your base project directory
        +   then `cd gcloud-java-examples` and finally `mvn package appassembler:assemble -DskipTests`.
         
         4. Run an example from the command line using the Maven-generated scripts.
         
        @@ -159,7 +159,7 @@ Java 7 or above is required for using this client.
         Versioning
         ----------
         
        -This library follows [Semantic Versioning] (http://semver.org/).
        +This library follows [Semantic Versioning](http://semver.org/).
         
         It is currently in major version zero (``0.y.z``), which means that anything
         may change at any time and the public API should not be considered
        
        From 9e948248d7e969ddbbd23b8ee4f0de690a2a5c8f Mon Sep 17 00:00:00 2001
        From: JP Martin 
        Date: Wed, 4 May 2016 13:18:15 -0700
        Subject: [PATCH 16/38] NIO CountBytes example (#975)
        
        ---
         gcloud-java-examples/pom.xml                  |   4 +
         .../google/cloud/examples/nio/CountBytes.java | 102 ++++++++++++++++++
         .../com/google/cloud/examples/nio/Stat.java   |  16 +++
         3 files changed, 122 insertions(+)
         create mode 100644 gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/CountBytes.java
        
        diff --git a/gcloud-java-examples/pom.xml b/gcloud-java-examples/pom.xml
        index c49034d54569..54bb2d1a9681 100644
        --- a/gcloud-java-examples/pom.xml
        +++ b/gcloud-java-examples/pom.xml
        @@ -64,6 +64,10 @@
                       com.google.cloud.examples.nio.Stat
                       Stat
                     
        +            
        +              com.google.cloud.examples.nio.CountBytes
        +              CountBytes
        +            
                     
                       
                         com.google.cloud.examples.resourcemanager.ResourceManagerExample
        diff --git a/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/CountBytes.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/CountBytes.java
        new file mode 100644
        index 000000000000..50ad77d72f24
        --- /dev/null
        +++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/CountBytes.java
        @@ -0,0 +1,102 @@
        +/*
        + * Copyright 2016 Google Inc. All Rights Reserved.
        + *
        + * 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 com.google.cloud.examples.nio;
        +
        +import com.google.common.base.Stopwatch;
        +
        +import java.io.IOException;
        +import java.net.URI;
        +import java.nio.ByteBuffer;
        +import java.nio.channels.SeekableByteChannel;
        +import java.nio.file.Files;
        +import java.nio.file.Path;
        +import java.nio.file.Paths;
        +import java.util.concurrent.TimeUnit;
        +
        +/**
        + * CountBytes will read through the whole file given as input.
        + *
        + * 

        This example shows how to read a file size using NIO. + * File.size returns the size of the file as saved in Storage metadata. + * This class also shows how to read all of the file's contents using NIO, + * and reports how long it took. + * + *

        See the README for compilation instructions. Run this code with + * {@code target/appassembler/bin/CountBytes } + */ +public class CountBytes { + + /** + * See the class documentation. + */ + public static void main(String[] args) throws IOException { + if (args.length == 0 || args[0].equals("--help")) { + help(); + return; + } + for (String a : args) { + countFile(a); + } + } + + /** + * Print the length of the indicated file. + * + *

        This uses the normal Java NIO Api, so it can take advantage of any installed + * NIO Filesystem provider without any extra effort. + */ + private static void countFile(String fname) { + // large buffers pay off + final int bufSize = 50 * 1024 * 1024; + try { + Path path = Paths.get(new URI(fname)); + long size = Files.size(path); + System.out.println(fname + ": " + size + " bytes."); + ByteBuffer buf = ByteBuffer.allocate(bufSize); + System.out.println("Reading the whole file..."); + Stopwatch sw = Stopwatch.createStarted(); + SeekableByteChannel chan = Files.newByteChannel(path); + long total = 0; + int readCalls = 0; + while (chan.read(buf) > 0) { + readCalls++; + total += buf.position(); + buf.flip(); + } + readCalls++; // We must count the last call + long elapsed = sw.elapsed(TimeUnit.SECONDS); + System.out.println("Read all " + total + " bytes in " + elapsed + "s. " + + "(" + readCalls +" calls to chan.read)"); + if (total != size) { + System.out.println("Wait, this doesn't match! We saw " + total + " bytes, " + + "yet the file size is listed at " + size + " bytes."); + } + } catch (Exception ex) { + System.out.println(fname + ": " + ex.toString()); + } + } + + private static void help() { + String[] help = + {"The argument is a ", + "and we show the length of that file." + }; + for (String s : help) { + System.out.println(s); + } + } +} diff --git a/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/Stat.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/Stat.java index ee9c4387a71a..b8d018f8f738 100644 --- a/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/Stat.java +++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/Stat.java @@ -1,3 +1,19 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * 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 com.google.cloud.examples.nio; import java.io.IOException; From ded161f57648738fda452705af49db47dea2683c Mon Sep 17 00:00:00 2001 From: JP Martin Date: Wed, 4 May 2016 15:43:04 -0700 Subject: [PATCH 17/38] Add ParallelCountBytes Made it and CountBytes compute an MD5, so I could check that they match (they do). --- gcloud-java-examples/pom.xml | 4 + .../google/cloud/examples/nio/CountBytes.java | 6 + .../examples/nio/ParallelCountBytes.java | 174 ++++++++++++++++++ 3 files changed, 184 insertions(+) create mode 100644 gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/ParallelCountBytes.java diff --git a/gcloud-java-examples/pom.xml b/gcloud-java-examples/pom.xml index 54bb2d1a9681..8441e5600c29 100644 --- a/gcloud-java-examples/pom.xml +++ b/gcloud-java-examples/pom.xml @@ -68,6 +68,10 @@ com.google.cloud.examples.nio.CountBytes CountBytes + + com.google.cloud.examples.nio.ParallelCountBytes + ParallelCountBytes + com.google.cloud.examples.resourcemanager.ResourceManagerExample diff --git a/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/CountBytes.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/CountBytes.java index 50ad77d72f24..184df901f714 100644 --- a/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/CountBytes.java +++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/CountBytes.java @@ -18,6 +18,7 @@ import com.google.common.base.Stopwatch; +import javax.xml.bind.annotation.adapters.HexBinaryAdapter; import java.io.IOException; import java.net.URI; import java.nio.ByteBuffer; @@ -25,6 +26,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.security.MessageDigest; import java.util.concurrent.TimeUnit; /** @@ -72,8 +74,10 @@ private static void countFile(String fname) { SeekableByteChannel chan = Files.newByteChannel(path); long total = 0; int readCalls = 0; + MessageDigest md = MessageDigest.getInstance("MD5"); while (chan.read(buf) > 0) { readCalls++; + md.update(buf.array(), 0, buf.position()); total += buf.position(); buf.flip(); } @@ -81,6 +85,8 @@ private static void countFile(String fname) { long elapsed = sw.elapsed(TimeUnit.SECONDS); System.out.println("Read all " + total + " bytes in " + elapsed + "s. " + "(" + readCalls +" calls to chan.read)"); + String hex = (new HexBinaryAdapter()).marshal(md.digest()); + System.out.println("The MD5 is: 0x" + hex); if (total != size) { System.out.println("Wait, this doesn't match! We saw " + total + " bytes, " + "yet the file size is listed at " + size + " bytes."); diff --git a/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/ParallelCountBytes.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/ParallelCountBytes.java new file mode 100644 index 000000000000..388af029e905 --- /dev/null +++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/ParallelCountBytes.java @@ -0,0 +1,174 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * 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 com.google.cloud.examples.nio; + +import com.google.common.base.Stopwatch; + +import javax.xml.bind.annotation.adapters.HexBinaryAdapter; +import java.io.IOException; +import java.net.URI; +import java.nio.ByteBuffer; +import java.nio.channels.SeekableByteChannel; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.MessageDigest; +import java.util.concurrent.TimeUnit; + +/** + * ParallelCountBytes will read through the whole file given as input. + * + *

        This example shows how to go through all the contents of a file, + * in order, using multithreaded NIO reads.It also reports how long it took. + * + *

        See the README for compilation instructions. Run this code with + * {@code target/appassembler/bin/ParallelCountBytes } + */ +public class ParallelCountBytes { + + private class BufWithLock { + public Object lock; + public ByteBuffer buf; + public boolean full; + public Thread t; + + public BufWithLock(int size) { + this.buf = ByteBuffer.allocate(size); + this.lock = new Object(); + } + } + + /** + * See the class documentation. + */ + public static void main(String[] args) throws IOException { + new ParallelCountBytes().start(args); + } + + public void start(String[] args) throws IOException { + if (args.length == 0 || args[0].equals("--help")) { + help(); + return; + } + for (String a : args) { + countFile(a); + } + } + + private void stridedRead(SeekableByteChannel chan, int blockSize, int firstBlock, int stride, BufWithLock output) { + try { + // stagger the threads a little bit. + Thread.sleep(250 * firstBlock); + long pos = firstBlock * blockSize; + synchronized(output.lock) { + while (true) { + if (pos > chan.size()) { + break; + } + chan.position(pos); + // read until buffer is full, or EOF + while (chan.read(output.buf) > 0) {}; + output.full = true; + output.lock.notifyAll(); + if (output.buf.hasRemaining()) { + break; + } + // wait for main thread to process it + while (output.full) { + output.lock.wait(); + } + output.buf.flip(); + pos += stride * blockSize; + } + } + } catch (InterruptedException | IOException o) { + // this simple example doesn't handle errors, sorry. + } + } + + /** + * Print the length of the indicated file. + * + *

        This uses the normal Java NIO Api, so it can take advantage of any installed + * NIO Filesystem provider without any extra effort. + */ + private void countFile(String fname) throws IOException{ + // large buffers pay off + final int bufSize = 50 * 1024 * 1024; + try { + Path path = Paths.get(new URI(fname)); + long size = Files.size(path); + System.out.println(fname + ": " + size + " bytes."); + ByteBuffer buf = ByteBuffer.allocate(bufSize); + int nBlocks = (int)Math.ceil( size / (double)bufSize); + int nThreads = nBlocks; + if (nThreads > 4) nThreads = 4; + System.out.println("Reading the whole file using " + nThreads + " threads..."); + Stopwatch sw = Stopwatch.createStarted(); + final BufWithLock[] bufs = new BufWithLock[nThreads]; + for (int i = 0; i < nThreads; i++) { + bufs[i] = new BufWithLock(bufSize); + final SeekableByteChannel chan = Files.newByteChannel(path); + final int finalNThreads = nThreads; + final int finalI = i; + bufs[i].t = new Thread(new Runnable() { + @Override + public void run() { + stridedRead(chan, bufSize, finalI, finalNThreads, bufs[finalI]); + } + }); + bufs[i].t.start(); + } + + long total = 0; + MessageDigest md = MessageDigest.getInstance("MD5"); + for (int block = 0; block < nBlocks; block++) { + BufWithLock bwl = bufs[block % bufs.length]; + synchronized (bwl.lock) { + while (!bwl.full) { + bwl.lock.wait(); + } + md.update(bwl.buf.array(), 0, bwl.buf.position()); + total += bwl.buf.position(); + bwl.full = false; + bwl.lock.notifyAll(); + } + } + + long elapsed = sw.elapsed(TimeUnit.SECONDS); + System.out.println("Read all " + total + " bytes in " + elapsed + "s. "); + String hex = (new HexBinaryAdapter()).marshal(md.digest()); + System.out.println("The MD5 is: 0x" + hex); + if (total != size) { + System.out.println("Wait, this doesn't match! We saw " + total + " bytes, " + + "yet the file size is listed at " + size + " bytes."); + } + } catch (Exception ex) { + System.out.println(fname + ": " + ex.toString()); + } + } + + private static void help() { + String[] help = + {"The argument is a ", + "and we show the length of that file." + }; + for (String s : help) { + System.out.println(s); + } + } +} From db89ffe943a26d599810bdd2aafe9dcc6741f89e Mon Sep 17 00:00:00 2001 From: JP Martin Date: Thu, 5 May 2016 11:18:18 -0700 Subject: [PATCH 18/38] Refactor to use ExecutorService and BaseEncoding --- .../google/cloud/examples/nio/CountBytes.java | 6 +- .../examples/nio/ParallelCountBytes.java | 126 ++++++++---------- 2 files changed, 59 insertions(+), 73 deletions(-) diff --git a/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/CountBytes.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/CountBytes.java index 184df901f714..a3f9779a0790 100644 --- a/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/CountBytes.java +++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/CountBytes.java @@ -17,8 +17,8 @@ package com.google.cloud.examples.nio; import com.google.common.base.Stopwatch; +import com.google.common.io.BaseEncoding; -import javax.xml.bind.annotation.adapters.HexBinaryAdapter; import java.io.IOException; import java.net.URI; import java.nio.ByteBuffer; @@ -35,7 +35,7 @@ *

        This example shows how to read a file size using NIO. * File.size returns the size of the file as saved in Storage metadata. * This class also shows how to read all of the file's contents using NIO, - * and reports how long it took. + * computes a MD5 hash, and reports how long it took. * *

        See the README for compilation instructions. Run this code with * {@code target/appassembler/bin/CountBytes } @@ -85,7 +85,7 @@ private static void countFile(String fname) { long elapsed = sw.elapsed(TimeUnit.SECONDS); System.out.println("Read all " + total + " bytes in " + elapsed + "s. " + "(" + readCalls +" calls to chan.read)"); - String hex = (new HexBinaryAdapter()).marshal(md.digest()); + String hex = String.valueOf(BaseEncoding.base16().encode(md.digest())); System.out.println("The MD5 is: 0x" + hex); if (total != size) { System.out.println("Wait, this doesn't match! We saw " + total + " bytes, " + diff --git a/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/ParallelCountBytes.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/ParallelCountBytes.java index 388af029e905..825f60ad0ee8 100644 --- a/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/ParallelCountBytes.java +++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/ParallelCountBytes.java @@ -17,8 +17,8 @@ package com.google.cloud.examples.nio; import com.google.common.base.Stopwatch; +import com.google.common.io.BaseEncoding; -import javax.xml.bind.annotation.adapters.HexBinaryAdapter; import java.io.IOException; import java.net.URI; import java.nio.ByteBuffer; @@ -27,28 +27,58 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.security.MessageDigest; +import java.util.ArrayDeque; +import java.util.Queue; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; /** * ParallelCountBytes will read through the whole file given as input. * *

        This example shows how to go through all the contents of a file, - * in order, using multithreaded NIO reads.It also reports how long it took. + * in order, using multithreaded NIO reads. + * It prints a MD5 hash and reports how long it took. * *

        See the README for compilation instructions. Run this code with * {@code target/appassembler/bin/ParallelCountBytes } */ public class ParallelCountBytes { - private class BufWithLock { - public Object lock; - public ByteBuffer buf; - public boolean full; - public Thread t; + /** + * WorkUnit holds a buffer and the instructions for what to put in it. + */ + private class WorkUnit implements Callable { + public final ByteBuffer buf; + final SeekableByteChannel chan; + final int blockSize; + int blockIndex; - public BufWithLock(int size) { - this.buf = ByteBuffer.allocate(size); - this.lock = new Object(); + public WorkUnit(SeekableByteChannel chan, int blockSize, int blockIndex) { + this.chan = chan; + this.buf = ByteBuffer.allocate(blockSize); + this.blockSize = blockSize; + this.blockIndex = blockIndex; + } + + @Override + public WorkUnit call() throws IOException { + int pos = blockSize * blockIndex; + if (pos > chan.size()) { + return this; + } + chan.position(pos); + // read until buffer is full, or EOF + while (chan.read(buf) > 0) {}; + return this; + } + + public WorkUnit resetForIndex(int blockIndex) { + this.blockIndex = blockIndex; + buf.flip(); + return this; } } @@ -69,37 +99,6 @@ public void start(String[] args) throws IOException { } } - private void stridedRead(SeekableByteChannel chan, int blockSize, int firstBlock, int stride, BufWithLock output) { - try { - // stagger the threads a little bit. - Thread.sleep(250 * firstBlock); - long pos = firstBlock * blockSize; - synchronized(output.lock) { - while (true) { - if (pos > chan.size()) { - break; - } - chan.position(pos); - // read until buffer is full, or EOF - while (chan.read(output.buf) > 0) {}; - output.full = true; - output.lock.notifyAll(); - if (output.buf.hasRemaining()) { - break; - } - // wait for main thread to process it - while (output.full) { - output.lock.wait(); - } - output.buf.flip(); - pos += stride * blockSize; - } - } - } catch (InterruptedException | IOException o) { - // this simple example doesn't handle errors, sorry. - } - } - /** * Print the length of the indicated file. * @@ -109,49 +108,36 @@ private void stridedRead(SeekableByteChannel chan, int blockSize, int firstBlock private void countFile(String fname) throws IOException{ // large buffers pay off final int bufSize = 50 * 1024 * 1024; + Queue> work = new ArrayDeque<>(); try { Path path = Paths.get(new URI(fname)); long size = Files.size(path); System.out.println(fname + ": " + size + " bytes."); - ByteBuffer buf = ByteBuffer.allocate(bufSize); - int nBlocks = (int)Math.ceil( size / (double)bufSize); - int nThreads = nBlocks; + int nThreads = (int) Math.ceil(size / (double) bufSize); if (nThreads > 4) nThreads = 4; System.out.println("Reading the whole file using " + nThreads + " threads..."); Stopwatch sw = Stopwatch.createStarted(); - final BufWithLock[] bufs = new BufWithLock[nThreads]; - for (int i = 0; i < nThreads; i++) { - bufs[i] = new BufWithLock(bufSize); - final SeekableByteChannel chan = Files.newByteChannel(path); - final int finalNThreads = nThreads; - final int finalI = i; - bufs[i].t = new Thread(new Runnable() { - @Override - public void run() { - stridedRead(chan, bufSize, finalI, finalNThreads, bufs[finalI]); - } - }); - bufs[i].t.start(); - } - long total = 0; MessageDigest md = MessageDigest.getInstance("MD5"); - for (int block = 0; block < nBlocks; block++) { - BufWithLock bwl = bufs[block % bufs.length]; - synchronized (bwl.lock) { - while (!bwl.full) { - bwl.lock.wait(); - } - md.update(bwl.buf.array(), 0, bwl.buf.position()); - total += bwl.buf.position(); - bwl.full = false; - bwl.lock.notifyAll(); + + ExecutorService exec = Executors.newFixedThreadPool(nThreads); + int blockIndex; + for (blockIndex = 0; blockIndex < nThreads; blockIndex++) { + work.add(exec.submit(new WorkUnit(Files.newByteChannel(path), bufSize, blockIndex))); + } + while (true) { + WorkUnit full = work.remove().get(); + md.update(full.buf.array(), 0, full.buf.position()); + total += full.buf.position(); + if (full.buf.hasRemaining()) { + break; } + work.add(exec.submit(full.resetForIndex(blockIndex++))); } long elapsed = sw.elapsed(TimeUnit.SECONDS); System.out.println("Read all " + total + " bytes in " + elapsed + "s. "); - String hex = (new HexBinaryAdapter()).marshal(md.digest()); + String hex = String.valueOf(BaseEncoding.base16().encode(md.digest())); System.out.println("The MD5 is: 0x" + hex); if (total != size) { System.out.println("Wait, this doesn't match! We saw " + total + " bytes, " + From be727b57861d27a409b98e0d26d55f9ccc232e42 Mon Sep 17 00:00:00 2001 From: JP Martin Date: Thu, 5 May 2016 14:13:35 -0700 Subject: [PATCH 19/38] Add shutdown --- .../java/com/google/cloud/examples/nio/ParallelCountBytes.java | 1 + 1 file changed, 1 insertion(+) diff --git a/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/ParallelCountBytes.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/ParallelCountBytes.java index 825f60ad0ee8..67620880bb6d 100644 --- a/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/ParallelCountBytes.java +++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/ParallelCountBytes.java @@ -134,6 +134,7 @@ private void countFile(String fname) throws IOException{ } work.add(exec.submit(full.resetForIndex(blockIndex++))); } + exec.shutdown(); long elapsed = sw.elapsed(TimeUnit.SECONDS); System.out.println("Read all " + total + " bytes in " + elapsed + "s. "); From a176250251608b6e56bf7e2ee7b5e10c7eec3f0a Mon Sep 17 00:00:00 2001 From: JP Martin Date: Fri, 6 May 2016 10:01:49 -0700 Subject: [PATCH 20/38] Close channels Plus a little bit of cleanup. --- .../examples/nio/ParallelCountBytes.java | 42 +++++++++++-------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/ParallelCountBytes.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/ParallelCountBytes.java index 67620880bb6d..f63d5d1cba6d 100644 --- a/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/ParallelCountBytes.java +++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/ParallelCountBytes.java @@ -19,6 +19,7 @@ import com.google.common.base.Stopwatch; import com.google.common.io.BaseEncoding; +import java.io.Closeable; import java.io.IOException; import java.net.URI; import java.nio.ByteBuffer; @@ -49,8 +50,16 @@ public class ParallelCountBytes { /** * WorkUnit holds a buffer and the instructions for what to put in it. + * + *

        Use it like this: + *

          + *
        1. call() + *
        2. the data is now in buf, you can access it directly + *
        3. if need more, call resetForIndex(...) and go back to the top. + *
        4. else, call close() + *
        */ - private class WorkUnit implements Callable { + private static class WorkUnit implements Callable, Closeable { public final ByteBuffer buf; final SeekableByteChannel chan; final int blockSize; @@ -70,7 +79,7 @@ public WorkUnit call() throws IOException { return this; } chan.position(pos); - // read until buffer is full, or EOF + // read until buffer it is full, or EOF while (chan.read(buf) > 0) {}; return this; } @@ -80,16 +89,16 @@ public WorkUnit resetForIndex(int blockIndex) { buf.flip(); return this; } + + public void close() throws IOException { + chan.close(); + } } /** * See the class documentation. */ - public static void main(String[] args) throws IOException { - new ParallelCountBytes().start(args); - } - - public void start(String[] args) throws IOException { + public static void main(String[] args) throws Exception { if (args.length == 0 || args[0].equals("--help")) { help(); return; @@ -100,16 +109,15 @@ public void start(String[] args) throws IOException { } /** - * Print the length of the indicated file. + * Print the length and MD5 of the indicated file. * *

        This uses the normal Java NIO Api, so it can take advantage of any installed * NIO Filesystem provider without any extra effort. */ - private void countFile(String fname) throws IOException{ + private static void countFile(String fname) throws Exception { // large buffers pay off final int bufSize = 50 * 1024 * 1024; Queue> work = new ArrayDeque<>(); - try { Path path = Paths.get(new URI(fname)); long size = Files.size(path); System.out.println(fname + ": " + size + " bytes."); @@ -125,14 +133,15 @@ private void countFile(String fname) throws IOException{ for (blockIndex = 0; blockIndex < nThreads; blockIndex++) { work.add(exec.submit(new WorkUnit(Files.newByteChannel(path), bufSize, blockIndex))); } - while (true) { + while (!work.isEmpty()) { WorkUnit full = work.remove().get(); md.update(full.buf.array(), 0, full.buf.position()); total += full.buf.position(); if (full.buf.hasRemaining()) { - break; + full.close(); + } else { + work.add(exec.submit(full.resetForIndex(blockIndex++))); } - work.add(exec.submit(full.resetForIndex(blockIndex++))); } exec.shutdown(); @@ -141,12 +150,9 @@ private void countFile(String fname) throws IOException{ String hex = String.valueOf(BaseEncoding.base16().encode(md.digest())); System.out.println("The MD5 is: 0x" + hex); if (total != size) { - System.out.println("Wait, this doesn't match! We saw " + total + " bytes, " + - "yet the file size is listed at " + size + " bytes."); + System.out.println("Wait, this doesn't match! We saw " + total + " bytes, " + + "yet the file size is listed at " + size + " bytes."); } - } catch (Exception ex) { - System.out.println(fname + ": " + ex.toString()); - } } private static void help() { From f2aa350398a73ce118533db5419127a37b3a3797 Mon Sep 17 00:00:00 2001 From: JP Martin Date: Tue, 31 May 2016 23:12:24 -0700 Subject: [PATCH 21/38] Close channel and fix typo (#1036) --- .../google/cloud/examples/nio/CountBytes.java | 39 ++++++++++--------- .../examples/nio/ParallelCountBytes.java | 2 +- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/CountBytes.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/CountBytes.java index a3f9779a0790..18425ae62438 100644 --- a/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/CountBytes.java +++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/CountBytes.java @@ -71,25 +71,26 @@ private static void countFile(String fname) { ByteBuffer buf = ByteBuffer.allocate(bufSize); System.out.println("Reading the whole file..."); Stopwatch sw = Stopwatch.createStarted(); - SeekableByteChannel chan = Files.newByteChannel(path); - long total = 0; - int readCalls = 0; - MessageDigest md = MessageDigest.getInstance("MD5"); - while (chan.read(buf) > 0) { - readCalls++; - md.update(buf.array(), 0, buf.position()); - total += buf.position(); - buf.flip(); - } - readCalls++; // We must count the last call - long elapsed = sw.elapsed(TimeUnit.SECONDS); - System.out.println("Read all " + total + " bytes in " + elapsed + "s. " + - "(" + readCalls +" calls to chan.read)"); - String hex = String.valueOf(BaseEncoding.base16().encode(md.digest())); - System.out.println("The MD5 is: 0x" + hex); - if (total != size) { - System.out.println("Wait, this doesn't match! We saw " + total + " bytes, " + - "yet the file size is listed at " + size + " bytes."); + try (SeekableByteChannel chan = Files.newByteChannel(path)) { + long total = 0; + int readCalls = 0; + MessageDigest md = MessageDigest.getInstance("MD5"); + while (chan.read(buf) > 0) { + readCalls++; + md.update(buf.array(), 0, buf.position()); + total += buf.position(); + buf.flip(); + } + readCalls++; // We must count the last call + long elapsed = sw.elapsed(TimeUnit.SECONDS); + System.out.println("Read all " + total + " bytes in " + elapsed + "s. " + + "(" + readCalls +" calls to chan.read)"); + String hex = String.valueOf(BaseEncoding.base16().encode(md.digest())); + System.out.println("The MD5 is: 0x" + hex); + if (total != size) { + System.out.println("Wait, this doesn't match! We saw " + total + " bytes, " + + "yet the file size is listed at " + size + " bytes."); + } } } catch (Exception ex) { System.out.println(fname + ": " + ex.toString()); diff --git a/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/ParallelCountBytes.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/ParallelCountBytes.java index f63d5d1cba6d..f2b0959acee8 100644 --- a/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/ParallelCountBytes.java +++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/ParallelCountBytes.java @@ -79,7 +79,7 @@ public WorkUnit call() throws IOException { return this; } chan.position(pos); - // read until buffer it is full, or EOF + // read until buffer is full, or EOF while (chan.read(buf) > 0) {}; return this; } From 1305c4d223daa36247ffcc1e756f03cc4dea7586 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Mon, 13 Jun 2016 17:25:38 -0400 Subject: [PATCH 22/38] Update pom.xml versions --- gcloud-java-contrib/gcloud-java-nio-examples/README.md | 4 ++-- gcloud-java-contrib/gcloud-java-nio-examples/pom.xml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/gcloud-java-contrib/gcloud-java-nio-examples/README.md b/gcloud-java-contrib/gcloud-java-nio-examples/README.md index bc9b5060aa76..41ec45ff1874 100644 --- a/gcloud-java-contrib/gcloud-java-nio-examples/README.md +++ b/gcloud-java-contrib/gcloud-java-nio-examples/README.md @@ -22,12 +22,12 @@ To run this example: 4. Run the sample with: ``` - java -cp gcloud-java-contrib/gcloud-java-nio/target/gcloud-java-nio-0.2.1-SNAPSHOT-shaded.jar:gcloud-java-contrib/gcloud-java-nio-examples/target/gcloud-java-nio-examples-0.2.1-SNAPSHOT.jar com.google.cloud.nio.examples.ListFilesystems + java -cp gcloud-java-contrib/gcloud-java-nio/target/gcloud-java-nio-0.2.5-SNAPSHOT-shaded.jar:gcloud-java-contrib/gcloud-java-nio-examples/target/gcloud-java-nio-examples-0.2.5-SNAPSHOT.jar com.google.cloud.nio.examples.ListFilesystems ``` Notice that it lists gcs, which it wouldn't if you ran it without the nio jar: ``` - java -cp gcloud-java-contrib/gcloud-java-nio-examples/target/gcloud-java-nio-examples-0.2.1-SNAPSHOT.jar com.google.cloud.nio.examples.ListFilesystems + java -cp gcloud-java-contrib/gcloud-java-nio-examples/target/gcloud-java-nio-examples-0.2.5-SNAPSHOT.jar com.google.cloud.nio.examples.ListFilesystems ``` The sample doesn't have anything about GCS in it. It gets that ability from the nio jar that diff --git a/gcloud-java-contrib/gcloud-java-nio-examples/pom.xml b/gcloud-java-contrib/gcloud-java-nio-examples/pom.xml index cb674a04d9ba..c44b927f25ba 100644 --- a/gcloud-java-contrib/gcloud-java-nio-examples/pom.xml +++ b/gcloud-java-contrib/gcloud-java-nio-examples/pom.xml @@ -11,7 +11,7 @@ com.google.cloud gcloud-java-contrib - 0.2.1-SNAPSHOT + 0.2.5-SNAPSHOT nio From 85257a0ea851ad791906f4917f8438f290ae3d9b Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Mon, 13 Jun 2016 17:25:47 -0400 Subject: [PATCH 23/38] FakeStorageRpc with master changes --- .../com/google/cloud/storage/testing/FakeStorageRpc.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/gcloud-java-storage/src/main/java/com/google/cloud/storage/testing/FakeStorageRpc.java b/gcloud-java-storage/src/main/java/com/google/cloud/storage/testing/FakeStorageRpc.java index 24b38155368a..a07f5dc26c4c 100644 --- a/gcloud-java-storage/src/main/java/com/google/cloud/storage/testing/FakeStorageRpc.java +++ b/gcloud-java-storage/src/main/java/com/google/cloud/storage/testing/FakeStorageRpc.java @@ -193,7 +193,8 @@ public boolean delete(StorageObject object, Map options) throws Stora @Override public RpcBatch createBatch() { - return null; + //return new DefaultRpcBatch(storage); + throw new UnsupportedOperationException(); } @Override @@ -328,7 +329,7 @@ private void potentiallyThrow(Map options) throws UnsupportedOperatio throw new UnsupportedOperationException(); } } - + // Returns true if this is a folder. Adds it to folders if it isn't already there. private static boolean processedAsFolder(StorageObject so, String delimiter, String prefix, /* inout */ Map folders) { if (delimiter == null) { From 29ea8b0df2b5efd4159cdae38b581497efe1be7d Mon Sep 17 00:00:00 2001 From: JP Martin Date: Tue, 14 Jun 2016 11:20:54 -0700 Subject: [PATCH 24/38] Use appassembler for Compute and Dns examples (#1057) --- gcloud-java-examples/README.md | 26 +++++++++---------- gcloud-java-examples/pom.xml | 8 ++++++ .../examples/compute/ComputeExample.java | 12 +++------ .../google/cloud/examples/dns/DnsExample.java | 13 +++------- 4 files changed, 28 insertions(+), 31 deletions(-) diff --git a/gcloud-java-examples/README.md b/gcloud-java-examples/README.md index a4cb2c0471c0..17145046f29b 100644 --- a/gcloud-java-examples/README.md +++ b/gcloud-java-examples/README.md @@ -68,11 +68,11 @@ To run examples from your command line: Before running the example, go to the [Google Developers Console][developers-console] to ensure that Compute API is enabled. ``` - mvn exec:java -Dexec.mainClass="com.google.cloud.examples.compute.ComputeExample" -Dexec.args="create image-disk us-central1-a test-disk debian-cloud debian-8-jessie-v20160329" - mvn exec:java -Dexec.mainClass="com.google.cloud.examples.compute.ComputeExample" -Dexec.args="create instance us-central1-a test-instance n1-standard-1 test-disk default" - mvn exec:java -Dexec.mainClass="com.google.cloud.examples.compute.ComputeExample" -Dexec.args="add-access-config us-central1-a test-instance nic0 NAT" - mvn exec:java -Dexec.mainClass="com.google.cloud.examples.compute.ComputeExample" -Dexec.args="delete instance us-central1-a test-instance" - mvn exec:java -Dexec.mainClass="com.google.cloud.examples.compute.ComputeExample" -Dexec.args="delete disk us-central1-a test-disk" + target/appassembler/bin/ComputeExample create image-disk us-central1-a test-disk debian-cloud debian-8-jessie-v20160329 + target/appassembler/bin/ComputeExample create instance us-central1-a test-instance n1-standard-1 test-disk default + target/appassembler/bin/ComputeExample add-access-config us-central1-a test-instance nic0 NAT + target/appassembler/bin/ComputeExample delete instance us-central1-a test-instance + target/appassembler/bin/ComputeExample delete disk us-central1-a test-disk ``` * Here's an example run of `DatastoreExample`. @@ -90,14 +90,14 @@ To run examples from your command line: You will need to replace the domain name `elaborateexample.com` with your own domain name with [verified ownership] (https://www.google.com/webmasters/verification/home). Also, note that the example creates and deletes record sets of type A only. Operations with other record types are not implemented in the example. ``` - mvn exec:java -Dexec.mainClass="com.google.cloud.examples.dns.DnsExample" -Dexec.args="create some-sample-zone elaborateexample.com. description" - mvn exec:java -Dexec.mainClass="com.google.cloud.examples.dns.DnsExample" -Dexec.args="list" - mvn exec:java -Dexec.mainClass="com.google.cloud.examples.dns.DnsExample" -Dexec.args="list some-sample-zone records" - mvn exec:java -Dexec.mainClass="com.google.cloud.examples.dns.DnsExample" -Dexec.args="add-record some-sample-zone www.elaborateexample.com. 12.13.14.15 69" - mvn exec:java -Dexec.mainClass="com.google.cloud.examples.dns.DnsExample" -Dexec.args="get some-sample-zone" - mvn exec:java -Dexec.mainClass="com.google.cloud.examples.dns.DnsExample" -Dexec.args="delete-record some-sample-zone www.elaborateexample.com. 12.13.14.15 69" - mvn exec:java -Dexec.mainClass="com.google.cloud.examples.dns.DnsExample" -Dexec.args="list some-sample-zone changes ascending" - mvn exec:java -Dexec.mainClass="com.google.cloud.examples.dns.DnsExample" -Dexec.args="delete some-sample-zone" + target/appassembler/bin/DnsExample create some-sample-zone elaborateexample.com. description + target/appassembler/bin/DnsExample list + target/appassembler/bin/DnsExample list some-sample-zone records + target/appassembler/bin/DnsExample add-record some-sample-zone www.elaborateexample.com. 12.13.14.15 69 + target/appassembler/bin/DnsExample get some-sample-zone + target/appassembler/bin/DnsExample delete-record some-sample-zone www.elaborateexample.com. 12.13.14.15 69 + target/appassembler/bin/DnsExample list some-sample-zone changes ascending + target/appassembler/bin/DnsExample delete some-sample-zone ``` * Here's an example run of `PubSubExample`. diff --git a/gcloud-java-examples/pom.xml b/gcloud-java-examples/pom.xml index 8441e5600c29..93c7cced9c8e 100644 --- a/gcloud-java-examples/pom.xml +++ b/gcloud-java-examples/pom.xml @@ -56,6 +56,10 @@ com.google.cloud.examples.bigquery.BigQueryExample BigQueryExample + + com.google.cloud.examples.compute.ComputeExample + ComputeExample + com.google.cloud.examples.datastore.DatastoreExample DatastoreExample @@ -64,6 +68,10 @@ com.google.cloud.examples.nio.Stat Stat + + com.google.cloud.examples.dns.DnsExample + DnsExample + com.google.cloud.examples.nio.CountBytes CountBytes diff --git a/gcloud-java-examples/src/main/java/com/google/cloud/examples/compute/ComputeExample.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/compute/ComputeExample.java index 23421e197f58..603aaeefe4ae 100644 --- a/gcloud-java-examples/src/main/java/com/google/cloud/examples/compute/ComputeExample.java +++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/compute/ComputeExample.java @@ -83,13 +83,9 @@ * *

        This example demonstrates a simple/typical Compute usage. * - *

        Steps needed for running the example: - *

          - *
        1. login using gcloud SDK - {@code gcloud auth login}.
        2. - *
        3. compile using maven - {@code mvn compile}
        4. - *
        5. run using maven - - *
          {@code mvn exec:java -Dexec.mainClass="com.google.cloud.examples.compute.ComputeExample"
          - *  -Dexec.args="[]
          + * 

          See the README for the steps needed for compiling and running the example. + *

          Possible command-line arguments are: + *

          {@code []
            * list networks |
            * list region-operations  |
            * list instances ? |
          @@ -152,8 +148,6 @@
            * reset   |
            * set-tags   * |
            * set-metadata   *"}
          - *
        6. - *
        * *

        The first parameter is an optional {@code project_id} (logged-in project will be used if not * supplied). Second parameter is a Compute operation and can be used to demonstrate its usage. For diff --git a/gcloud-java-examples/src/main/java/com/google/cloud/examples/dns/DnsExample.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/dns/DnsExample.java index 8b3c91cbd664..5b920902091d 100644 --- a/gcloud-java-examples/src/main/java/com/google/cloud/examples/dns/DnsExample.java +++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/dns/DnsExample.java @@ -42,21 +42,16 @@ *

        This example creates, deletes, gets, and lists zones. It also creates and deletes * record sets of type A, and lists record sets. * - *

        Steps needed for running the example: - *

          - *
        1. login using gcloud SDK - {@code gcloud auth login}.
        2. - *
        3. compile using maven - {@code mvn compile}
        4. - *
        5. run using maven - {@code mvn exec:java - * -Dexec.mainClass="com.google.cloud.examples.dns.DnsExample" - * -Dexec.args="[] + *

          See the README for steps needed for running the example. + *

          The command-line arguments can be: + *

          {@code []
            * create    |
            * get  |
            * delete  |
            * list [ [changes [descending | ascending] | records]] |
            * add-record     |
            * delete-record    [] |
          - * quota}
        6. - *
        + * quota}
        * *

        The first parameter is an optional {@code project_id}. The project specified in the Google * Cloud SDK configuration (see {@code gcloud config list}) will be used if the project ID is not From 312565c38c47fbf5f1b5e26a46be2546056773fd Mon Sep 17 00:00:00 2001 From: JP Martin Date: Sun, 19 Jun 2016 23:31:34 -0700 Subject: [PATCH 25/38] File system close declares it throws IOException (#1060) --- .../contrib/nio/CloudStorageFileSystem.java | 6 +- .../CloudStorageFileSystemProviderTest.java | 4 +- .../contrib/nio/CloudStoragePathTest.java | 92 +++++++++---------- 3 files changed, 52 insertions(+), 50 deletions(-) diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystem.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystem.java index 1dcf61666f30..c6f5d0c4877c 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystem.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystem.java @@ -158,10 +158,12 @@ public CloudStoragePath getPath(String first, String... more) { * Does nothing. */ @Override - public void close() {} + public void close() throws IOException { + // TODO(#809): Synchronously close all channels associated with this FileSystem instance. + } /** - * Returns {@code true}. + * Returns {@code true}, even if you previously called the {@link #close()} method. */ @Override public boolean isOpen() { diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProviderTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProviderTest.java index e1dfe7bceebd..84d22ce3f1fc 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProviderTest.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProviderTest.java @@ -391,7 +391,7 @@ public void testExists_trailingSlash() { } @Test - public void testExists_trailingSlash_disablePseudoDirectories() { + public void testExists_trailingSlash_disablePseudoDirectories() throws IOException { try (CloudStorageFileSystem fs = forBucket("military", usePseudoDirectories(false))) { assertThat(Files.exists(fs.getPath("fashion/"))).isFalse(); } @@ -547,7 +547,7 @@ public void testIsDirectory_trailingSlash_alwaysTrue() { } @Test - public void testIsDirectory_trailingSlash_pseudoDirectoriesDisabled_false() { + public void testIsDirectory_trailingSlash_pseudoDirectoriesDisabled_false() throws IOException { try (CloudStorageFileSystem fs = forBucket("doodle", usePseudoDirectories(false))) { assertThat(Files.isDirectory(fs.getPath("fundir/"))).isFalse(); } diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStoragePathTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStoragePathTest.java index 852aca4d07a2..a7a27bb5adc1 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStoragePathTest.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStoragePathTest.java @@ -51,7 +51,7 @@ public void before() { } @Test - public void testCreate_neverRemoveExtraSlashes() { + public void testCreate_neverRemoveExtraSlashes() throws IOException { try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle")) { assertThat(fs.getPath("lol//cat").toString()).isEqualTo("lol//cat"); assertThat((Object) fs.getPath("lol//cat")).isEqualTo(fs.getPath("lol//cat")); @@ -59,7 +59,7 @@ public void testCreate_neverRemoveExtraSlashes() { } @Test - public void testCreate_preservesTrailingSlash() { + public void testCreate_preservesTrailingSlash() throws IOException { try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle")) { assertThat(fs.getPath("lol/cat/").toString()).isEqualTo("lol/cat/"); assertThat((Object) fs.getPath("lol/cat/")).isEqualTo(fs.getPath("lol/cat/")); @@ -67,7 +67,7 @@ public void testCreate_preservesTrailingSlash() { } @Test - public void testGetGcsFilename_empty_notAllowed() { + public void testGetGcsFilename_empty_notAllowed() throws IOException { try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle")) { thrown.expect(IllegalArgumentException.class); fs.getPath("").getBlobId(); @@ -75,21 +75,21 @@ public void testGetGcsFilename_empty_notAllowed() { } @Test - public void testGetGcsFilename_stripsPrefixSlash() { + public void testGetGcsFilename_stripsPrefixSlash() throws IOException { try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle")) { assertThat(fs.getPath("/hi").getBlobId().name()).isEqualTo("hi"); } } @Test - public void testGetGcsFilename_overrideStripPrefixSlash_doesntStripPrefixSlash() { + public void testGetGcsFilename_overrideStripPrefixSlash_doesntStripPrefixSlash() throws IOException { try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle", stripPrefixSlash(false))) { assertThat(fs.getPath("/hi").getBlobId().name()).isEqualTo("/hi"); } } @Test - public void testGetGcsFilename_extraSlashes_throwsIae() { + public void testGetGcsFilename_extraSlashes_throwsIae() throws IOException { try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle")) { thrown.expect(IllegalArgumentException.class); fs.getPath("a//b").getBlobId().name(); @@ -97,14 +97,14 @@ public void testGetGcsFilename_extraSlashes_throwsIae() { } @Test - public void testGetGcsFilename_overridepermitEmptyPathComponents() { + public void testGetGcsFilename_overridepermitEmptyPathComponents() throws IOException { try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle", permitEmptyPathComponents(true))) { assertThat(fs.getPath("a//b").getBlobId().name()).isEqualTo("a//b"); } } @Test - public void testGetGcsFilename_freaksOutOnExtraSlashesAndDotDirs() { + public void testGetGcsFilename_freaksOutOnExtraSlashesAndDotDirs() throws IOException { try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle")) { thrown.expect(IllegalArgumentException.class); fs.getPath("a//b/..").getBlobId().name(); @@ -112,7 +112,7 @@ public void testGetGcsFilename_freaksOutOnExtraSlashesAndDotDirs() { } @Test - public void testNameCount() { + public void testNameCount() throws IOException { try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle")) { assertThat(fs.getPath("").getNameCount()).isEqualTo(1); assertThat(fs.getPath("/").getNameCount()).isEqualTo(0); @@ -123,7 +123,7 @@ public void testNameCount() { } @Test - public void testGetName() { + public void testGetName() throws IOException { try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle")) { assertThat(fs.getPath("").getName(0).toString()).isEqualTo(""); assertThat(fs.getPath("/hi").getName(0).toString()).isEqualTo("hi"); @@ -132,7 +132,7 @@ public void testGetName() { } @Test - public void testGetName_negative_throwsIae() { + public void testGetName_negative_throwsIae() throws IOException { try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle")) { thrown.expect(IllegalArgumentException.class); fs.getPath("angel").getName(-1); @@ -140,7 +140,7 @@ public void testGetName_negative_throwsIae() { } @Test - public void testGetName_overflow_throwsIae() { + public void testGetName_overflow_throwsIae() throws IOException { try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle")) { thrown.expect(IllegalArgumentException.class); fs.getPath("angel").getName(1); @@ -148,7 +148,7 @@ public void testGetName_overflow_throwsIae() { } @Test - public void testIterator() { + public void testIterator() throws IOException { try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle")) { assertThat(Iterables.get(fs.getPath("/dog/mog"), 0).toString()).isEqualTo("dog"); assertThat(Iterables.get(fs.getPath("/dog/mog"), 1).toString()).isEqualTo("mog"); @@ -159,7 +159,7 @@ public void testIterator() { } @Test - public void testNormalize() { + public void testNormalize() throws IOException { try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle")) { assertThat(fs.getPath("/").normalize().toString()).isEqualTo("/"); assertThat(fs.getPath("a/x/../b/x/..").normalize().toString()).isEqualTo("a/b/"); @@ -169,7 +169,7 @@ public void testNormalize() { } @Test - public void testNormalize_dot_becomesBlank() { + public void testNormalize_dot_becomesBlank() throws IOException { try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle")) { assertThat(fs.getPath("").normalize().toString()).isEqualTo(""); assertThat(fs.getPath(".").normalize().toString()).isEqualTo(""); @@ -177,14 +177,14 @@ public void testNormalize_dot_becomesBlank() { } @Test - public void testNormalize_trailingSlash_isPreserved() { + public void testNormalize_trailingSlash_isPreserved() throws IOException { try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle")) { assertThat(fs.getPath("o/").normalize().toString()).isEqualTo("o/"); } } @Test - public void testNormalize_doubleDot_becomesBlank() { + public void testNormalize_doubleDot_becomesBlank() throws IOException { try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle")) { assertThat(fs.getPath("..").normalize().toString()).isEqualTo(""); assertThat(fs.getPath("../..").normalize().toString()).isEqualTo(""); @@ -192,14 +192,14 @@ public void testNormalize_doubleDot_becomesBlank() { } @Test - public void testNormalize_extraSlashes_getRemoved() { + public void testNormalize_extraSlashes_getRemoved() throws IOException { try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle")) { assertThat(fs.getPath("//life///b/good//").normalize().toString()).isEqualTo("/life/b/good/"); } } @Test - public void testToRealPath_hasDotDir_throwsIae() { + public void testToRealPath_hasDotDir_throwsIae() throws IOException { try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle")) { fs.getPath("a/hi./b").toRealPath(); fs.getPath("a/.hi/b").toRealPath(); @@ -210,7 +210,7 @@ public void testToRealPath_hasDotDir_throwsIae() { } @Test - public void testToRealPath_hasDotDotDir_throwsIae() { + public void testToRealPath_hasDotDotDir_throwsIae() throws IOException { try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle")) { fs.getPath("a/hi../b").toRealPath(); fs.getPath("a/..hi/b").toRealPath(); @@ -221,7 +221,7 @@ public void testToRealPath_hasDotDotDir_throwsIae() { } @Test - public void testToRealPath_extraSlashes_throwsIae() { + public void testToRealPath_extraSlashes_throwsIae() throws IOException { try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle")) { thrown.expect(IllegalArgumentException.class); thrown.expectMessage("extra slashes"); @@ -230,7 +230,7 @@ public void testToRealPath_extraSlashes_throwsIae() { } @Test - public void testToRealPath_overridePermitEmptyPathComponents_extraSlashes_slashesRemain() { + public void testToRealPath_overridePermitEmptyPathComponents_extraSlashes_slashesRemain() throws IOException { try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle", permitEmptyPathComponents(true))) { assertThat(fs.getPath("/life///b/./good/").toRealPath().toString()) .isEqualTo("life///b/./good/"); @@ -238,7 +238,7 @@ public void testToRealPath_overridePermitEmptyPathComponents_extraSlashes_slashe } @Test - public void testToRealPath_permitEmptyPathComponents_doesNotNormalize() { + public void testToRealPath_permitEmptyPathComponents_doesNotNormalize() throws IOException { try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle", permitEmptyPathComponents(true))) { assertThat(fs.getPath("a").toRealPath().toString()).isEqualTo("a"); assertThat(fs.getPath("a//b").toRealPath().toString()).isEqualTo("a//b"); @@ -247,14 +247,14 @@ public void testToRealPath_permitEmptyPathComponents_doesNotNormalize() { } @Test - public void testToRealPath_withWorkingDirectory_makesAbsolute() { + public void testToRealPath_withWorkingDirectory_makesAbsolute() throws IOException { try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle", workingDirectory("/lol"))) { assertThat(fs.getPath("a").toRealPath().toString()).isEqualTo("lol/a"); } } @Test - public void testToRealPath_disableStripPrefixSlash_makesPathAbsolute() { + public void testToRealPath_disableStripPrefixSlash_makesPathAbsolute() throws IOException { try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle", stripPrefixSlash(false))) { assertThat(fs.getPath("a").toRealPath().toString()).isEqualTo("/a"); assertThat(fs.getPath("/a").toRealPath().toString()).isEqualTo("/a"); @@ -262,21 +262,21 @@ public void testToRealPath_disableStripPrefixSlash_makesPathAbsolute() { } @Test - public void testToRealPath_trailingSlash_getsPreserved() { + public void testToRealPath_trailingSlash_getsPreserved() throws IOException { try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle")) { assertThat(fs.getPath("a/b/").toRealPath().toString()).isEqualTo("a/b/"); } } @Test - public void testNormalize_empty_returnsEmpty() { + public void testNormalize_empty_returnsEmpty() throws IOException { try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle")) { assertThat(fs.getPath("").normalize().toString()).isEqualTo(""); } } @Test - public void testNormalize_preserveTrailingSlash() { + public void testNormalize_preserveTrailingSlash() throws IOException { try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle")) { assertThat(fs.getPath("a/b/../c/").normalize().toString()).isEqualTo("a/c/"); assertThat(fs.getPath("a/b/./c/").normalize().toString()).isEqualTo("a/b/c/"); @@ -284,7 +284,7 @@ public void testNormalize_preserveTrailingSlash() { } @Test - public void testGetParent_preserveTrailingSlash() { + public void testGetParent_preserveTrailingSlash() throws IOException { try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle")) { assertThat(fs.getPath("a/b/c").getParent().toString()).isEqualTo("a/b/"); assertThat(fs.getPath("a/b/c/").getParent().toString()).isEqualTo("a/b/"); @@ -296,7 +296,7 @@ public void testGetParent_preserveTrailingSlash() { } @Test - public void testGetRoot() { + public void testGetRoot() throws IOException { try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle")) { assertThat(fs.getPath("/hello").getRoot().toString()).isEqualTo("/"); assertThat((Object) fs.getPath("hello").getRoot()).isNull(); @@ -304,7 +304,7 @@ public void testGetRoot() { } @Test - public void testRelativize() { + public void testRelativize() throws IOException { try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle")) { assertThat( fs.getPath("/foo/bar/lol/cat").relativize(fs.getPath("/foo/a/b/../../c")).toString()) @@ -313,7 +313,7 @@ public void testRelativize() { } @Test - public void testRelativize_providerMismatch() { + public void testRelativize_providerMismatch() throws IOException { try (CloudStorageFileSystem gcs = CloudStorageFileSystem.forBucket("doodle")) { thrown.expect(ProviderMismatchException.class); gcs.getPath("/etc").relativize(FileSystems.getDefault().getPath("/dog")); @@ -322,7 +322,7 @@ public void testRelativize_providerMismatch() { @Test @SuppressWarnings("ReturnValueIgnored") // testing that an Exception is thrown - public void testRelativize_providerMismatch2() { + public void testRelativize_providerMismatch2() throws IOException { try (CloudStorageFileSystem gcs = CloudStorageFileSystem.forBucket("doodle")) { thrown.expect(ProviderMismatchException.class); gcs.getPath("/dog").relativize(FileSystems.getDefault().getPath("/etc")); @@ -330,7 +330,7 @@ public void testRelativize_providerMismatch2() { } @Test - public void testResolve() { + public void testResolve() throws IOException { try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle")) { assertThat(fs.getPath("/hi").resolve("there").toString()).isEqualTo("/hi/there"); assertThat(fs.getPath("hi").resolve("there").toString()).isEqualTo("hi/there"); @@ -338,7 +338,7 @@ public void testResolve() { } @Test - public void testResolve_providerMismatch() { + public void testResolve_providerMismatch() throws IOException { try (CloudStorageFileSystem gcs = CloudStorageFileSystem.forBucket("doodle")) { thrown.expect(ProviderMismatchException.class); gcs.getPath("etc").resolve(FileSystems.getDefault().getPath("/dog")); @@ -346,7 +346,7 @@ public void testResolve_providerMismatch() { } @Test - public void testIsAbsolute() { + public void testIsAbsolute() throws IOException { try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle")) { assertThat(fs.getPath("/hi").isAbsolute()).isTrue(); assertThat(fs.getPath("hi").isAbsolute()).isFalse(); @@ -354,7 +354,7 @@ public void testIsAbsolute() { } @Test - public void testToAbsolutePath() { + public void testToAbsolutePath() throws IOException { try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle")) { assertThat((Object) fs.getPath("/hi").toAbsolutePath()).isEqualTo(fs.getPath("/hi")); assertThat((Object) fs.getPath("hi").toAbsolutePath()).isEqualTo(fs.getPath("/hi")); @@ -362,14 +362,14 @@ public void testToAbsolutePath() { } @Test - public void testToAbsolutePath_withWorkingDirectory() { + public void testToAbsolutePath_withWorkingDirectory() throws IOException { try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle", workingDirectory("/lol"))) { assertThat(fs.getPath("a").toAbsolutePath().toString()).isEqualTo("/lol/a"); } } @Test - public void testGetFileName() { + public void testGetFileName() throws IOException { try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle")) { assertThat(fs.getPath("/hi/there").getFileName().toString()).isEqualTo("there"); assertThat(fs.getPath("military/fashion/show").getFileName().toString()).isEqualTo("show"); @@ -377,7 +377,7 @@ public void testGetFileName() { } @Test - public void testCompareTo() { + public void testCompareTo() throws IOException { try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle")) { assertThat(fs.getPath("/hi/there").compareTo(fs.getPath("/hi/there"))).isEqualTo(0); assertThat(fs.getPath("/hi/there").compareTo(fs.getPath("/hi/therf"))).isEqualTo(-1); @@ -386,7 +386,7 @@ public void testCompareTo() { } @Test - public void testStartsWith() { + public void testStartsWith() throws IOException { try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle")) { assertThat(fs.getPath("/hi/there").startsWith(fs.getPath("/hi/there"))).isTrue(); assertThat(fs.getPath("/hi/there").startsWith(fs.getPath("/hi/therf"))).isFalse(); @@ -399,7 +399,7 @@ public void testStartsWith() { } @Test - public void testEndsWith() { + public void testEndsWith() throws IOException { try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle")) { assertThat(fs.getPath("/hi/there").endsWith(fs.getPath("there"))).isTrue(); assertThat(fs.getPath("/hi/there").endsWith(fs.getPath("therf"))).isFalse(); @@ -440,7 +440,7 @@ public void testRelativize_willWorkWithRecursiveCopy() throws IOException { } @Test - public void testToFile_unsupported() { + public void testToFile_unsupported() throws IOException { try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle")) { Path path = fs.getPath("/lol"); thrown.expect(UnsupportedOperationException.class); @@ -449,7 +449,7 @@ public void testToFile_unsupported() { } @Test - public void testEquals() { + public void testEquals() throws IOException { try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle")) { new EqualsTester() // These are obviously equal. @@ -465,7 +465,7 @@ public void testEquals() { } @Test - public void testEquals_currentDirectoryIsTakenIntoConsideration() { + public void testEquals_currentDirectoryIsTakenIntoConsideration() throws IOException { try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle", workingDirectory("/hello"))) { new EqualsTester() .addEqualityGroup(fs.getPath("cat"), fs.getPath("/hello/cat")) @@ -475,7 +475,7 @@ public void testEquals_currentDirectoryIsTakenIntoConsideration() { } @Test - public void testNullness() throws NoSuchMethodException, SecurityException { + public void testNullness() throws IOException, NoSuchMethodException, SecurityException { try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("doodle")) { NullPointerTester tester = new NullPointerTester(); tester.ignore(CloudStoragePath.class.getMethod("equals", Object.class)); From 37fe1ebbf5209727bea3720d0e286ff23e10f8b6 Mon Sep 17 00:00:00 2001 From: JP Martin Date: Tue, 21 Jun 2016 10:33:58 -0700 Subject: [PATCH 26/38] Handle files over 2GB (#1065) * Handle files over 2GB * Use Guava's checkArgument --- .../storage/contrib/nio/CloudStorageReadChannel.java | 2 +- .../src/main/java/com/google/cloud/ReadChannel.java | 2 +- .../google/cloud/examples/nio/ParallelCountBytes.java | 3 ++- .../java/com/google/cloud/storage/BlobReadChannel.java | 10 +++++----- .../google/cloud/storage/spi/DefaultStorageRpc.java | 4 +++- 5 files changed, 12 insertions(+), 9 deletions(-) diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageReadChannel.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageReadChannel.java index 5b5e2b649e0d..bb03e53fc362 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageReadChannel.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageReadChannel.java @@ -118,7 +118,7 @@ public SeekableByteChannel position(long newPosition) throws IOException { if (newPosition == position) { return this; } - channel.seek((int) newPosition); + channel.seek(newPosition); position = newPosition; return this; } diff --git a/gcloud-java-core/src/main/java/com/google/cloud/ReadChannel.java b/gcloud-java-core/src/main/java/com/google/cloud/ReadChannel.java index 2afb8b2d5b32..1ac45902b27d 100644 --- a/gcloud-java-core/src/main/java/com/google/cloud/ReadChannel.java +++ b/gcloud-java-core/src/main/java/com/google/cloud/ReadChannel.java @@ -38,7 +38,7 @@ public interface ReadChannel extends ReadableByteChannel, Closeable, Restorable< @Override void close(); - void seek(int position) throws IOException; + void seek(long position) throws IOException; /** * Sets the minimum size that will be read by a single RPC. diff --git a/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/ParallelCountBytes.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/ParallelCountBytes.java index f2b0959acee8..0f268ddb72ca 100644 --- a/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/ParallelCountBytes.java +++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/ParallelCountBytes.java @@ -74,7 +74,7 @@ public WorkUnit(SeekableByteChannel chan, int blockSize, int blockIndex) { @Override public WorkUnit call() throws IOException { - int pos = blockSize * blockIndex; + long pos = ((long)blockSize) * blockIndex; if (pos > chan.size()) { return this; } @@ -90,6 +90,7 @@ public WorkUnit resetForIndex(int blockIndex) { return this; } + public void close() throws IOException { chan.close(); } diff --git a/gcloud-java-storage/src/main/java/com/google/cloud/storage/BlobReadChannel.java b/gcloud-java-storage/src/main/java/com/google/cloud/storage/BlobReadChannel.java index 0352e8a9d550..04b085574844 100644 --- a/gcloud-java-storage/src/main/java/com/google/cloud/storage/BlobReadChannel.java +++ b/gcloud-java-storage/src/main/java/com/google/cloud/storage/BlobReadChannel.java @@ -45,7 +45,7 @@ class BlobReadChannel implements ReadChannel { private final BlobId blob; private final Map requestOptions; private String lastEtag; - private int position; + private long position; private boolean isOpen; private boolean endOfStream; private int chunkSize = DEFAULT_CHUNK_SIZE; @@ -99,7 +99,7 @@ private void validateOpen() throws ClosedChannelException { } @Override - public void seek(int position) throws IOException { + public void seek(long position) throws IOException { validateOpen(); this.position = position; buffer = null; @@ -164,7 +164,7 @@ static class StateImpl implements RestorableState, Serializable { private final BlobId blob; private final Map requestOptions; private final String lastEtag; - private final int position; + private final long position; private final boolean isOpen; private final boolean endOfStream; private final int chunkSize; @@ -185,7 +185,7 @@ static class Builder { private final BlobId blob; private final Map requestOptions; private String lastEtag; - private int position; + private long position; private boolean isOpen; private boolean endOfStream; private int chunkSize; @@ -201,7 +201,7 @@ Builder lastEtag(String lastEtag) { return this; } - Builder position(int position) { + Builder position(long position) { this.position = position; return this; } diff --git a/gcloud-java-storage/src/main/java/com/google/cloud/storage/spi/DefaultStorageRpc.java b/gcloud-java-storage/src/main/java/com/google/cloud/storage/spi/DefaultStorageRpc.java index 65d540458c7c..883ce80003bb 100644 --- a/gcloud-java-storage/src/main/java/com/google/cloud/storage/spi/DefaultStorageRpc.java +++ b/gcloud-java-storage/src/main/java/com/google/cloud/storage/spi/DefaultStorageRpc.java @@ -31,6 +31,7 @@ import static com.google.cloud.storage.spi.StorageRpc.Option.PREFIX; import static com.google.cloud.storage.spi.StorageRpc.Option.VERSIONS; import static com.google.common.base.MoreObjects.firstNonNull; +import static com.google.common.base.Preconditions.checkArgument; import static java.net.HttpURLConnection.HTTP_NOT_FOUND; import static javax.servlet.http.HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE; @@ -467,10 +468,11 @@ public Tuple read(StorageObject from, Map options, lo .setIfMetagenerationNotMatch(IF_METAGENERATION_NOT_MATCH.getLong(options)) .setIfGenerationMatch(IF_GENERATION_MATCH.getLong(options)) .setIfGenerationNotMatch(IF_GENERATION_NOT_MATCH.getLong(options)); + checkArgument(position >= 0, "Position should be non-negative, is %d", position); StringBuilder range = new StringBuilder(); range.append("bytes=").append(position).append("-").append(position + bytes - 1); req.getRequestHeaders().setRange(range.toString()); - ByteArrayOutputStream output = new ByteArrayOutputStream(); + ByteArrayOutputStream output = new ByteArrayOutputStream(bytes); req.executeMedia().download(output); String etag = req.getLastResponseHeaders().getETag(); return Tuple.of(etag, output.toByteArray()); From 595c16af83e14981ea920d4c7a5e3be4758e8808 Mon Sep 17 00:00:00 2001 From: JP Martin Date: Tue, 21 Jun 2016 11:27:20 -0700 Subject: [PATCH 27/38] Rename LocalGcsHelper to LocalStorageHelper --- .../contrib/nio/CloudStorageFileAttributeViewTest.java | 4 ++-- .../contrib/nio/CloudStorageFileAttributesTest.java | 4 ++-- .../contrib/nio/CloudStorageFileSystemProviderTest.java | 4 ++-- .../storage/contrib/nio/CloudStorageFileSystemTest.java | 8 +++++--- .../storage/contrib/nio/CloudStorageOptionsTest.java | 4 ++-- .../cloud/storage/contrib/nio/CloudStoragePathTest.java | 4 ++-- .../{LocalGcsHelper.java => LocalStorageHelper.java} | 2 +- 7 files changed, 16 insertions(+), 14 deletions(-) rename gcloud-java-storage/src/main/java/com/google/cloud/storage/testing/{LocalGcsHelper.java => LocalStorageHelper.java} (98%) diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributeViewTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributeViewTest.java index e85da0446c6b..d48594e80a16 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributeViewTest.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributeViewTest.java @@ -19,7 +19,7 @@ import static com.google.common.truth.Truth.assertThat; import static java.nio.charset.StandardCharsets.UTF_8; -import com.google.cloud.storage.testing.LocalGcsHelper; +import com.google.cloud.storage.testing.LocalStorageHelper; import com.google.common.testing.EqualsTester; import com.google.common.testing.NullPointerTester; @@ -52,7 +52,7 @@ public class CloudStorageFileAttributeViewTest { @Before public void before() { - CloudStorageFileSystemProvider.setGCloudOptions(LocalGcsHelper.options()); + CloudStorageFileSystemProvider.setGCloudOptions(LocalStorageHelper.options()); path = Paths.get(URI.create("gs://red/water")); } diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributesTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributesTest.java index 4b64e96da3cb..1ce61e9257b2 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributesTest.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributesTest.java @@ -20,7 +20,7 @@ import static java.nio.charset.StandardCharsets.UTF_8; import com.google.cloud.storage.Acl; -import com.google.cloud.storage.testing.LocalGcsHelper; +import com.google.cloud.storage.testing.LocalStorageHelper; import com.google.common.testing.EqualsTester; import com.google.common.testing.NullPointerTester; @@ -48,7 +48,7 @@ public class CloudStorageFileAttributesTest { @Before public void before() { - CloudStorageFileSystemProvider.setGCloudOptions(LocalGcsHelper.options()); + CloudStorageFileSystemProvider.setGCloudOptions(LocalStorageHelper.options()); path = Paths.get(URI.create("gs://bucket/randompath")); dir = Paths.get(URI.create("gs://bucket/randompath/")); } diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProviderTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProviderTest.java index 84d22ce3f1fc..9c191a4f5882 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProviderTest.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProviderTest.java @@ -26,7 +26,7 @@ import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING; import static java.nio.file.StandardOpenOption.WRITE; -import com.google.cloud.storage.testing.LocalGcsHelper; +import com.google.cloud.storage.testing.LocalStorageHelper; import com.google.common.collect.ImmutableList; import com.google.common.testing.NullPointerTester; @@ -91,7 +91,7 @@ public class CloudStorageFileSystemProviderTest { @Before public void before() { - CloudStorageFileSystemProvider.setGCloudOptions(LocalGcsHelper.options()); + CloudStorageFileSystemProvider.setGCloudOptions(LocalStorageHelper.options()); } @Test diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemTest.java index bfb3a5bc7be9..dbba88967773 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemTest.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemTest.java @@ -19,7 +19,8 @@ import static com.google.common.truth.Truth.assertThat; import static java.nio.charset.StandardCharsets.UTF_8; -import com.google.cloud.storage.testing.LocalGcsHelper; +import com.google.cloud.storage.StorageOptions; +import com.google.cloud.storage.testing.LocalStorageHelper; import com.google.common.testing.EqualsTester; import com.google.common.testing.NullPointerTester; @@ -56,7 +57,7 @@ public class CloudStorageFileSystemTest { @Before public void before() { - CloudStorageFileSystemProvider.setGCloudOptions(LocalGcsHelper.options()); + CloudStorageFileSystemProvider.setGCloudOptions(LocalStorageHelper.options()); } @Test @@ -130,7 +131,8 @@ public void testNullness() throws IOException, NoSuchMethodException, SecurityEx NullPointerTester tester = new NullPointerTester() .ignore(CloudStorageFileSystem.class.getMethod("equals", Object.class)) - .setDefault(CloudStorageConfiguration.class, CloudStorageConfiguration.DEFAULT); + .setDefault(CloudStorageConfiguration.class, CloudStorageConfiguration.DEFAULT) + .setDefault(StorageOptions.class, LocalStorageHelper.options()); tester.testAllPublicStaticMethods(CloudStorageFileSystem.class); tester.testAllPublicInstanceMethods(fs); } diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageOptionsTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageOptionsTest.java index ddf5b46f0c17..b18aae93ba27 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageOptionsTest.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageOptionsTest.java @@ -20,7 +20,7 @@ import static java.nio.charset.StandardCharsets.UTF_8; import com.google.cloud.storage.Acl; -import com.google.cloud.storage.testing.LocalGcsHelper; +import com.google.cloud.storage.testing.LocalStorageHelper; import com.google.common.testing.NullPointerTester; import org.junit.Before; @@ -42,7 +42,7 @@ public class CloudStorageOptionsTest { @Before public void before() { - CloudStorageFileSystemProvider.setGCloudOptions(LocalGcsHelper.options()); + CloudStorageFileSystemProvider.setGCloudOptions(LocalStorageHelper.options()); } @Test diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStoragePathTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStoragePathTest.java index a7a27bb5adc1..fe65c6927665 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStoragePathTest.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStoragePathTest.java @@ -18,7 +18,7 @@ import static com.google.common.truth.Truth.assertThat; -import com.google.cloud.storage.testing.LocalGcsHelper; +import com.google.cloud.storage.testing.LocalStorageHelper; import com.google.common.collect.Iterables; import com.google.common.testing.EqualsTester; import com.google.common.testing.NullPointerTester; @@ -47,7 +47,7 @@ public class CloudStoragePathTest { @Before public void before() { - CloudStorageFileSystemProvider.setGCloudOptions(LocalGcsHelper.options()); + CloudStorageFileSystemProvider.setGCloudOptions(LocalStorageHelper.options()); } @Test diff --git a/gcloud-java-storage/src/main/java/com/google/cloud/storage/testing/LocalGcsHelper.java b/gcloud-java-storage/src/main/java/com/google/cloud/storage/testing/LocalStorageHelper.java similarity index 98% rename from gcloud-java-storage/src/main/java/com/google/cloud/storage/testing/LocalGcsHelper.java rename to gcloud-java-storage/src/main/java/com/google/cloud/storage/testing/LocalStorageHelper.java index deb12c896511..7797cb7d91f6 100644 --- a/gcloud-java-storage/src/main/java/com/google/cloud/storage/testing/LocalGcsHelper.java +++ b/gcloud-java-storage/src/main/java/com/google/cloud/storage/testing/LocalStorageHelper.java @@ -24,7 +24,7 @@ * Utility to create an in-memory storage configuration for testing. Storage options can be * obtained via the {@link #options()} method. Returned options will point to FakeStorageRpc. */ -public class LocalGcsHelper { +public class LocalStorageHelper { // used for testing. Will throw if you pass it an option. private static final FakeStorageRpc instance = new FakeStorageRpc(true); From 1e6b5aa681d5077d0189a72d3853bfdb4b3f8767 Mon Sep 17 00:00:00 2001 From: JP Martin Date: Tue, 21 Jun 2016 13:07:30 -0700 Subject: [PATCH 28/38] Document limitations of FakeStorageRpc (#1069) --- .../cloud/storage/testing/FakeStorageRpc.java | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/gcloud-java-storage/src/main/java/com/google/cloud/storage/testing/FakeStorageRpc.java b/gcloud-java-storage/src/main/java/com/google/cloud/storage/testing/FakeStorageRpc.java index a07f5dc26c4c..0639708f31f3 100644 --- a/gcloud-java-storage/src/main/java/com/google/cloud/storage/testing/FakeStorageRpc.java +++ b/gcloud-java-storage/src/main/java/com/google/cloud/storage/testing/FakeStorageRpc.java @@ -37,9 +37,24 @@ /** * A bare-bones in-memory implementation of Storage, meant for testing. - * See LocalGcsHelper. * - * This class is NOT thread-safe. + *

        This class is not thread-safe. It's also (currently) limited in the following ways: + *

          + *
        • Partially Supported + *
            + *
          • create + *
          • get + *
          • delete + *
          • directory listing + *
          + *
        • Unsupported + *
            + *
          • generations + *
          • file attributes + *
          • patch + *
          • continueRewrite + *
          + *
        */ @NotThreadSafe public class FakeStorageRpc implements StorageRpc { From 39ba0778a43b6b91439465c0a336e59e6d5c8923 Mon Sep 17 00:00:00 2001 From: JP Martin Date: Tue, 21 Jun 2016 13:59:54 -0700 Subject: [PATCH 29/38] More precise description of FakeStorageRpc limitations (#1070) --- .../cloud/storage/testing/FakeStorageRpc.java | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/gcloud-java-storage/src/main/java/com/google/cloud/storage/testing/FakeStorageRpc.java b/gcloud-java-storage/src/main/java/com/google/cloud/storage/testing/FakeStorageRpc.java index 0639708f31f3..dc1e28d56826 100644 --- a/gcloud-java-storage/src/main/java/com/google/cloud/storage/testing/FakeStorageRpc.java +++ b/gcloud-java-storage/src/main/java/com/google/cloud/storage/testing/FakeStorageRpc.java @@ -40,19 +40,24 @@ * *

        This class is not thread-safe. It's also (currently) limited in the following ways: *

          - *
        • Partially Supported + *
        • Supported *
            - *
          • create - *
          • get - *
          • delete - *
          • directory listing + *
          • object create + *
          • object get + *
          • object delete + *
          • list the contents of a bucket *
          *
        • Unsupported *
            + *
          • bucket create + *
          • bucket get + *
          • bucket delete + *
          • list all buckets *
          • generations *
          • file attributes *
          • patch *
          • continueRewrite + *
          • createBatch *
          *
        */ From 5917ae7e96edd45dc9cdd67cd646de1f0b374dac Mon Sep 17 00:00:00 2001 From: JP Martin Date: Thu, 23 Jun 2016 09:24:47 -0700 Subject: [PATCH 30/38] Make sure every java file starts with the Apache license. (#1067) --- .../com/google/cloud/bigquery/BigQueryError.java | 16 ++++++++++++++++ .../com/google/cloud/bigquery/JobStatistics.java | 16 ++++++++++++++++ .../com/google/cloud/bigquery/JobStatus.java | 16 ++++++++++++++++ .../cloud/bigquery/UserDefinedFunction.java | 16 ++++++++++++++++ .../google/cloud/bigquery/BigQueryErrorTest.java | 16 ++++++++++++++++ .../cloud/storage/contrib/nio/it/ITGcsNio.java | 16 ++++++++++++++++ .../spi/DefaultResourceManagerRpc.java | 16 ++++++++++++++++ .../testing/LocalResourceManagerHelper.java | 16 ++++++++++++++++ .../testing/LocalResourceManagerHelperTest.java | 16 ++++++++++++++++ 9 files changed, 144 insertions(+) diff --git a/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryError.java b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryError.java index 121ca578c0d3..b243cf8b1f99 100644 --- a/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryError.java +++ b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryError.java @@ -1,3 +1,19 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * 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 com.google.cloud.bigquery; import com.google.api.services.bigquery.model.ErrorProto; diff --git a/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/JobStatistics.java b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/JobStatistics.java index 90ad164a7ce5..84f882fb2c53 100644 --- a/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/JobStatistics.java +++ b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/JobStatistics.java @@ -1,3 +1,19 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * 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 com.google.cloud.bigquery; import com.google.api.services.bigquery.model.JobStatistics2; diff --git a/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/JobStatus.java b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/JobStatus.java index 7c948e6373f9..b2bfe99a503d 100644 --- a/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/JobStatus.java +++ b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/JobStatus.java @@ -1,3 +1,19 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * 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 com.google.cloud.bigquery; import com.google.common.base.MoreObjects; diff --git a/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/UserDefinedFunction.java b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/UserDefinedFunction.java index 09fa2563c59f..36ac2c27485e 100644 --- a/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/UserDefinedFunction.java +++ b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/UserDefinedFunction.java @@ -1,3 +1,19 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * 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 com.google.cloud.bigquery; import com.google.api.services.bigquery.model.UserDefinedFunctionResource; diff --git a/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/BigQueryErrorTest.java b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/BigQueryErrorTest.java index 1d30bb87815e..c63095b96528 100644 --- a/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/BigQueryErrorTest.java +++ b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/BigQueryErrorTest.java @@ -1,3 +1,19 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * 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 com.google.cloud.bigquery; import static org.junit.Assert.assertEquals; diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/it/ITGcsNio.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/it/ITGcsNio.java index 0f2667ad2cad..3593f0c9ee69 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/it/ITGcsNio.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/it/ITGcsNio.java @@ -1,3 +1,19 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * 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 com.google.cloud.storage.contrib.nio.it; import static com.google.common.truth.Truth.assertThat; diff --git a/gcloud-java-resourcemanager/src/main/java/com/google/cloud/resourcemanager/spi/DefaultResourceManagerRpc.java b/gcloud-java-resourcemanager/src/main/java/com/google/cloud/resourcemanager/spi/DefaultResourceManagerRpc.java index 427478baef5f..43cb40bcd306 100644 --- a/gcloud-java-resourcemanager/src/main/java/com/google/cloud/resourcemanager/spi/DefaultResourceManagerRpc.java +++ b/gcloud-java-resourcemanager/src/main/java/com/google/cloud/resourcemanager/spi/DefaultResourceManagerRpc.java @@ -1,3 +1,19 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * 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 com.google.cloud.resourcemanager.spi; import static com.google.cloud.resourcemanager.spi.ResourceManagerRpc.Option.FIELDS; diff --git a/gcloud-java-resourcemanager/src/main/java/com/google/cloud/resourcemanager/testing/LocalResourceManagerHelper.java b/gcloud-java-resourcemanager/src/main/java/com/google/cloud/resourcemanager/testing/LocalResourceManagerHelper.java index 6c16372c0424..48934d602bfe 100644 --- a/gcloud-java-resourcemanager/src/main/java/com/google/cloud/resourcemanager/testing/LocalResourceManagerHelper.java +++ b/gcloud-java-resourcemanager/src/main/java/com/google/cloud/resourcemanager/testing/LocalResourceManagerHelper.java @@ -1,3 +1,19 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * 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 com.google.cloud.resourcemanager.testing; import static com.google.common.base.Preconditions.checkArgument; diff --git a/gcloud-java-resourcemanager/src/test/java/com/google/cloud/resourcemanager/testing/LocalResourceManagerHelperTest.java b/gcloud-java-resourcemanager/src/test/java/com/google/cloud/resourcemanager/testing/LocalResourceManagerHelperTest.java index cf65b4981bfc..cfe475cb038b 100644 --- a/gcloud-java-resourcemanager/src/test/java/com/google/cloud/resourcemanager/testing/LocalResourceManagerHelperTest.java +++ b/gcloud-java-resourcemanager/src/test/java/com/google/cloud/resourcemanager/testing/LocalResourceManagerHelperTest.java @@ -1,3 +1,19 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * 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 com.google.cloud.resourcemanager.testing; import static org.junit.Assert.assertEquals; From 3b55bfbd56293367c25f7fc83b17f2d527572aae Mon Sep 17 00:00:00 2001 From: JP Martin Date: Fri, 24 Jun 2016 00:25:51 -0700 Subject: [PATCH 31/38] Improve NIO documentation (#1076) Documentation has been added to many methods to clarify the preferred method for instantiating the library, which is the non-SPI version. --- .../nio/CloudStorageConfiguration.java | 7 +++-- .../contrib/nio/CloudStorageFileSystem.java | 15 ++++++++-- .../nio/CloudStorageFileSystemProvider.java | 25 +++++++++++++---- .../storage/contrib/nio/it/ITGcsNio.java | 28 +++++++++---------- .../cloud/storage/testing/FakeStorageRpc.java | 23 +++++++-------- 5 files changed, 60 insertions(+), 38 deletions(-) diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageConfiguration.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageConfiguration.java index b5166c143f62..0c8046ca2277 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageConfiguration.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageConfiguration.java @@ -82,8 +82,9 @@ public static final class Builder { private int blockSize = CloudStorageFileSystem.BLOCK_SIZE_DEFAULT; /** - * Changes current working directory for new filesystem. This cannot be changed once it's - * been set. You'll need to create another {@link CloudStorageFileSystem} object. + * Changes current working directory for new filesystem. This defaults to the root directory. + * The working directory cannot be changed once it's been set. You'll need to create another + * {@link CloudStorageFileSystem} object. * * @throws IllegalArgumentException if {@code path} is not absolute. */ @@ -95,7 +96,7 @@ public Builder workingDirectory(String path) { /** * Configures whether or not we should throw an exception when encountering object names - * containing superfluous slashes, e.g. {@code a//b} + * containing superfluous slashes, e.g. {@code a//b}. */ public Builder permitEmptyPathComponents(boolean value) { permitEmptyPathComponents = value; diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystem.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystem.java index c6f5d0c4877c..650eda1199af 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystem.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystem.java @@ -38,6 +38,8 @@ import javax.annotation.CheckReturnValue; import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; +import javax.annotation.CheckReturnValue; +import javax.annotation.concurrent.ThreadSafe; /** * Google Cloud Storage {@link FileSystem} implementation. @@ -47,7 +49,7 @@ * @see
        * Bucket and Object Naming Guidelines */ -@Immutable +@ThreadSafe public final class CloudStorageFileSystem extends FileSystem { /** @@ -65,6 +67,7 @@ public final class CloudStorageFileSystem extends FileSystem { * @see #forBucket(String, CloudStorageConfiguration) * @see java.nio.file.FileSystems#getFileSystem(java.net.URI) */ + @CheckReturnValue public static CloudStorageFileSystem forBucket(String bucket) { return forBucket(bucket, CloudStorageConfiguration.DEFAULT); } @@ -74,6 +77,7 @@ public static CloudStorageFileSystem forBucket(String bucket) { * * @see #forBucket(String) */ + @CheckReturnValue public static CloudStorageFileSystem forBucket(String bucket, CloudStorageConfiguration config) { checkArgument( !bucket.startsWith(URI_SCHEME + ":"), "Bucket name must not have schema: %s", bucket); @@ -155,7 +159,9 @@ public CloudStoragePath getPath(String first, String... more) { } /** - * Does nothing. + * Does nothing currently. This method might be updated in the future to close all channels + * associated with this file system object. However it's unlikely that even then, calling this + * method will become mandatory. */ @Override public void close() throws IOException { @@ -191,6 +197,9 @@ public Iterable getRootDirectories() { return ImmutableSet.of(CloudStoragePath.getPath(this, UnixPath.ROOT)); } + /** + * Returns nothing because GCS doesn't have disk partitions of limited size, or anything similar. + */ @Override public Iterable getFileStores() { return ImmutableSet.of(); @@ -206,7 +215,7 @@ public Set supportedFileAttributeViews() { */ @Override public PathMatcher getPathMatcher(String syntaxAndPattern) { - // TODO: Implement me. + // TODO(#813): Implement me. throw new UnsupportedOperationException(); } diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProvider.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProvider.java index f7ded91ecc61..e6c5aeafc999 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProvider.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProvider.java @@ -70,10 +70,16 @@ import javax.annotation.Nullable; import javax.annotation.concurrent.ThreadSafe; +import javax.inject.Singleton; /** * Google Cloud Storage {@link FileSystemProvider} implementation. + * + *

        Note: This class should never be used directly. This class is instantiated by the + * service loader and called through a standardized API, e.g. {@link java.nio.file.Files}. However + * the javadocs in this class serve as useful documentation for the behavior of the GCS NIO library. */ +@Singleton @ThreadSafe @AutoService(FileSystemProvider.class) public final class CloudStorageFileSystemProvider extends FileSystemProvider { @@ -152,6 +158,13 @@ public CloudStorageFileSystem getFileSystem(URI uri) { /** * Returns Cloud Storage file system, provided a URI with no path, e.g. {@code gs://bucket}. + * + * @param uri bucket and current working directory, e.g. {@code gs://bucket} + * @param env map of configuration options, whose keys correspond to the method names of + * {@link CloudStorageConfiguration.Builder}. However you are not allowed to set the working + * directory, as that should be provided in the {@code uri} + * @throws IllegalArgumentException if {@code uri} specifies a user, query, fragment, or scheme is + * not {@value CloudStorageFileSystem#URI_SCHEME} */ @Override public CloudStorageFileSystem newFileSystem(URI uri, Map env) { @@ -527,9 +540,9 @@ public A readAttributes( @Override public Map readAttributes(Path path, String attributes, LinkOption... options) { - // Java 7 NIO defines at least eleven string attributes we'd want to support - // (eg. BasicFileAttributeView and PosixFileAttributeView), so rather than a partial - // implementation we rely on the other overload for now. + // TODO(#811): Java 7 NIO defines at least eleven string attributes we'd want to support + // (eg. BasicFileAttributeView and PosixFileAttributeView), so rather than a partial + // implementation we rely on the other overload for now. throw new UnsupportedOperationException(); } @@ -611,13 +624,13 @@ public String toString() { } private IOException asIOException(StorageException oops) { + // RPC API can only throw StorageException, but CloudStorageFileSystemProvider + // can only throw IOException. Square peg, round hole. + // TODO(#810): Research if other codes should be translated similarly. if (oops.code() == 404) { return new NoSuchFileException(oops.reason()); } - // TODO: research if other codes should be translated to IOException. - // RPC API can only throw StorageException, but CloudStorageFileSystemProvider - // can only throw IOException. Square peg, round hole. Throwable cause = oops.getCause(); try { if (cause instanceof FileAlreadyExistsException) { diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/it/ITGcsNio.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/it/ITGcsNio.java index 3593f0c9ee69..949d98051eea 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/it/ITGcsNio.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/it/ITGcsNio.java @@ -58,17 +58,16 @@ import java.util.logging.Logger; /** - * Integration test for gcloud-nio. This test actually talks to GCS (you need an account). - * Tests both reading and writing. + * Integration test for gcloud-nio. * - * You *must* set the GOOGLE_APPLICATION_CREDENTIALS environment variable - * for this test to work. It must contain the name of a local file that contains - * your Service Account JSON Key. + *

        This test actually talks to GCS (you need an account) and tests both reading and writing. You + * *must* set the {@code GOOGLE_APPLICATION_CREDENTIALS} environment variable for this test to work. + * It must contain the name of a local file that contains your Service Account JSON Key. * - * The instructions for how to get the Service Account JSON Key are + *

        The instructions for how to get the Service Account JSON Key are * at https://cloud.google.com/storage/docs/authentication?hl=en#service_accounts * - * The short version is this: go to cloud.google.com/console, + *

        The short version is this: go to cloud.google.com/console, * select your project, search for "API manager", click "Credentials", * click "create credentials/service account key", new service account, * JSON. The contents of the file that's sent to your browsers is your @@ -78,19 +77,18 @@ @RunWith(JUnit4.class) public class ITGcsNio { - private static final List FILE_CONTENTS = ImmutableList.of( - "Tous les êtres humains naissent libres et égaux en dignité et en droits.", - "Ils sont doués de raison et de conscience et doivent agir ", - "les uns envers les autres dans un esprit de fraternité."); + private static final List FILE_CONTENTS = + ImmutableList.of( + "Tous les êtres humains naissent libres et égaux en dignité et en droits.", + "Ils sont doués de raison et de conscience et doivent agir ", + "les uns envers les autres dans un esprit de fraternité."); private static final Logger log = Logger.getLogger(ITGcsNio.class.getName()); private static final String BUCKET = RemoteStorageHelper.generateBucketName(); private static final String SML_FILE = "tmp-test-small-file.txt"; private static final int SML_SIZE = 100; - // it's big, relatively speaking. - private static final String BIG_FILE = "tmp-test-big-file.txt"; - // arbitrary size that's not too round. - private static final int BIG_SIZE = 2 * 1024 * 1024 - 50; + private static final String BIG_FILE = "tmp-test-big-file.txt"; // it's big, relatively speaking. + private static final int BIG_SIZE = 2 * 1024 * 1024 - 50; // arbitrary size that's not too round. private static final String PREFIX = "tmp-test-file"; private static Storage storage; private static StorageOptions storageOptions; diff --git a/gcloud-java-storage/src/main/java/com/google/cloud/storage/testing/FakeStorageRpc.java b/gcloud-java-storage/src/main/java/com/google/cloud/storage/testing/FakeStorageRpc.java index dc1e28d56826..198e9d1ddeeb 100644 --- a/gcloud-java-storage/src/main/java/com/google/cloud/storage/testing/FakeStorageRpc.java +++ b/gcloud-java-storage/src/main/java/com/google/cloud/storage/testing/FakeStorageRpc.java @@ -58,6 +58,7 @@ *

      11. patch *
      12. continueRewrite *
      13. createBatch + *
      14. checksums, etags * * */ @@ -65,7 +66,7 @@ public class FakeStorageRpc implements StorageRpc { // fullname -> metadata - Map stuff = new HashMap<>(); + Map metadata = new HashMap<>(); // fullname -> contents Map contents = new HashMap<>(); // fullname -> future contents that will be visible on close. @@ -74,7 +75,7 @@ public class FakeStorageRpc implements StorageRpc { private final boolean throwIfOption; /** - * @param throwIfOption if true, we throw when given any option. + * @param throwIfOption if true, we throw when given any option */ public FakeStorageRpc(boolean throwIfOption) { this.throwIfOption = throwIfOption; @@ -82,7 +83,7 @@ public FakeStorageRpc(boolean throwIfOption) { // remove all files void reset() { - stuff = new HashMap<>(); + metadata = new HashMap<>(); contents = new HashMap<>(); } @@ -96,7 +97,7 @@ public StorageObject create(StorageObject object, InputStream content, Map> list(String bucket, Map List values = new ArrayList<>(); Map folders = new HashMap<>(); - for (StorageObject so : stuff.values()) { + for (StorageObject so : metadata.values()) { if (!so.getName().startsWith(prefix)) { continue; } @@ -175,8 +176,8 @@ public StorageObject get(StorageObject object, Map options) throws St } String key = fullname(object); - if (stuff.containsKey(key)) { - StorageObject ret = stuff.get(key); + if (metadata.containsKey(key)) { + StorageObject ret = metadata.get(key); if (contents.containsKey(key)) { ret.setSize(BigInteger.valueOf(contents.get(key).length)); } @@ -208,7 +209,7 @@ public boolean delete(Bucket bucket, Map options) throws StorageExcep public boolean delete(StorageObject object, Map options) throws StorageException { String key = fullname(object); contents.remove(key); - return null != stuff.remove(key); + return null != metadata.remove(key); } @Override @@ -272,10 +273,10 @@ public String open(StorageObject object, Map options) throws StorageE } } } - if (mustNotExist && stuff.containsKey(key)) { + if (mustNotExist && metadata.containsKey(key)) { throw new StorageException(new FileAlreadyExistsException(key)); } - stuff.put(key, object); + metadata.put(key, object); return fullname(object); } @@ -327,7 +328,7 @@ public RewriteResponse openRewrite(RewriteRequest rewriteRequest) throws Storage throw new StorageException(new FileAlreadyExistsException(destKey)); } - stuff.put(destKey, rewriteRequest.target); + metadata.put(destKey, rewriteRequest.target); byte[] data = contents.get(sourceKey); contents.put(destKey, Arrays.copyOf(data, data.length)); From bb3417780b27beec2fc35cee110f19b6cb6dc05b Mon Sep 17 00:00:00 2001 From: Marco Ziccardi Date: Mon, 27 Jun 2016 14:19:08 +0200 Subject: [PATCH 32/38] Temporarily hide FakeStorageRpc and LocalStorageHelper (#1079) --- .../nio/CloudStorageFileAttributeViewTest.java | 1 - .../nio/CloudStorageFileAttributesTest.java | 1 - .../nio/CloudStorageFileSystemProviderTest.java | 1 - .../contrib/nio/CloudStorageFileSystemTest.java | 1 - .../contrib/nio/CloudStorageOptionsTest.java | 1 - .../contrib/nio/CloudStoragePathTest.java | 1 - .../storage/contrib/nio}/FakeStorageRpc.java | 4 ++-- .../storage/contrib/nio}/LocalStorageHelper.java | 16 +++++++--------- 8 files changed, 9 insertions(+), 17 deletions(-) rename {gcloud-java-storage/src/main/java/com/google/cloud/storage/testing => gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio}/FakeStorageRpc.java (99%) rename {gcloud-java-storage/src/main/java/com/google/cloud/storage/testing => gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio}/LocalStorageHelper.java (86%) diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributeViewTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributeViewTest.java index d48594e80a16..4a437a288f5f 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributeViewTest.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributeViewTest.java @@ -19,7 +19,6 @@ import static com.google.common.truth.Truth.assertThat; import static java.nio.charset.StandardCharsets.UTF_8; -import com.google.cloud.storage.testing.LocalStorageHelper; import com.google.common.testing.EqualsTester; import com.google.common.testing.NullPointerTester; diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributesTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributesTest.java index 1ce61e9257b2..c5001114c14e 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributesTest.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileAttributesTest.java @@ -20,7 +20,6 @@ import static java.nio.charset.StandardCharsets.UTF_8; import com.google.cloud.storage.Acl; -import com.google.cloud.storage.testing.LocalStorageHelper; import com.google.common.testing.EqualsTester; import com.google.common.testing.NullPointerTester; diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProviderTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProviderTest.java index 9c191a4f5882..6e19a1d7bb11 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProviderTest.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProviderTest.java @@ -26,7 +26,6 @@ import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING; import static java.nio.file.StandardOpenOption.WRITE; -import com.google.cloud.storage.testing.LocalStorageHelper; import com.google.common.collect.ImmutableList; import com.google.common.testing.NullPointerTester; diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemTest.java index dbba88967773..90e61ded721a 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemTest.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemTest.java @@ -20,7 +20,6 @@ import static java.nio.charset.StandardCharsets.UTF_8; import com.google.cloud.storage.StorageOptions; -import com.google.cloud.storage.testing.LocalStorageHelper; import com.google.common.testing.EqualsTester; import com.google.common.testing.NullPointerTester; diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageOptionsTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageOptionsTest.java index b18aae93ba27..e23ba5055511 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageOptionsTest.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageOptionsTest.java @@ -20,7 +20,6 @@ import static java.nio.charset.StandardCharsets.UTF_8; import com.google.cloud.storage.Acl; -import com.google.cloud.storage.testing.LocalStorageHelper; import com.google.common.testing.NullPointerTester; import org.junit.Before; diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStoragePathTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStoragePathTest.java index fe65c6927665..7ba37211cabb 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStoragePathTest.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStoragePathTest.java @@ -18,7 +18,6 @@ import static com.google.common.truth.Truth.assertThat; -import com.google.cloud.storage.testing.LocalStorageHelper; import com.google.common.collect.Iterables; import com.google.common.testing.EqualsTester; import com.google.common.testing.NullPointerTester; diff --git a/gcloud-java-storage/src/main/java/com/google/cloud/storage/testing/FakeStorageRpc.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/FakeStorageRpc.java similarity index 99% rename from gcloud-java-storage/src/main/java/com/google/cloud/storage/testing/FakeStorageRpc.java rename to gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/FakeStorageRpc.java index 198e9d1ddeeb..a5aad9d78fbf 100644 --- a/gcloud-java-storage/src/main/java/com/google/cloud/storage/testing/FakeStorageRpc.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/FakeStorageRpc.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.cloud.storage.testing; +package com.google.cloud.storage.contrib.nio; import com.google.api.services.storage.model.Bucket; import com.google.api.services.storage.model.StorageObject; @@ -63,7 +63,7 @@ * */ @NotThreadSafe -public class FakeStorageRpc implements StorageRpc { +class FakeStorageRpc implements StorageRpc { // fullname -> metadata Map metadata = new HashMap<>(); diff --git a/gcloud-java-storage/src/main/java/com/google/cloud/storage/testing/LocalStorageHelper.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/LocalStorageHelper.java similarity index 86% rename from gcloud-java-storage/src/main/java/com/google/cloud/storage/testing/LocalStorageHelper.java rename to gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/LocalStorageHelper.java index 7797cb7d91f6..a27a0df3a43a 100644 --- a/gcloud-java-storage/src/main/java/com/google/cloud/storage/testing/LocalStorageHelper.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/LocalStorageHelper.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 Google Inc. All Rights Reserved. + * Copyright 2016 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.cloud.storage.testing; +package com.google.cloud.storage.contrib.nio; import com.google.cloud.spi.ServiceRpcFactory; import com.google.cloud.storage.spi.StorageRpc; @@ -24,15 +24,14 @@ * Utility to create an in-memory storage configuration for testing. Storage options can be * obtained via the {@link #options()} method. Returned options will point to FakeStorageRpc. */ -public class LocalStorageHelper { +class LocalStorageHelper { // used for testing. Will throw if you pass it an option. private static final FakeStorageRpc instance = new FakeStorageRpc(true); /** - * Returns a {@link StorageOptions} that use the static FakeStorageRpc instance, - * and resets it first so you start from a clean slate. - * That instance will throw if you pass it any option. * + * Returns a {@link StorageOptions} that use the static FakeStorageRpc instance, and resets it + * first so you start from a clean slate. That instance will throw if you pass it any option. */ public static StorageOptions options() { instance.reset(); @@ -49,8 +48,8 @@ public StorageRpc create(StorageOptions options) { } /** - * Returns a {@link StorageOptions} that creates a new FakeStorageRpc instance - * with the given option. + * Returns a {@link StorageOptions} that creates a new FakeStorageRpc instance with the given + * option. */ public static StorageOptions customOptions(final boolean throwIfOptions) { return StorageOptions.builder() @@ -64,5 +63,4 @@ public StorageRpc create(StorageOptions options) { }) .build(); } - } From 8c120527cedd381925ef5576cf57a42138a2e023 Mon Sep 17 00:00:00 2001 From: JP Martin Date: Tue, 28 Jun 2016 06:25:26 -0700 Subject: [PATCH 33/38] Improve nio and contrib READMEs (#1077) --- gcloud-java-contrib/README.md | 7 + .../gcloud-java-nio-examples/README.md | 2 + gcloud-java-contrib/gcloud-java-nio/README.md | 210 ++++++++++++++++++ 3 files changed, 219 insertions(+) create mode 100644 gcloud-java-contrib/gcloud-java-nio/README.md diff --git a/gcloud-java-contrib/README.md b/gcloud-java-contrib/README.md index 4df9f83129c4..0a0ad0dd61a6 100644 --- a/gcloud-java-contrib/README.md +++ b/gcloud-java-contrib/README.md @@ -17,6 +17,9 @@ Contents Quickstart ---------- + +### gcloud-java-nio + If you are using Maven, add this to your pom.xml file ```xml @@ -34,6 +37,10 @@ If you are using SBT, add this to your dependencies libraryDependencies += "com.google.cloud" % "gcloud-java-contrib" % "0.2.4" ``` +### gcloud-java-nio-examples + +See its [README](./gcloud-java-nio-examples/README.md) for instructions on how to run it. + Java Versions ------------- diff --git a/gcloud-java-contrib/gcloud-java-nio-examples/README.md b/gcloud-java-contrib/gcloud-java-nio-examples/README.md index 41ec45ff1874..c9db073aa933 100644 --- a/gcloud-java-contrib/gcloud-java-nio-examples/README.md +++ b/gcloud-java-contrib/gcloud-java-nio-examples/README.md @@ -39,3 +39,5 @@ facility, the [standard way](http://docs.oracle.com/javase/7/docs/technotes/guid If you have access to a project's source code you can also simply add gcloud-java-nio as a dependency and let Maven pull in the required dependencies (this is what the nio unit tests do). This approach is preferable as the fat jar approach may waste memory on multiple copies of dependencies. + +[developers-console]:https://console.developers.google.com/ \ No newline at end of file diff --git a/gcloud-java-contrib/gcloud-java-nio/README.md b/gcloud-java-contrib/gcloud-java-nio/README.md new file mode 100644 index 000000000000..9a15d8e24458 --- /dev/null +++ b/gcloud-java-contrib/gcloud-java-nio/README.md @@ -0,0 +1,210 @@ +NIO Filesystem Provider for Google Cloud Storage (Alpha) +======================================================== + +Implementation of Java 7 `java.nio.file.FileSystem` for +[Google Cloud Storage](https://cloud.google.com/storage/). + +This library allows you to use the standardized Java file system API +for interacting with Google Cloud Storage. + +[![Build Status](https://travis-ci.org/GoogleCloudPlatform/gcloud-java.svg?branch=master)](https://travis-ci.org/GoogleCloudPlatform/gcloud-java) +[![Coverage Status](https://coveralls.io/repos/GoogleCloudPlatform/gcloud-java/badge.svg?branch=master)](https://coveralls.io/r/GoogleCloudPlatform/gcloud-java?branch=master) +[![Maven](https://img.shields.io/maven-central/v/com.google.cloud/gcloud-java-nio.svg)]( https://img.shields.io/maven-central/v/com.google.cloud/gcloud-java-nio.svg) +[![Codacy Badge](https://api.codacy.com/project/badge/grade/9da006ad7c3a4fe1abd142e77c003917)](https://www.codacy.com/app/mziccard/gcloud-java) +[![Dependency Status](https://www.versioneye.com/user/projects/56bd8ee72a29ed002d2b0969/badge.svg?style=flat)](https://www.versioneye.com/user/projects/56bd8ee72a29ed002d2b0969) + +- [Homepage](https://googlecloudplatform.github.io/gcloud-java/) +- [API Documentation](http://googlecloudplatform.github.io/gcloud-java/apidocs/index.html?com/google/cloud/storage/package-summary.html) + +> Note: This client is a work-in-progress, and may occasionally +> make backwards-incompatible changes. + +Quickstart +---------- +If you are using Maven, add this to your pom.xml file +```xml + + com.google.cloud + gcloud-java-nio + 0.2.4 + +``` +If you are using Gradle, add this to your dependencies +```Groovy +compile 'com.google.cloud:gcloud-java-nio:0.2.4' +``` +If you are using SBT, add this to your dependencies +```Scala +libraryDependencies += "com.google.cloud" % "gcloud-java-nio" % "0.2.4" +``` + +Example Applications +------------------- + +* [`Stat`](../../gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/Stat.java) +shows how to get started with NIO. + +* [`ParallelCountBytes`](../../gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/ParallelCountBytes.java) +efficiently downloads a file from Google Cloud Storage. + +* [`ListFileSystems`](../gcloud-java-nio-examples/README.md) illustrates how +NIO can add Google Cloud Storage support to some legacy programs, without +having to modify them. + + +Authentication +-------------- + +See the [Authentication](https://github.com/GoogleCloudPlatform/gcloud-java#authentication) +section in the base directory's README. + +About Google Cloud Storage +-------------------------- + +[Google Cloud Storage][cloud-storage] is a durable and highly available +object storage service. Google Cloud Storage is almost infinitely scalable +and guarantees consistency: when a write succeeds, the latest copy of the +object will be returned to any GET, globally. + +See the [Google Cloud Storage docs][cloud-storage-activation] for more details +on how to activate Cloud Storage for your project. + +About Java NIO Providers +------------------------ + +Java NIO Providers is an extension mechanism that is part of Java and allows +third parties to extend Java's [normal File API][java-file-api] to support +additional filesystems. + +Getting Started +--------------- +#### Prerequisites + +For this tutorial, you will need a [Google Developers +Console](https://console.developers.google.com/) project with "Google Cloud +Storage" and "Google Cloud Storage JSON API" enabled via the console's API +Manager. You will need to [enable +billing](https://support.google.com/cloud/answer/6158867?hl=en) to use Google +Cloud Storage. [Follow these +instructions](https://cloud.google.com/docs/authentication#preparation) to get +your project set up. You will also need to set up the local development +environment by [installing the Google Cloud SDK](https://cloud.google.com/sdk/) +and running the following commands in command line: `gcloud auth login` and +`gcloud config set project [YOUR PROJECT ID]`. + +#### Installation and setup +You'll need to obtain the `gcloud-java-nio` library. + +There are two ways to use this library. + +The recommended way is to follow the [Quickstart](#quickstart) section to add +`gcloud-java-nio` as a dependency in your code. + +The second way is more complicated, but it allows you to add Google Cloud +Storage support to some legacy Java programs. This approach is described in the +[gcloud-java-nio-examples README](../gcloud-java-nio-examples/README.md). + +#### Accessing files + +The simplest way to get started is with `Paths` and `Files`: + + Path path = Paths.get(URI.create("gs://bucket/lolcat.csv")); + List lines = Files.readAllLines(path, StandardCharsets.UTF_8); + +If you know the paths will point to Google Cloud Storage, you can also use the +direct formulation: + + try (CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("bucket") { + Path path = fs.getPath("lolcat.csv"); + List lines = Files.readAllLines(path, StandardCharsets.UTF_8); + } + +Once you have a `Path` you can use it as you would a normal file. For example +you can use `InputStream` and `OutputStream` for streaming: + + try (InputStream input = Files.openInputStream(path)) { + // ... + } + +You can also set various attributes using CloudStorageOptions static helpers: + + Files.write(csvPath, csvLines, StandardCharsets.UTF_8, + withMimeType(MediaType.CSV_UTF8), + withoutCaching()); + +Limitations +----------- + +This library is usable, but not yet complete. The following features are not +yet implemented: + * Listing all the buckets + * Resuming upload or download + * Generations + * File attributes + * (more) + +Some features are not on the roadmap: this library would be a poor choice to +mirror a local filesystem onto the cloud because Google Cloud Storage has a +different set of features from your local disk. This library, by design, +does not mask those differences. Rather, it aims to expose the common +subset via a familiar interface. + +**NOTE:** Cloud Storage uses a flat namespace and therefore doesn't support real +directories. So this library supports what's known as "pseudo-directories". Any +path that includes a trailing slash, will be considered a directory. It will +always be assumed to exist, without performing any I/O. This allows you to do +path manipulation in the same manner as you would with the normal UNIX file +system implementation. You can disable this feature with +`CloudStorageConfiguration.usePseudoDirectories()`. + +#### Complete source code + +There are examples in [gcloud-java-examples](../gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/) +for your perusal. + +Java Versions +------------- + +Java 7 or above is required for using this client. + +Versioning +---------- + +This library follows [Semantic Versioning](http://semver.org/). + +It is currently in major version zero (``0.y.z``), which means that anything +may change at any time and the public API should not be considered +stable. + +Contributing +------------ + +Contributions to this library are always welcome and highly encouraged. + +See `gcloud-java`'s [CONTRIBUTING] documentation and the `gcloud-*` +[shared documentation](https://github.com/GoogleCloudPlatform/gcloud-common/blob/master/contributing/readme.md#how-to-contribute-to-gcloud) +for more information on how to get started. + +Please note that this project is released with a Contributor Code of Conduct. +By participating in this project you agree to abide by its terms. See +[Code of Conduct][code-of-conduct] for more information. + +License +------- + +Apache 2.0 - See [LICENSE] for more information. + + +[CONTRIBUTING]:https://github.com/GoogleCloudPlatform/gcloud-java/blob/master/CONTRIBUTING.md +[code-of-conduct]:https://github.com/GoogleCloudPlatform/gcloud-java/blob/master/CODE_OF_CONDUCT.md#contributor-code-of-conduct +[LICENSE]: https://github.com/GoogleCloudPlatform/gcloud-java/blob/master/LICENSE +[TESTING]: https://github.com/GoogleCloudPlatform/gcloud-java/blob/master/TESTING.md#testing-code-that-uses-storage +[cloud-platform]: https://cloud.google.com/ + +[cloud-storage]: https://cloud.google.com/storage/ +[cloud-storage-docs]: https://cloud.google.com/storage/docs/overview +[cloud-storage-create-bucket]: https://cloud.google.com/storage/docs/cloud-console#_creatingbuckets +[storage-api]: http://googlecloudplatform.github.io/gcloud-java/apidocs/index.html?com/google/cloud/storage/package-summary.html +[cloud-storage-activation]:https://cloud.google.com/storage/docs/signup?hl=en + +[java-file-api]: https://docs.oracle.com/javase/7/docs/api/java/nio/file/Files.html From d31301d5327dade4dfe55a630623f927ec8c0b98 Mon Sep 17 00:00:00 2001 From: Marco Ziccardi Date: Tue, 28 Jun 2016 16:41:16 +0200 Subject: [PATCH 34/38] Make examples' javadoc more homogenous and minor html fixes --- .../examples/bigquery/BigQueryExample.java | 50 ++++++++----------- .../examples/compute/ComputeExample.java | 9 ++-- .../examples/datastore/DatastoreExample.java | 13 ++--- .../google/cloud/examples/dns/DnsExample.java | 7 +-- .../google/cloud/examples/nio/CountBytes.java | 6 ++- .../examples/nio/ParallelCountBytes.java | 6 ++- .../com/google/cloud/examples/nio/Stat.java | 6 ++- .../ResourceManagerExample.java | 13 ++--- .../examples/storage/StorageExample.java | 14 ++---- 9 files changed, 57 insertions(+), 67 deletions(-) diff --git a/gcloud-java-examples/src/main/java/com/google/cloud/examples/bigquery/BigQueryExample.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/bigquery/BigQueryExample.java index 3dc14f13a07c..d7c1c1ef0b1e 100644 --- a/gcloud-java-examples/src/main/java/com/google/cloud/examples/bigquery/BigQueryExample.java +++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/bigquery/BigQueryExample.java @@ -58,35 +58,29 @@ * *

        This example demonstrates a simple/typical BigQuery usage. * - *

        Steps needed for running the example: - *

          - *
        1. log in using gcloud SDK - {@code gcloud auth login}.
        2. - *
        3. compile using maven - {@code cd gcloud-java-examples; - * mvn package appassembler:assemble -DskipTests -Dmaven.javadoc.skip=true -Dmaven.source.skip=true} - *
        4. - *
        5. run - + *

          See the + * + * README for compilation instructions. Run this code with *

          {@code target/appassembler/bin/BigQueryExample []
          - *  list datasets |
          - *  list tables  |
          - *  list jobs |
          - *  list data   |
          - *  info dataset  |
          - *  info table  
          | - * info job | - * create dataset | - * create table
          (:)+ | - * create view
          | - * create external-table
          (:)+ | - * delete dataset | - * delete table
          | - * cancel | - * copy | - * load
          + | - * extract
          + | - * query | - * load-file
          "} - * - * + * list datasets | + * list tables | + * list jobs | + * list data
          | + * info dataset | + * info table
          | + * info job | + * create dataset | + * create table
          (:)+ | + * create view
          | + * create external-table
          (:)+ | + * delete dataset | + * delete table
          | + * cancel | + * copy | + * load
          + | + * extract
          + | + * query | + * load-file
          } * *

          The first parameter is an optional {@code project_id} (logged-in project will be used if not * supplied). Second parameter is a BigQuery operation and can be used to demonstrate its usage. For diff --git a/gcloud-java-examples/src/main/java/com/google/cloud/examples/compute/ComputeExample.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/compute/ComputeExample.java index 603aaeefe4ae..a66fe20e52c8 100644 --- a/gcloud-java-examples/src/main/java/com/google/cloud/examples/compute/ComputeExample.java +++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/compute/ComputeExample.java @@ -83,9 +83,10 @@ * *

          This example demonstrates a simple/typical Compute usage. * - *

          See the README for the steps needed for compiling and running the example. - *

          Possible command-line arguments are: - *

          {@code []
          + * 

          See the + * + * README for compilation instructions. Run this code with + *

          {@code target/appassembler/bin/ComputeExample []
            * list networks |
            * list region-operations  |
            * list instances ? |
          @@ -147,7 +148,7 @@
            * stop   |
            * reset   |
            * set-tags   * |
          - * set-metadata   *"}
          + * set-metadata *}
          * *

          The first parameter is an optional {@code project_id} (logged-in project will be used if not * supplied). Second parameter is a Compute operation and can be used to demonstrate its usage. For diff --git a/gcloud-java-examples/src/main/java/com/google/cloud/examples/datastore/DatastoreExample.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/datastore/DatastoreExample.java index 15381ab05ba3..0e3e505dd308 100644 --- a/gcloud-java-examples/src/main/java/com/google/cloud/examples/datastore/DatastoreExample.java +++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/datastore/DatastoreExample.java @@ -41,14 +41,11 @@ *

          This example adds, displays or clears comments for a given user. This example also sets * contact information for a user. * - *

          Steps needed for running the example:

            - *
          1. login using gcloud SDK - {@code gcloud auth login}.
          2. - *
          3. compile using maven - {@code cd gcloud-java-examples; - * mvn package appassembler:assemble -DskipTests -Dmaven.javadoc.skip=true -Dmaven.source.skip=true} - *
          4. - *
          5. run - {@code target/appassembler/bin/DatastoreExample - * [projectId] [user] [delete|display|add comment|set ]}
          6. - *
          + *

          See the + * + * README for compilation instructions. Run this code with + *

          {@code target/appassembler/bin/DatastoreExample
          + * [projectId] [user] [delete|display|add comment|set  ]}
          * *

          If no action is provided {@code display} is executed. */ diff --git a/gcloud-java-examples/src/main/java/com/google/cloud/examples/dns/DnsExample.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/dns/DnsExample.java index 5b920902091d..f49659c3158f 100644 --- a/gcloud-java-examples/src/main/java/com/google/cloud/examples/dns/DnsExample.java +++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/dns/DnsExample.java @@ -42,9 +42,10 @@ *

          This example creates, deletes, gets, and lists zones. It also creates and deletes * record sets of type A, and lists record sets. * - *

          See the README for steps needed for running the example. - *

          The command-line arguments can be: - *

          {@code []
          + * 

          See the + * + * README for compilation instructions. Run this code with + *

          {@code target/appassembler/bin/DnsExample []
            * create    |
            * get  |
            * delete  |
          diff --git a/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/CountBytes.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/CountBytes.java
          index 18425ae62438..90d6f43e5f3f 100644
          --- a/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/CountBytes.java
          +++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/CountBytes.java
          @@ -37,8 +37,10 @@
            * This class also shows how to read all of the file's contents using NIO,
            * computes a MD5 hash, and reports how long it took.
            *
          - * 

          See the README for compilation instructions. Run this code with - * {@code target/appassembler/bin/CountBytes } + *

          See the + * + * README for compilation instructions. Run this code with + *

          {@code target/appassembler/bin/CountBytes }
          */ public class CountBytes { diff --git a/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/ParallelCountBytes.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/ParallelCountBytes.java index 0f268ddb72ca..2ccd68b6587d 100644 --- a/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/ParallelCountBytes.java +++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/ParallelCountBytes.java @@ -43,8 +43,10 @@ * in order, using multithreaded NIO reads. * It prints a MD5 hash and reports how long it took. * - *

          See the README for compilation instructions. Run this code with - * {@code target/appassembler/bin/ParallelCountBytes } + *

          See the + * + * README for compilation instructions. Run this code with + *

          {@code target/appassembler/bin/ParallelCountBytes }
          */ public class ParallelCountBytes { diff --git a/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/Stat.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/Stat.java index b8d018f8f738..19933204705b 100644 --- a/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/Stat.java +++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/Stat.java @@ -35,8 +35,10 @@ * or you can directly pass in a GCS file name to use. In that case you have to * be logged in (using e.g. the gcloud auth command). * - *

          See the README for compilation instructions. Run this code with - * {@code target/appassembler/bin/Stat --help | --check | --list | } + *

          See the + * + * README for compilation instructions. Run this code with + *

          {@code target/appassembler/bin/Stat --help | --check | --list | }
          * *

          In short, this version (in gcloud-java-examples) is in a package that lists gcloud-java-nio * as a dependency, so it will work directly without having to do any special work. diff --git a/gcloud-java-examples/src/main/java/com/google/cloud/examples/resourcemanager/ResourceManagerExample.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/resourcemanager/ResourceManagerExample.java index 57dc81f44e50..53bcd5357594 100644 --- a/gcloud-java-examples/src/main/java/com/google/cloud/examples/resourcemanager/ResourceManagerExample.java +++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/resourcemanager/ResourceManagerExample.java @@ -32,14 +32,11 @@ * *

          This example creates, deletes, gets, and lists projects. * - *

          Steps needed for running the example:

            - *
          1. log in using gcloud SDK - {@code gcloud auth login}.
          2. - *
          3. compile using maven - {@code cd gcloud-java-examples; - * mvn package appassembler:assemble -DskipTests -Dmaven.javadoc.skip=true -Dmaven.source.skip=true} - *
          4. - *
          5. run - {@code target/appassembler/bin/ResourceManagerExample - * [list | [create | delete | get] projectId]}
          6. - *
          + *

          See the + * + * README for compilation instructions. Run this code with + *

          {@code target/appassembler/bin/ResourceManagerExample
          + * [list | [create | delete | get] projectId]}
          */ public class ResourceManagerExample { diff --git a/gcloud-java-examples/src/main/java/com/google/cloud/examples/storage/StorageExample.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/storage/StorageExample.java index aea9dffd3f6b..f803377c1255 100644 --- a/gcloud-java-examples/src/main/java/com/google/cloud/examples/storage/StorageExample.java +++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/storage/StorageExample.java @@ -64,13 +64,9 @@ * *

          This example demonstrates a simple/typical storage usage. * - *

          Steps needed for running the example: - *

            - *
          1. log in using gcloud SDK - {@code gcloud auth login}.
          2. - *
          3. compile using maven - {@code cd gcloud-java-examples; - * mvn package appassembler:assemble -DskipTests -Dmaven.javadoc.skip=true -Dmaven.source.skip=true} - *
          4. - *
          5. run - + *

            See the + * + * README for compilation instructions. Run this code with *

            {@code target/appassembler/bin/StorageExample []
              *  list [] |
              *  info [ []] |
            @@ -84,9 +80,7 @@
              *  add-acl domain  ?  OWNER|READER|WRITER |
              *  add-acl project  ? :(OWNERS|EDITORS|VIEWERS) OWNER|READER|WRITER |
              *  add-acl user  ? |allUsers|allAuthenticatedUsers OWNER|READER|WRITER |
            - *  add-acl group  ?  OWNER|READER|WRITER"}
            - *
          6. - *
          + * add-acl group ? OWNER|READER|WRITER}
          * *

          The first parameter is an optional {@code project_id} (logged-in project will be used if not * supplied). Second parameter is a Storage operation (list, delete, compose,...) and can be used to From 5dd4692a58a6a039c382e99f72f7f431c5759aca Mon Sep 17 00:00:00 2001 From: Marco Ziccardi Date: Wed, 29 Jun 2016 18:20:51 +0200 Subject: [PATCH 35/38] Use appassembler in Pub/Sub example --- gcloud-java-examples/README.md | 8 ++++---- gcloud-java-examples/pom.xml | 6 ++++++ .../google/cloud/examples/pubsub/PubSubExample.java | 12 ++++-------- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/gcloud-java-examples/README.md b/gcloud-java-examples/README.md index 17145046f29b..50ac44e11c47 100644 --- a/gcloud-java-examples/README.md +++ b/gcloud-java-examples/README.md @@ -104,10 +104,10 @@ To run examples from your command line: Before running the example, go to the [Google Developers Console][developers-console] to ensure that "Google Cloud Pub/Sub" is enabled. ``` - mvn exec:java -Dexec.mainClass="com.google.cloud.examples.pubsub.PubSubExample" -Dexec.args="create topic test-topic" - mvn exec:java -Dexec.mainClass="com.google.cloud.examples.pubsub.PubSubExample" -Dexec.args="create subscription test-topic test-subscription" - mvn exec:java -Dexec.mainClass="com.google.cloud.examples.pubsub.PubSubExample" -Dexec.args="publish test-topic message1 message2" - mvn exec:java -Dexec.mainClass="com.google.cloud.examples.pubsub.PubSubExample" -Dexec.args="pull sync test-subscription 2" + target/appassembler/bin/PubSubExample create topic test-topic + target/appassembler/bin/PubSubExample create subscription test-topic test-subscription + target/appassembler/bin/PubSubExample publish test-topic message1 message2 + target/appassembler/bin/PubSubExample pull sync test-subscription 2 ``` * Here's an example run of `ResourceManagerExample`. diff --git a/gcloud-java-examples/pom.xml b/gcloud-java-examples/pom.xml index 93c7cced9c8e..66920628bfbe 100644 --- a/gcloud-java-examples/pom.xml +++ b/gcloud-java-examples/pom.xml @@ -80,6 +80,12 @@ com.google.cloud.examples.nio.ParallelCountBytes ParallelCountBytes + + + com.google.cloud.examples.pubsub.PubSubExample + + PubSubExample + com.google.cloud.examples.resourcemanager.ResourceManagerExample diff --git a/gcloud-java-examples/src/main/java/com/google/cloud/examples/pubsub/PubSubExample.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/pubsub/PubSubExample.java index c488405345be..167ba610cc9d 100644 --- a/gcloud-java-examples/src/main/java/com/google/cloud/examples/pubsub/PubSubExample.java +++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/pubsub/PubSubExample.java @@ -43,12 +43,10 @@ * *

          This example demonstrates a simple/typical Pub/Sub usage. * - *

          Steps needed for running the example: - *

            - *
          1. login using gcloud SDK - {@code gcloud auth login}.
          2. - *
          3. compile using maven - {@code mvn compile}
          4. - *
          5. run using maven - - *
            {@code mvn exec:java -Dexec.mainClass="com.google.cloud.examples.pubsub.PubSubExample"
            + * 

            See the + * + * README for compilation instructions. Run this code with + *

            {@code target/appassembler/bin/PubSubExample
              *  -Dexec.args="[]
              *  pull async  ?
              *  pull sync  
            @@ -64,8 +62,6 @@
              *  delete subscription 
              *  info topic 
              *  info subscription "}
            - *
          6. - *
          * *

          The first parameter is an optional {@code project_id} (logged-in project will be used if not * supplied). Second parameter is a Pub/Sub operation and can be used to demonstrate its usage. For From 24b73ab39c03eda81b364369c69bc8c9413050f7 Mon Sep 17 00:00:00 2001 From: Marco Ziccardi Date: Thu, 30 Jun 2016 10:33:57 +0200 Subject: [PATCH 36/38] Minor checkstyle and codacy fixes to gcloud-java-nio --- .../nio/CloudStorageConfiguration.java | 6 ++--- .../contrib/nio/CloudStorageFileSystem.java | 26 +++++++++---------- .../nio/CloudStorageFileSystemProvider.java | 10 +++---- .../contrib/nio/CloudStorageReadChannel.java | 8 +++--- .../CloudStorageFileSystemProviderTest.java | 9 +++---- .../nio/CloudStorageReadChannelTest.java | 2 +- .../nio/CloudStorageWriteChannelTest.java | 2 +- .../storage/contrib/nio/FakeStorageRpc.java | 19 +++++--------- 8 files changed, 37 insertions(+), 45 deletions(-) diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageConfiguration.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageConfiguration.java index 0c8046ca2277..fa21c5f8cdd2 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageConfiguration.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageConfiguration.java @@ -28,6 +28,8 @@ @AutoValue public abstract class CloudStorageConfiguration { + public static final CloudStorageConfiguration DEFAULT = builder().build(); + /** * Returns path of current working directory. This defaults to the root directory. */ @@ -76,7 +78,7 @@ public static Builder builder() { public static final class Builder { private String workingDirectory = UnixPath.ROOT; - private boolean permitEmptyPathComponents = false; + private boolean permitEmptyPathComponents; private boolean stripPrefixSlash = true; private boolean usePseudoDirectories = true; private int blockSize = CloudStorageFileSystem.BLOCK_SIZE_DEFAULT; @@ -147,8 +149,6 @@ public CloudStorageConfiguration build() { Builder() {} } - public static final CloudStorageConfiguration DEFAULT = builder().build(); - static CloudStorageConfiguration fromMap(Map env) { Builder builder = builder(); for (Map.Entry entry : env.entrySet()) { diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystem.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystem.java index 650eda1199af..ce12ff3eee97 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystem.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystem.java @@ -37,8 +37,6 @@ import javax.annotation.CheckReturnValue; import javax.annotation.Nullable; -import javax.annotation.concurrent.Immutable; -import javax.annotation.CheckReturnValue; import javax.annotation.concurrent.ThreadSafe; /** @@ -52,6 +50,17 @@ @ThreadSafe public final class CloudStorageFileSystem extends FileSystem { + public static final String URI_SCHEME = "gs"; + public static final String GCS_VIEW = "gcs"; + public static final String BASIC_VIEW = "basic"; + public static final int BLOCK_SIZE_DEFAULT = 2 * 1024 * 1024; + public static final FileTime FILE_TIME_UNKNOWN = FileTime.fromMillis(0); + public static final ImmutableSet SUPPORTED_VIEWS = ImmutableSet.of(BASIC_VIEW, GCS_VIEW); + + private final CloudStorageFileSystemProvider provider; + private final String bucket; + private final CloudStorageConfiguration config; + /** * Returns Google Cloud Storage {@link FileSystem} object for {@code bucket}. * @@ -108,17 +117,6 @@ public static CloudStorageFileSystem forBucket(String bucket, CloudStorageConfig bucket, checkNotNull(config)); } - public static final String URI_SCHEME = "gs"; - public static final String GCS_VIEW = "gcs"; - public static final String BASIC_VIEW = "basic"; - public static final int BLOCK_SIZE_DEFAULT = 2 * 1024 * 1024; - public static final FileTime FILE_TIME_UNKNOWN = FileTime.fromMillis(0); - public static final ImmutableSet SUPPORTED_VIEWS = ImmutableSet.of(BASIC_VIEW, GCS_VIEW); - - private final CloudStorageFileSystemProvider provider; - private final String bucket; - private final CloudStorageConfiguration config; - CloudStorageFileSystem( CloudStorageFileSystemProvider provider, String bucket, CloudStorageConfiguration config) { checkArgument(!bucket.isEmpty(), "bucket"); @@ -189,7 +187,7 @@ public boolean isReadOnly() { */ @Override public String getSeparator() { - return "" + UnixPath.SEPARATOR; + return Character.toString(UnixPath.SEPARATOR); } @Override diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProvider.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProvider.java index e6c5aeafc999..cc8bd5cf0bbf 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProvider.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProvider.java @@ -308,10 +308,10 @@ private SeekableByteChannel newWriteChannel(Path path, Set try { return new CloudStorageWriteChannel( - storage.writer( - infoBuilder.build(), writeOptions.toArray(new Storage.BlobWriteOption[0]))); + storage.writer(infoBuilder.build(), + writeOptions.toArray(new Storage.BlobWriteOption[writeOptions.size()]))); } catch (StorageException oops) { - throw asIOException(oops); + throw asIoException(oops); } } @@ -471,7 +471,7 @@ public void copy(Path source, Path target, CopyOption... options) throws IOExcep CopyWriter copyWriter = storage.copy(copyReqBuilder.build()); copyWriter.result(); } catch (StorageException oops) { - throw asIOException(oops); + throw asIoException(oops); } } @@ -623,7 +623,7 @@ public String toString() { return MoreObjects.toStringHelper(this).add("storage", storage).toString(); } - private IOException asIOException(StorageException oops) { + private IOException asIoException(StorageException oops) { // RPC API can only throw StorageException, but CloudStorageFileSystemProvider // can only throw IOException. Square peg, round hole. // TODO(#810): Research if other codes should be translated similarly. diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageReadChannel.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageReadChannel.java index bb03e53fc362..bd9cb9def6c5 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageReadChannel.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageReadChannel.java @@ -41,6 +41,10 @@ @ThreadSafe final class CloudStorageReadChannel implements SeekableByteChannel { + private final ReadChannel channel; + private long position; + private long size; + @CheckReturnValue @SuppressWarnings("resource") static CloudStorageReadChannel create(Storage gcsStorage, BlobId file, long position) @@ -54,10 +58,6 @@ static CloudStorageReadChannel create(Storage gcsStorage, BlobId file, long posi return new CloudStorageReadChannel(position, size, channel); } - private final ReadChannel channel; - private long position; - private long size; - private CloudStorageReadChannel(long position, long size, ReadChannel channel) { this.position = position; this.size = size; diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProviderTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProviderTest.java index 6e19a1d7bb11..5475459d9376 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProviderTest.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProviderTest.java @@ -22,6 +22,7 @@ import static java.nio.file.StandardCopyOption.ATOMIC_MOVE; import static java.nio.file.StandardCopyOption.COPY_ATTRIBUTES; import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; +import static java.nio.file.StandardOpenOption.CREATE; import static java.nio.file.StandardOpenOption.CREATE_NEW; import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING; import static java.nio.file.StandardOpenOption.WRITE; @@ -53,8 +54,6 @@ import java.nio.file.OpenOption; import java.nio.file.Path; import java.nio.file.Paths; -import java.nio.file.StandardCopyOption; -import java.nio.file.StandardOpenOption; import java.util.List; /** @@ -348,7 +347,7 @@ public void testWrite() throws IOException { @Test public void testWriteOnClose() throws IOException { Path path = Paths.get(URI.create("gs://greenbean/adipose")); - try (SeekableByteChannel chan = Files.newByteChannel(path, StandardOpenOption.WRITE)) { + try (SeekableByteChannel chan = Files.newByteChannel(path, WRITE)) { // writing lots of contents to defeat channel-internal buffering. for (int i = 0; i < 9999; i++) { for (String s : FILE_CONTENTS) { @@ -627,8 +626,8 @@ public void testNullness() throws IOException, NoSuchMethodException, SecurityEx tester.ignore(CloudStorageFileSystemProvider.class.getMethod("equals", Object.class)); tester.setDefault(URI.class, URI.create("gs://blood")); tester.setDefault(Path.class, fs.getPath("and/one")); - tester.setDefault(OpenOption.class, StandardOpenOption.CREATE); - tester.setDefault(CopyOption.class, StandardCopyOption.COPY_ATTRIBUTES); + tester.setDefault(OpenOption.class, CREATE); + tester.setDefault(CopyOption.class, COPY_ATTRIBUTES); // can't do that, setGCloudOptions accepts a null argument. // TODO(jart): Figure out how to re-enable this. // tester.testAllPublicStaticMethods(CloudStorageFileSystemProvider.class); diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageReadChannelTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageReadChannelTest.java index 64cb5b2bdc42..ac42be8cd155 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageReadChannelTest.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageReadChannelTest.java @@ -54,7 +54,7 @@ public class CloudStorageReadChannelTest { private final Storage gcsStorage = mock(Storage.class); private final BlobId file = BlobId.of("blob", "attack"); - private Blob metadata = mock(Blob.class); + private final Blob metadata = mock(Blob.class); private final ReadChannel gcsChannel = mock(ReadChannel.class); @Before diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageWriteChannelTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageWriteChannelTest.java index f652812a13d6..8e95e04b0616 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageWriteChannelTest.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/CloudStorageWriteChannelTest.java @@ -49,7 +49,7 @@ public class CloudStorageWriteChannelTest { @Rule public final ExpectedException thrown = ExpectedException.none(); private final WriteChannel gcsChannel = mock(WriteChannel.class); - private CloudStorageWriteChannel chan = new CloudStorageWriteChannel(gcsChannel); + private final CloudStorageWriteChannel chan = new CloudStorageWriteChannel(gcsChannel); @Before public void before() { diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/FakeStorageRpc.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/FakeStorageRpc.java index a5aad9d78fbf..e7b10677cfcf 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/FakeStorageRpc.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/FakeStorageRpc.java @@ -265,12 +265,9 @@ public String open(StorageObject object, Map options) throws StorageE String key = fullname(object); boolean mustNotExist = false; for (Option option : options.keySet()) { - if (option instanceof StorageRpc.Option) { - // this is a bit of a hack, since we don't implement generations. - if ((StorageRpc.Option) option == Option.IF_GENERATION_MATCH - && ((Long) options.get(option)).longValue() == 0L) { - mustNotExist = true; - } + // this is a bit of a hack, since we don't implement generations. + if (option == Option.IF_GENERATION_MATCH && ((Long) options.get(option)) == 0L) { + mustNotExist = true; } } if (mustNotExist && metadata.containsKey(key)) { @@ -314,12 +311,10 @@ public RewriteResponse openRewrite(RewriteRequest rewriteRequest) throws Storage boolean mustNotExist = false; for (Option option : rewriteRequest.targetOptions.keySet()) { - if (option instanceof StorageRpc.Option) { - // this is a bit of a hack, since we don't implement generations. - if ((StorageRpc.Option) option == Option.IF_GENERATION_MATCH - && ((Long) rewriteRequest.targetOptions.get(option)).longValue() == 0L) { - mustNotExist = true; - } + // this is a bit of a hack, since we don't implement generations. + if (option == Option.IF_GENERATION_MATCH + && (Long) rewriteRequest.targetOptions.get(option) == 0L) { + mustNotExist = true; } } From c65d2fc0966fbfc78b341e9044d19f539d33dd45 Mon Sep 17 00:00:00 2001 From: Marco Ziccardi Date: Thu, 30 Jun 2016 10:39:58 +0200 Subject: [PATCH 37/38] Minor javadoc fixes to gcloud-java-nio --- gcloud-java-contrib/README.md | 2 +- .../gcloud-java-nio-examples/README.md | 19 +++++++++-------- .../gcloud-java-nio-examples/pom.xml | 2 +- gcloud-java-contrib/gcloud-java-nio/README.md | 2 +- gcloud-java-contrib/gcloud-java-nio/pom.xml | 2 +- .../nio/CloudStorageConfiguration.java | 2 +- .../contrib/nio/CloudStorageFileSystem.java | 12 ++++++----- .../nio/CloudStorageFileSystemProvider.java | 7 ++++--- .../contrib/nio/CloudStorageOption.java | 6 +++--- .../contrib/nio/CloudStorageOptions.java | 2 +- .../storage/contrib/nio/CloudStoragePath.java | 7 ++++--- .../storage/contrib/nio/CloudStorageUtil.java | 6 +++--- .../storage/contrib/nio/it/ITGcsNio.java | 21 +++++++++---------- gcloud-java-examples/README.md | 12 ++++++----- .../com/google/cloud/examples/nio/Stat.java | 10 ++++----- .../cloud/storage/testing/package-info.java | 19 +---------------- 16 files changed, 60 insertions(+), 71 deletions(-) diff --git a/gcloud-java-contrib/README.md b/gcloud-java-contrib/README.md index 0a0ad0dd61a6..85808ae24a07 100644 --- a/gcloud-java-contrib/README.md +++ b/gcloud-java-contrib/README.md @@ -13,7 +13,7 @@ Contents -------- * [gcloud-java-nio](./gcloud-java-nio/): NIO Filesystem Provider for Google Cloud Storage. - * [gcloud-java-nio-examples](./gcloud-java-nio-examples/): How to add GCS NIO after the fact. + * [gcloud-java-nio-examples](./gcloud-java-nio-examples/): How to add Google Cloud Storage NIO after the fact. Quickstart ---------- diff --git a/gcloud-java-contrib/gcloud-java-nio-examples/README.md b/gcloud-java-contrib/gcloud-java-nio-examples/README.md index c9db073aa933..9a07c31177f2 100644 --- a/gcloud-java-contrib/gcloud-java-nio-examples/README.md +++ b/gcloud-java-contrib/gcloud-java-nio-examples/README.md @@ -1,8 +1,8 @@ Example of adding the Google Cloud Storage NIO Provider to a legacy jar ======================================================================= -This project shows how to add GCS capabilities to a jar file for a Java 7 application -that uses Java NIO without the need to recompile. +This project shows how to add Google Cloud Storage capabilities to a jar file for a Java 7 +application that uses Java NIO without the need to recompile. Note that whenever possible, you instead want to recompile the app and use the normal dependency mechanism to add a dependency to gcloud-java-nio. You can see examples of @@ -25,19 +25,20 @@ To run this example: java -cp gcloud-java-contrib/gcloud-java-nio/target/gcloud-java-nio-0.2.5-SNAPSHOT-shaded.jar:gcloud-java-contrib/gcloud-java-nio-examples/target/gcloud-java-nio-examples-0.2.5-SNAPSHOT.jar com.google.cloud.nio.examples.ListFilesystems ``` - Notice that it lists gcs, which it wouldn't if you ran it without the nio jar: + Notice that it lists Google Cloud Storage, which it wouldn't if you ran it without the NIO jar: ``` java -cp gcloud-java-contrib/gcloud-java-nio-examples/target/gcloud-java-nio-examples-0.2.5-SNAPSHOT.jar com.google.cloud.nio.examples.ListFilesystems ``` -The sample doesn't have anything about GCS in it. It gets that ability from the nio jar that -we're adding to the classpath. You can use the nio "fat shaded" jar for this purpose as it also -includes the dependencies for gcloud-java-nio. +The sample doesn't have anything about Google Cloud Storage in it. It gets that ability from the NIO +jar that we're adding to the classpath. You can use the NIO "fat shaded" jar for this purpose as it +also includes the dependencies for gcloud-java-nio. The underlying mechanism is Java's standard [ServiceLoader](https://docs.oracle.com/javase/7/docs/api/java/util/ServiceLoader.html) facility, the [standard way](http://docs.oracle.com/javase/7/docs/technotes/guides/io/fsp/filesystemprovider.html) to plug in NIO providers like this one. If you have access to a project's source code you can also simply add gcloud-java-nio as -a dependency and let Maven pull in the required dependencies (this is what the nio unit tests do). -This approach is preferable as the fat jar approach may waste memory on multiple copies of dependencies. +a dependency and let Maven pull in the required dependencies (this is what the NIO unit tests do). +This approach is preferable as the fat jar approach may waste memory on multiple copies of +dependencies. -[developers-console]:https://console.developers.google.com/ \ No newline at end of file +[developers-console]:https://console.developers.google.com/ diff --git a/gcloud-java-contrib/gcloud-java-nio-examples/pom.xml b/gcloud-java-contrib/gcloud-java-nio-examples/pom.xml index c44b927f25ba..448c38a2e8e2 100644 --- a/gcloud-java-contrib/gcloud-java-nio-examples/pom.xml +++ b/gcloud-java-contrib/gcloud-java-nio-examples/pom.xml @@ -6,7 +6,7 @@ GCloud Java NIO examples https://github.com/GoogleCloudPlatform/gcloud-java/tree/master/gcloud-java-contrib/gcloud-java-nio-examples - Demonstrates how to use the gcloud-java-nio jar to add GCS functionality to legacy code. + Demonstrates how to use the gcloud-java-nio jar to add Google Cloud Storage functionality to legacy code. com.google.cloud diff --git a/gcloud-java-contrib/gcloud-java-nio/README.md b/gcloud-java-contrib/gcloud-java-nio/README.md index 9a15d8e24458..ecb541971020 100644 --- a/gcloud-java-contrib/gcloud-java-nio/README.md +++ b/gcloud-java-contrib/gcloud-java-nio/README.md @@ -141,7 +141,7 @@ yet implemented: * Resuming upload or download * Generations * File attributes - * (more) + * (more - list is not exhaustive) Some features are not on the roadmap: this library would be a poor choice to mirror a local filesystem onto the cloud because Google Cloud Storage has a diff --git a/gcloud-java-contrib/gcloud-java-nio/pom.xml b/gcloud-java-contrib/gcloud-java-nio/pom.xml index 3c988e768bbc..a63b8b45856e 100644 --- a/gcloud-java-contrib/gcloud-java-nio/pom.xml +++ b/gcloud-java-contrib/gcloud-java-nio/pom.xml @@ -6,7 +6,7 @@ GCloud Java NIO https://github.com/GoogleCloudPlatform/gcloud-java/tree/master/gcloud-java-contrib/gcloud-java-nio - FileSystemProvider for Java NIO to access GCS transparently. + FileSystemProvider for Java NIO to access Google Cloud Storage transparently. com.google.cloud diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageConfiguration.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageConfiguration.java index fa21c5f8cdd2..efa086d7db80 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageConfiguration.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageConfiguration.java @@ -55,7 +55,7 @@ public abstract class CloudStorageConfiguration { public abstract boolean usePseudoDirectories(); /** - * Returns block size (in bytes) used when talking to the GCS HTTP server. + * Returns block size (in bytes) used when talking to the Google Cloud Storage HTTP server. */ public abstract int blockSize(); diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystem.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystem.java index ce12ff3eee97..60a39fb5a817 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystem.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystem.java @@ -97,9 +97,10 @@ public static CloudStorageFileSystem forBucket(String bucket, CloudStorageConfig /** * Returns Google Cloud Storage {@link FileSystem} object for {@code bucket}. * - *

          GCS file system objects are basically free. You can create as many as you want, even if you - * have multiple instances for the same bucket. There's no actual system resources associated - * with this object. Therefore calling {@link #close()} on the returned value is optional. + *

          Google Cloud Storage file system objects are basically free. You can create as many as you + * want, even if you have multiple instances for the same bucket. There's no actual system + * resources associated with this object. Therefore calling {@link #close()} on the returned value + * is optional. * *

          Note: It is also possible to instantiate this class via Java's * {@code FileSystems.getFileSystem(URI.create("gs://bucket"))}. We discourage you @@ -151,7 +152,7 @@ public CloudStorageConfiguration config() { public CloudStoragePath getPath(String first, String... more) { checkArgument( !first.startsWith(URI_SCHEME + ":"), - "GCS FileSystem.getPath() must not have schema and bucket name: %s", + "Google Cloud Storage FileSystem.getPath() must not have schema and bucket name: %s", first); return CloudStoragePath.getPath(this, first, more); } @@ -196,7 +197,8 @@ public Iterable getRootDirectories() { } /** - * Returns nothing because GCS doesn't have disk partitions of limited size, or anything similar. + * Returns nothing because Google Cloud Storage doesn't have disk partitions of limited size, or + * anything similar. */ @Override public Iterable getFileStores() { diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProvider.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProvider.java index cc8bd5cf0bbf..624c410ddb5c 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProvider.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageFileSystemProvider.java @@ -77,7 +77,8 @@ * *

          Note: This class should never be used directly. This class is instantiated by the * service loader and called through a standardized API, e.g. {@link java.nio.file.Files}. However - * the javadocs in this class serve as useful documentation for the behavior of the GCS NIO library. + * the javadocs in this class serve as useful documentation for the behavior of the Google Cloud + * Storage NIO library. */ @Singleton @ThreadSafe @@ -481,7 +482,7 @@ public boolean isSameFile(Path path, Path path2) { } /** - * Always returns {@code false}, because GCS doesn't support hidden files. + * Always returns {@code false}, because Google Cloud Storage doesn't support hidden files. */ @Override public boolean isHidden(Path path) { @@ -561,7 +562,7 @@ public V getFileAttributeView( } /** - * Does nothing since GCS uses fake directories. + * Does nothing since Google Cloud Storage uses fake directories. */ @Override public void createDirectory(Path dir, FileAttribute... attrs) { diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageOption.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageOption.java index 266bd67c2c84..d4921e10a0ec 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageOption.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageOption.java @@ -25,17 +25,17 @@ public interface CloudStorageOption { /** - * Interface for GCS options that can be specified when opening files. + * Interface for Google Cloud Storage options that can be specified when opening files. */ interface Open extends CloudStorageOption, OpenOption {} /** - * Interface for GCS options that can be specified when copying files. + * Interface for Google Cloud Storage options that can be specified when copying files. */ interface Copy extends CloudStorageOption, CopyOption {} /** - * Interface for GCS options that can be specified when opening or copying files. + * Interface for Google Cloud Storage options that can be specified when opening or copying files. */ interface OpenCopy extends Open, Copy {} } diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageOptions.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageOptions.java index 6ee762eff0ea..74293b7a79f2 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageOptions.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageOptions.java @@ -83,7 +83,7 @@ public static CloudStorageOption.OpenCopy withUserMetadata(String key, String va } /** - * Sets the block size (in bytes) when talking to the GCS server. + * Sets the block size (in bytes) when talking to the Google Cloud Storage server. * *

          The default is {@value CloudStorageFileSystem#BLOCK_SIZE_DEFAULT}. */ diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStoragePath.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStoragePath.java index fdb407ba1852..7c9154b20d93 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStoragePath.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStoragePath.java @@ -139,7 +139,8 @@ private UnixPath toRealPathInternal(boolean errorCheck) { objectName = objectName.removeBeginningSeparator(); } checkArgument( - !errorCheck || !objectName.isEmpty(), "I/O not allowed on empty GCS object names."); + !errorCheck || !objectName.isEmpty(), + "I/O not allowed on empty Google Cloud Storage object names."); return objectName; } @@ -256,8 +257,8 @@ public WatchKey register(WatchService watcher, Kind... events) { } /** - * Throws {@link UnsupportedOperationException} because GCS files are not backed by the local file - * system. + * Throws {@link UnsupportedOperationException} because Google Cloud Storage files are not backed + * by the local file system. */ @Override public File toFile() { diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageUtil.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageUtil.java index 78a602e407b3..14b1921a7b9e 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageUtil.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/CloudStorageUtil.java @@ -36,9 +36,9 @@ static void checkBucket(String bucket) { "Invalid bucket name: '" + bucket + "'. " - + "GCS bucket names must contain only lowercase letters, numbers, dashes (-), " - + "underscores (_), and dots (.). Bucket names must start and end with a number or a " - + "letter. See the following page for more details: " + + "Google Cloud Storage bucket names must contain only lowercase letters, numbers, " + + "dashes (-), underscores (_), and dots (.). Bucket names must start and end with a " + + "number or a letter. See the following page for more details: " + "https://developers.google.com/storage/docs/bucketnaming"); } diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/it/ITGcsNio.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/it/ITGcsNio.java index 949d98051eea..dad6fc35ba2d 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/it/ITGcsNio.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/cloud/storage/contrib/nio/it/ITGcsNio.java @@ -58,21 +58,20 @@ import java.util.logging.Logger; /** - * Integration test for gcloud-nio. + * Integration test for gcloud-java-nio. * - *

          This test actually talks to GCS (you need an account) and tests both reading and writing. You - * *must* set the {@code GOOGLE_APPLICATION_CREDENTIALS} environment variable for this test to work. - * It must contain the name of a local file that contains your Service Account JSON Key. + *

          This test actually talks to Google Cloud Storage (you need an account) and tests both reading + * and writing. You *must* set the {@code GOOGLE_APPLICATION_CREDENTIALS} environment variable for + * this test to work. It must contain the name of a local file that contains your Service Account + * JSON Key. * - *

          The instructions for how to get the Service Account JSON Key are - * at https://cloud.google.com/storage/docs/authentication?hl=en#service_accounts + *

          See + * Service Accounts for instructions on how to get the Service Account JSON Key. * - *

          The short version is this: go to cloud.google.com/console, - * select your project, search for "API manager", click "Credentials", - * click "create credentials/service account key", new service account, - * JSON. The contents of the file that's sent to your browsers is your + *

          The short version is this: go to cloud.google.com/console, select your project, search for + * "API manager", click "Credentials", click "create credentials/service account key", new service + * account, JSON. The contents of the file that's sent to your browsers is your * "Service Account JSON Key". - * */ @RunWith(JUnit4.class) public class ITGcsNio { diff --git a/gcloud-java-examples/README.md b/gcloud-java-examples/README.md index 50ac44e11c47..0a09b14f31e5 100644 --- a/gcloud-java-examples/README.md +++ b/gcloud-java-examples/README.md @@ -46,9 +46,9 @@ To run examples from your command line: Before running the example, go to the [Google Developers Console][developers-console] to ensure that BigQuery API is enabled. You can upload a CSV file `my_csv_file` to the `my_bucket` bucket - (replace `my_csv_file` and `my_bucket` with actual file and bucket names) using the GCS - [web browser](https://console.developers.google.com/storage/browser). The CSV file will be used to - load data into a BigQuery table and should look something like: + (replace `my_csv_file` and `my_bucket` with actual file and bucket names) using the Google Cloud + Storage [web browser](https://console.developers.google.com/storage/browser). The CSV file will + be used to load data into a BigQuery table and should look something like: ```csv value1 value2 @@ -82,6 +82,7 @@ To run examples from your command line: target/appassembler/bin/DatastoreExample your-project-id my_name add my\ comment target/appassembler/bin/DatastoreExample your-project-id my_name display target/appassembler/bin/DatastoreExample your-project-id my_name delete + target/appassembler/bin/DatastoreExample your-project-id my_name set myname@mydomain.com 1234 ``` * Here's an example run of `DnsExample`. @@ -143,8 +144,9 @@ To run examples from your command line: target/appassembler/bin/Stat gs://mybucket/myfile.txt ``` - The sample doesn't have anything special about GCS in it, it just opens files via the NIO API. - It lists gcloud-java-nio as a dependency, and that enables it to interpret `gs://` paths. + The sample doesn't have anything special about Google Cloud Storage in it, it just opens files + via the NIO API. It lists gcloud-java-nio as a dependency, and that enables it to interpret + `gs://` paths. Troubleshooting --------------- diff --git a/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/Stat.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/Stat.java index 19933204705b..35a1a2ae7c7f 100644 --- a/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/Stat.java +++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/Stat.java @@ -31,9 +31,9 @@ * *

          It's meant to be used to test GCloud's integration with Java NIO. * - *

          You can either use the '--check' argument to see whether GCS is enabled, - * or you can directly pass in a GCS file name to use. In that case you have to - * be logged in (using e.g. the gcloud auth command). + *

          You can either use the '--check' argument to see whether Google Cloud Storage is enabled, or + * you can directly pass in a Google Cloud Storage file name to use. In that case you have to be + * logged in (using e.g. the gcloud auth command). * *

          See the * @@ -92,13 +92,13 @@ private static void help() { " to list the filesystem providers.", "", " * --check", - " to double-check the GCS provider is installed.", + " to double-check the Google Cloud Storage provider is installed.", "", "The purpose of this tool is to demonstrate that the gcloud NIO filesystem provider", "can add Google Cloud Storage support to programs not explicitly designed for it.", "", "This tool normally knows nothing of Google Cloud Storage. If you pass it --check", - "or a GCS file name (e.g. gs://mybucket/myfile), it will show an error.", + "or a Google Cloud Storage file name (e.g. gs://mybucket/myfile), it will show an error.", "However, by just adding the gcloud-nio jar as a dependency and recompiling, this tool is", "made aware of gs:// paths and can access files on the cloud.", "", diff --git a/gcloud-java-storage/src/main/java/com/google/cloud/storage/testing/package-info.java b/gcloud-java-storage/src/main/java/com/google/cloud/storage/testing/package-info.java index 3fbf95fb29f7..a8732b029900 100644 --- a/gcloud-java-storage/src/main/java/com/google/cloud/storage/testing/package-info.java +++ b/gcloud-java-storage/src/main/java/com/google/cloud/storage/testing/package-info.java @@ -15,9 +15,7 @@ */ /** - * Two testing helpers for Google Cloud Storage. - * - * RemoteGcsHelper helps with testing on the actual cloud. + * A testing helper for Google Cloud Storage. * *

          A simple usage example: * @@ -34,21 +32,6 @@ * RemoteStorageHelper.forceDelete(storage, bucket, 5, TimeUnit.SECONDS); * }

          * - * LocalGcsHelper helps with testing on an in-memory filesystem (this is best for unit tests). - * Note that this filesystem isn't a complete implementation. In particular, it is not thread-safe. - * - *

          A simple usage example: - * - *

          - * CloudStorageFileSystemProvider.setGCloudOptions(LocalGcsHelper.options());
          - * Path path = Paths.get(URI.create("gs://bucket/wednesday"));
          - * thrown.expect(NoSuchFileException.class);
          - * Files.newByteChannel(path);
          - * 
          - * - *

          The first line ensures that the test uses the in-memory GCS instead of the real one - * (note that you have to set the cloud options before the first usage of the gs:// prefix). - * * @see * gcloud-java tools for testing */ From 90f2c4bab617fcc29c3f046850f27d826e5f2151 Mon Sep 17 00:00:00 2001 From: Marco Ziccardi Date: Sun, 3 Jul 2016 15:32:41 +0200 Subject: [PATCH 38/38] Refactor nio's package-info and add snippets --- .../storage/contrib/nio/package-info.java | 92 ++++++++++--------- .../CreateCloudStorageFileSystem.java | 42 +++++++++ .../nio/snippets/CreateInputStream.java | 37 ++++++++ .../examples/nio/snippets/GetFileSystem.java | 41 +++++++++ .../examples/nio/snippets/ReadAllLines.java | 36 ++++++++ .../nio/snippets/WriteFileWithAttributes.java | 48 ++++++++++ 6 files changed, 251 insertions(+), 45 deletions(-) create mode 100644 gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/snippets/CreateCloudStorageFileSystem.java create mode 100644 gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/snippets/CreateInputStream.java create mode 100644 gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/snippets/GetFileSystem.java create mode 100644 gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/snippets/ReadAllLines.java create mode 100644 gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/snippets/WriteFileWithAttributes.java diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/package-info.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/package-info.java index bee99002b82f..1376ca25d02e 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/package-info.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/cloud/storage/contrib/nio/package-info.java @@ -22,43 +22,55 @@ * *

          How It Works

          * - * The simplest way to get started is with {@code Paths} and {@code Files}:
             {@code
          - *
          + * The simplest way to get started is with {@code Paths} and {@code Files}:
          + * 
          {@code
            *   Path path = Paths.get(URI.create("gs://bucket/lolcat.csv"));
          - *   List lines = Files.readAllLines(path, StandardCharsets.UTF_8);}
          + * List lines = Files.readAllLines(path, StandardCharsets.UTF_8); + * }
          + * + *

          For the complete source code see + * + * ReadAllLines.java. * *

          If you want to configure the bucket per-environment, it might make more sense to use the * {@code FileSystem} API: - *

          - *   class Foo {
          - *     static String bucket = System.getProperty(...);
          - *     static FileSystem fs = FileSystems.getFileSystem(URI.create("gs://" + bucket));
          - *     void bar() {
          - *       byte[] data = "hello world".getBytes(StandardCharsets.UTF_8);
          - *       Path path = fs.getPath("/object");
          - *       Files.write(path, data);
          - *       data = Files.readBytes(path);
          - *     }
          - *     void doodle() {
          - *       Path path = fs.getPath("/path/to/doodle.csv");
          - *       List<String> lines = Files.readAllLines(path, StandardCharsets.UTF_8);
          - *     }
          - *   }
          - * - *

          You can also use InputStream and OutputStream for streaming: + *

          {@code
          + *   FileSystem fs = FileSystems.getFileSystem(URI.create("gs://bucket"));
          + *   byte[] data = "hello world".getBytes(StandardCharsets.UTF_8);
          + *   Path path = fs.getPath("/object");
          + *   Files.write(path, data);
          + *   List lines = Files.readAllLines(path, StandardCharsets.UTF_8);
          + * }
          + * + *

          For the complete source code see + * + * GetFileSystem.java. + * + *

          You can also use {@code InputStream} and {@code OutputStream} for streaming: *

            *   Path path = Paths.get(URI.create("gs://bucket/lolcat.csv"));
          - *   try (InputStream input = Files.openInputStream(path)) {
          - *     // ...
          - *   }
          + * try (InputStream input = Files.newInputStream(path)) { + * // use input stream + * } + * + * + *

          For the complete source code see + * + * CreateInputStream.java. * *

          You can set various attributes using * {@link com.google.cloud.storage.contrib.nio.CloudStorageOptions CloudStorageOptions} static * helpers: *

          - *   Files.write(csvPath, csvLines, StandardCharsets.UTF_8,
          - *       withMimeType(MediaType.CSV_UTF8),
          - *       withoutCaching());
          + * Path path = Paths.get(URI.create("gs://bucket/lolcat.csv")); + * Files.write(path, csvLines, StandardCharsets.UTF_8, + * withMimeType("text/csv; charset=UTF-8"), + * withoutCaching()); + * + * + *

          For the complete source code see + * + * WriteFileWithAttributes.java. * *

          NOTE: Cloud Storage uses a flat namespace and therefore doesn't support real * directories. So this library supports what's known as "pseudo-directories". Any path that @@ -67,32 +79,22 @@ * would with the normal UNIX file system implementation. You can disable this feature with * {@link com.google.cloud.storage.contrib.nio.CloudStorageConfiguration#usePseudoDirectories()}. * - *

          Unit Testing

          - * - *

          Here's a simple unit test:

          - *
          - *   class MyTest {
          - *     {@literal @}Rule
          - *     public final AppEngineRule appEngine = AppEngineRule.builder().build();
          - *
          - *     {@literal @}Test
          - *     public test_fileWrite() throws Exception {
          - *       Path path = Paths.get(URI.create("gs://bucket/traditional"));
          - *       Files.write(path, "eyebrow".getBytes(StandardCharsets.US_ASCII));
          - *       assertEquals("eyebrow", new String(Files.readBytes(path), StandardCharsets.US_ASCII));
          - *     }
          - *   }
          - * *

          Non-SPI Interface

          * *

          If you don't want to rely on Java SPI, which requires a META-INF file in your jar generated by - * Google Auto, you can instantiate this file system directly as follows:

             {@code
          + * Google Auto, you can instantiate this file system directly as follows:
            *
          - *   CloudStorageFileSystem fs = CloudStorageFileSystemProvider.forBucket("bucket");
          + * 
          + *   CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("bucket");
            *   byte[] data = "hello world".getBytes(StandardCharsets.UTF_8);
            *   Path path = fs.getPath("/object");
            *   Files.write(path, data);
          - *   data = Files.readBytes(path);}
          + * data = Files.readAllBytes(path); + *
          + * + *

          For the complete source code see + * + * CreateCloudStorageFileSystem.java. */ @javax.annotation.ParametersAreNonnullByDefault package com.google.cloud.storage.contrib.nio; diff --git a/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/snippets/CreateCloudStorageFileSystem.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/snippets/CreateCloudStorageFileSystem.java new file mode 100644 index 000000000000..b646fa4f1f90 --- /dev/null +++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/snippets/CreateCloudStorageFileSystem.java @@ -0,0 +1,42 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * 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 com.google.cloud.examples.nio.snippets; + +import com.google.cloud.storage.contrib.nio.CloudStorageFileSystem; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; + +/** + * A snippet for Google Cloud Storage NIO that shows how to create a {@link CloudStorageFileSystem} + * for a bucket. The snippet also shows how to create a file, given the file system. + */ +public class CreateCloudStorageFileSystem { + + public static void main(String... args) throws IOException { + // Create a file system for the bucket + CloudStorageFileSystem fs = CloudStorageFileSystem.forBucket("bucket"); + byte[] data = "hello world".getBytes(StandardCharsets.UTF_8); + Path path = fs.getPath("/object"); + // Write a file in the bucket + Files.write(path, data); + // Read a file from the bucket + data = Files.readAllBytes(path); + } +} diff --git a/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/snippets/CreateInputStream.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/snippets/CreateInputStream.java new file mode 100644 index 000000000000..243102d31e01 --- /dev/null +++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/snippets/CreateInputStream.java @@ -0,0 +1,37 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * 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 com.google.cloud.examples.nio.snippets; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +/** + * A snippet showing how to create an input stream for a Google Cloud Storage file using NIO. + */ +public class CreateInputStream { + + public static void main(String... args) throws IOException { + Path path = Paths.get(URI.create("gs://bucket/lolcat.csv")); + try (InputStream input = Files.newInputStream(path)) { + // use input stream + } + } +} diff --git a/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/snippets/GetFileSystem.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/snippets/GetFileSystem.java new file mode 100644 index 000000000000..2819340fa14a --- /dev/null +++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/snippets/GetFileSystem.java @@ -0,0 +1,41 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * 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 com.google.cloud.examples.nio.snippets; + +import java.io.IOException; +import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; + +/** + * A snippet showing how to get a {@link FileSystem} instance for a Google Cloud Storage bucket. + * This snippet also shows how to create a file and read its lines. + */ +public class GetFileSystem { + + public static void main(String... args) throws IOException { + FileSystem fs = FileSystems.getFileSystem(URI.create("gs://bucket")); + byte[] data = "hello world".getBytes(StandardCharsets.UTF_8); + Path path = fs.getPath("/object"); + Files.write(path, data); + List lines = Files.readAllLines(path, StandardCharsets.UTF_8); + } +} diff --git a/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/snippets/ReadAllLines.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/snippets/ReadAllLines.java new file mode 100644 index 000000000000..cbc7baacf48c --- /dev/null +++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/snippets/ReadAllLines.java @@ -0,0 +1,36 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * 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 com.google.cloud.examples.nio.snippets; + +import java.io.IOException; +import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; + +/** + * A snippet showing how to read all lines of a Google Cloud Storage file using NIO. + */ +public class ReadAllLines { + + public static void main(String... args) throws IOException { + Path path = Paths.get(URI.create("gs://bucket/lolcat.csv")); + List lines = Files.readAllLines(path, StandardCharsets.UTF_8); + } +} diff --git a/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/snippets/WriteFileWithAttributes.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/snippets/WriteFileWithAttributes.java new file mode 100644 index 000000000000..d6486b3cfdc2 --- /dev/null +++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/nio/snippets/WriteFileWithAttributes.java @@ -0,0 +1,48 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * 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 com.google.cloud.examples.nio.snippets; + +import static com.google.cloud.storage.contrib.nio.CloudStorageOptions.withMimeType; +import static com.google.cloud.storage.contrib.nio.CloudStorageOptions.withoutCaching; + +import com.google.cloud.storage.contrib.nio.CloudStorageOptions; + +import java.io.IOException; +import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.List; + +/** + * A snippet showing how to write a file to Google Cloud Storage using NIO. This example also shows + * how to set file attributes, using {@link CloudStorageOptions} static helpers. + */ +public class WriteFileWithAttributes { + + private static final String[] LINES = {"value1,", "value"}; + + public static void main(String... args) throws IOException { + List csvLines = Arrays.asList(LINES); + Path path = Paths.get(URI.create("gs://bucket/lolcat.csv")); + Files.write(path, csvLines, StandardCharsets.UTF_8, + withMimeType("text/csv; charset=UTF-8"), + withoutCaching()); + } +}