write =
BlobWriteOption.toWriteOptions(blobInfo, options);
return storage.create(write.x(), content, write.y());
diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/CopyWriter.java b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/CopyWriter.java
index 7eb91d0910a2..743630b6c4c2 100644
--- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/CopyWriter.java
+++ b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/CopyWriter.java
@@ -22,9 +22,9 @@
import com.google.gcloud.Restorable;
import com.google.gcloud.RestorableState;
import com.google.gcloud.RetryHelper;
-import com.google.gcloud.spi.StorageRpc;
-import com.google.gcloud.spi.StorageRpc.RewriteRequest;
-import com.google.gcloud.spi.StorageRpc.RewriteResponse;
+import com.google.gcloud.storage.spi.StorageRpc;
+import com.google.gcloud.storage.spi.StorageRpc.RewriteRequest;
+import com.google.gcloud.storage.spi.StorageRpc.RewriteResponse;
import java.io.Serializable;
import java.util.Map;
@@ -32,7 +32,13 @@
import java.util.concurrent.Callable;
/**
- * Google Storage blob copy writer. This class holds the result of a copy request. If source and
+ * Google Storage blob copy writer. A {@code CopyWriter} object allows to copy both blob's data and
+ * information. To override source blob's information supply a {@code BlobInfo} to the
+ * {@code CopyRequest} using either
+ * {@link Storage.CopyRequest.Builder#target(BlobInfo, Storage.BlobTargetOption...)} or
+ * {@link Storage.CopyRequest.Builder#target(BlobInfo, Iterable)}.
+ *
+ * This class holds the result of a copy request. If source and
* destination blobs share the same location and storage class the copy is completed in one RPC call
* otherwise one or more {@link #copyChunk} calls are necessary to complete the copy. In addition,
* {@link CopyWriter#result()} can be used to automatically complete the copy and return information
@@ -65,11 +71,11 @@ public class CopyWriter implements Restorable {
*
* @throws StorageException upon failure
*/
- public BlobInfo result() {
+ public Blob result() {
while (!isDone()) {
copyChunk();
}
- return BlobInfo.fromPb(rewriteResponse.result);
+ return Blob.fromPb(serviceOptions.service(), rewriteResponse.result);
}
/**
@@ -120,8 +126,10 @@ public RestorableState capture() {
serviceOptions,
BlobId.fromPb(rewriteResponse.rewriteRequest.source),
rewriteResponse.rewriteRequest.sourceOptions,
+ rewriteResponse.rewriteRequest.overrideInfo,
BlobInfo.fromPb(rewriteResponse.rewriteRequest.target),
rewriteResponse.rewriteRequest.targetOptions)
+ .result(rewriteResponse.result != null ? BlobInfo.fromPb(rewriteResponse.result) : null)
.blobSize(blobSize())
.isDone(isDone())
.megabytesCopiedPerChunk(rewriteResponse.rewriteRequest.megabytesRewrittenPerCall)
@@ -132,11 +140,12 @@ public RestorableState capture() {
static class StateImpl implements RestorableState, Serializable {
- private static final long serialVersionUID = 8279287678903181701L;
+ private static final long serialVersionUID = 1693964441435822700L;
private final StorageOptions serviceOptions;
private final BlobId source;
private final Map sourceOptions;
+ private final boolean overrideInfo;
private final BlobInfo target;
private final Map targetOptions;
private final BlobInfo result;
@@ -150,6 +159,7 @@ static class StateImpl implements RestorableState, Serializable {
this.serviceOptions = builder.serviceOptions;
this.source = builder.source;
this.sourceOptions = builder.sourceOptions;
+ this.overrideInfo = builder.overrideInfo;
this.target = builder.target;
this.targetOptions = builder.targetOptions;
this.result = builder.result;
@@ -165,6 +175,7 @@ static class Builder {
private final StorageOptions serviceOptions;
private final BlobId source;
private final Map sourceOptions;
+ private final boolean overrideInfo;
private final BlobInfo target;
private final Map targetOptions;
private BlobInfo result;
@@ -175,11 +186,12 @@ static class Builder {
private Long megabytesCopiedPerChunk;
private Builder(StorageOptions options, BlobId source,
- Map sourceOptions,
- BlobInfo target, Map targetOptions) {
+ Map sourceOptions, boolean overrideInfo, BlobInfo target,
+ Map targetOptions) {
this.serviceOptions = options;
this.source = source;
this.sourceOptions = sourceOptions;
+ this.overrideInfo = overrideInfo;
this.target = target;
this.targetOptions = targetOptions;
}
@@ -220,15 +232,15 @@ RestorableState build() {
}
static Builder builder(StorageOptions options, BlobId source,
- Map sourceOptions, BlobInfo target,
+ Map sourceOptions, boolean overrideInfo, BlobInfo target,
Map targetOptions) {
- return new Builder(options, source, sourceOptions, target, targetOptions);
+ return new Builder(options, source, sourceOptions, overrideInfo, target, targetOptions);
}
@Override
public CopyWriter restore() {
- RewriteRequest rewriteRequest = new RewriteRequest(
- source.toPb(), sourceOptions, target.toPb(), targetOptions, megabytesCopiedPerChunk);
+ RewriteRequest rewriteRequest = new RewriteRequest(source.toPb(), sourceOptions,
+ overrideInfo, target.toPb(), targetOptions, megabytesCopiedPerChunk);
RewriteResponse rewriteResponse = new RewriteResponse(rewriteRequest,
result != null ? result.toPb() : null, blobSize, isDone, rewriteToken,
totalBytesCopied);
@@ -237,8 +249,9 @@ public CopyWriter restore() {
@Override
public int hashCode() {
- return Objects.hash(serviceOptions, source, sourceOptions, target, targetOptions, result,
- blobSize, isDone, megabytesCopiedPerChunk, rewriteToken, totalBytesCopied);
+ return Objects.hash(serviceOptions, source, sourceOptions, overrideInfo, target,
+ targetOptions, result, blobSize, isDone, megabytesCopiedPerChunk, rewriteToken,
+ totalBytesCopied);
}
@Override
@@ -253,6 +266,7 @@ public boolean equals(Object obj) {
return Objects.equals(this.serviceOptions, other.serviceOptions)
&& Objects.equals(this.source, other.source)
&& Objects.equals(this.sourceOptions, other.sourceOptions)
+ && Objects.equals(this.overrideInfo, other.overrideInfo)
&& Objects.equals(this.target, other.target)
&& Objects.equals(this.targetOptions, other.targetOptions)
&& Objects.equals(this.result, other.result)
@@ -267,10 +281,14 @@ public boolean equals(Object obj) {
public String toString() {
return MoreObjects.toStringHelper(this)
.add("source", source)
+ .add("overrideInfo", overrideInfo)
.add("target", target)
- .add("isDone", isDone)
- .add("totalBytesRewritten", totalBytesCopied)
+ .add("result", result)
.add("blobSize", blobSize)
+ .add("isDone", isDone)
+ .add("rewriteToken", rewriteToken)
+ .add("totalBytesCopied", totalBytesCopied)
+ .add("megabytesCopiedPerChunk", megabytesCopiedPerChunk)
.toString();
}
}
diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Option.java b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Option.java
index 2ec8426bfa9f..65c55da7efc8 100644
--- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Option.java
+++ b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Option.java
@@ -19,7 +19,7 @@
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.base.MoreObjects;
-import com.google.gcloud.spi.StorageRpc;
+import com.google.gcloud.storage.spi.StorageRpc;
import java.io.Serializable;
import java.util.Objects;
diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Storage.java b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Storage.java
index 98f3450b7f10..78f421e94e52 100644
--- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Storage.java
+++ b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Storage.java
@@ -24,13 +24,14 @@
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
+import com.google.gcloud.AuthCredentials;
import com.google.gcloud.AuthCredentials.ServiceAccountAuthCredentials;
import com.google.gcloud.Page;
import com.google.gcloud.ReadChannel;
import com.google.gcloud.Service;
import com.google.gcloud.WriteChannel;
-import com.google.gcloud.spi.StorageRpc;
-import com.google.gcloud.spi.StorageRpc.Tuple;
+import com.google.gcloud.storage.spi.StorageRpc;
+import com.google.gcloud.storage.spi.StorageRpc.Tuple;
import java.io.InputStream;
import java.io.Serializable;
@@ -52,8 +53,6 @@
*/
public interface Storage extends Service {
- String DEFAULT_CONTENT_TYPE = "application/octet-stream";
-
enum PredefinedAcl {
AUTHENTICATED_READ("authenticatedRead"),
ALL_AUTHENTICATED_USERS("allAuthenticatedUsers"),
@@ -626,16 +625,16 @@ private BucketListOption(StorageRpc.Option option, Object value) {
}
/**
- * Returns an option to specify the maximum number of buckets to be returned.
+ * Returns an option to specify the maximum number of buckets returned per page.
*/
- public static BucketListOption maxResults(long maxResults) {
- return new BucketListOption(StorageRpc.Option.MAX_RESULTS, maxResults);
+ public static BucketListOption pageSize(long pageSize) {
+ return new BucketListOption(StorageRpc.Option.MAX_RESULTS, pageSize);
}
/**
* Returns an option to specify the page token from which to start listing buckets.
*/
- public static BucketListOption startPageToken(String pageToken) {
+ public static BucketListOption pageToken(String pageToken) {
return new BucketListOption(StorageRpc.Option.PAGE_TOKEN, pageToken);
}
@@ -672,16 +671,16 @@ private BlobListOption(StorageRpc.Option option, Object value) {
}
/**
- * Returns an option to specify the maximum number of blobs to be returned.
+ * Returns an option to specify the maximum number of blobs returned per page.
*/
- public static BlobListOption maxResults(long maxResults) {
- return new BlobListOption(StorageRpc.Option.MAX_RESULTS, maxResults);
+ public static BlobListOption pageSize(long pageSize) {
+ return new BlobListOption(StorageRpc.Option.MAX_RESULTS, pageSize);
}
/**
* Returns an option to specify the page token from which to start listing blobs.
*/
- public static BlobListOption startPageToken(String pageToken) {
+ public static BlobListOption pageToken(String pageToken) {
return new BlobListOption(StorageRpc.Option.PAGE_TOKEN, pageToken);
}
@@ -694,10 +693,17 @@ public static BlobListOption prefix(String prefix) {
}
/**
- * Returns an option to specify whether blob listing should include subdirectories or not.
+ * If specified, results are returned in a directory-like mode. Blobs whose names, after a
+ * possible {@link #prefix(String)}, do not contain the '/' delimiter are returned as is. Blobs
+ * whose names, after a possible {@link #prefix(String)}, contain the '/' delimiter, will have
+ * their name truncated after the delimiter and will be returned as {@link Blob} objects where
+ * only {@link Blob#blobId()}, {@link Blob#size()} and {@link Blob#isDirectory()} are set. For
+ * such directory blobs, ({@link BlobId#generation()} returns {@code null}), {@link Blob#size()}
+ * returns {@code 0} while {@link Blob#isDirectory()} returns {@code true}. Duplicate directory
+ * blobs are omitted.
*/
- public static BlobListOption recursive(boolean recursive) {
- return new BlobListOption(StorageRpc.Option.DELIMITER, recursive);
+ public static BlobListOption currentDirectory() {
+ return new BlobListOption(StorageRpc.Option.DELIMITER, true);
}
/**
@@ -956,6 +962,7 @@ class CopyRequest implements Serializable {
private final BlobId source;
private final List sourceOptions;
+ private final boolean overrideInfo;
private final BlobInfo target;
private final List targetOptions;
private final Long megabytesCopiedPerChunk;
@@ -965,6 +972,7 @@ public static class Builder {
private final Set sourceOptions = new LinkedHashSet<>();
private final Set targetOptions = new LinkedHashSet<>();
private BlobId source;
+ private boolean overrideInfo;
private BlobInfo target;
private Long megabytesCopiedPerChunk;
@@ -1013,39 +1021,38 @@ public Builder sourceOptions(Iterable options) {
*
* @return the builder
*/
- public Builder target(BlobId target) {
- this.target = BlobInfo.builder(target).build();
+ public Builder target(BlobId targetId) {
+ this.overrideInfo = false;
+ this.target = BlobInfo.builder(targetId).build();
return this;
}
/**
* Sets the copy target and target options. {@code target} parameter is used to override
- * source blob information (e.g. {@code contentType}, {@code contentLanguage}). {@code
- * target.contentType} is a required field.
+ * source blob information (e.g. {@code contentType}, {@code contentLanguage}). Target blob
+ * information is set exactly to {@code target}, no information is inherited from the source
+ * blob.
*
* @return the builder
- * @throws IllegalArgumentException if {@code target.contentType} is {@code null}
*/
- public Builder target(BlobInfo target, BlobTargetOption... options)
- throws IllegalArgumentException {
- checkContentType(target);
- this.target = target;
+ public Builder target(BlobInfo target, BlobTargetOption... options) {
+ this.overrideInfo = true;
+ this.target = checkNotNull(target);
Collections.addAll(targetOptions, options);
return this;
}
/**
* Sets the copy target and target options. {@code target} parameter is used to override
- * source blob information (e.g. {@code contentType}, {@code contentLanguage}). {@code
- * target.contentType} is a required field.
+ * source blob information (e.g. {@code contentType}, {@code contentLanguage}). Target blob
+ * information is set exactly to {@code target}, no information is inherited from the source
+ * blob.
*
* @return the builder
- * @throws IllegalArgumentException if {@code target.contentType} is {@code null}
*/
- public Builder target(BlobInfo target, Iterable options)
- throws IllegalArgumentException {
- checkContentType(target);
- this.target = target;
+ public Builder target(BlobInfo target, Iterable options) {
+ this.overrideInfo = true;
+ this.target = checkNotNull(target);
Iterables.addAll(targetOptions, options);
return this;
}
@@ -1066,8 +1073,6 @@ public Builder megabytesCopiedPerChunk(Long megabytesCopiedPerChunk) {
* Creates a {@code CopyRequest} object.
*/
public CopyRequest build() {
- checkNotNull(source);
- checkNotNull(target);
return new CopyRequest(this);
}
}
@@ -1075,6 +1080,7 @@ public CopyRequest build() {
private CopyRequest(Builder builder) {
source = checkNotNull(builder.source);
sourceOptions = ImmutableList.copyOf(builder.sourceOptions);
+ overrideInfo = builder.overrideInfo;
target = checkNotNull(builder.target);
targetOptions = ImmutableList.copyOf(builder.targetOptions);
megabytesCopiedPerChunk = builder.megabytesCopiedPerChunk;
@@ -1101,6 +1107,17 @@ public BlobInfo target() {
return target;
}
+ /**
+ * Returns whether to override the target blob information with {@link #target()}.
+ * If {@code true}, the value of {@link #target()} is used to replace source blob information
+ * (e.g. {@code contentType}, {@code contentLanguage}). Target blob information is set exactly
+ * to this value, no information is inherited from the source blob. If {@code false}, target
+ * blob information is inherited from the source blob.
+ */
+ public boolean overrideInfo() {
+ return overrideInfo;
+ }
+
/**
* Returns blob's target options.
*/
@@ -1119,34 +1136,27 @@ public Long megabytesCopiedPerChunk() {
/**
* Creates a copy request. {@code target} parameter is used to override source blob information
- * (e.g. {@code contentType}, {@code contentLanguage}). {@code target.contentType} is a required
- * field.
+ * (e.g. {@code contentType}, {@code contentLanguage}).
*
* @param sourceBucket name of the bucket containing the source blob
* @param sourceBlob name of the source blob
* @param target a {@code BlobInfo} object for the target blob
* @return a copy request
- * @throws IllegalArgumentException if {@code target.contentType} is {@code null}
*/
- public static CopyRequest of(String sourceBucket, String sourceBlob, BlobInfo target)
- throws IllegalArgumentException {
- checkContentType(target);
+ public static CopyRequest of(String sourceBucket, String sourceBlob, BlobInfo target) {
return builder().source(sourceBucket, sourceBlob).target(target).build();
}
/**
- * Creates a copy request. {@code target} parameter is used to override source blob information
- * (e.g. {@code contentType}, {@code contentLanguage}). {@code target.contentType} is a required
- * field.
+ * Creates a copy request. {@code target} parameter is used to replace source blob information
+ * (e.g. {@code contentType}, {@code contentLanguage}). Target blob information is set exactly
+ * to {@code target}, no information is inherited from the source blob.
*
* @param sourceBlobId a {@code BlobId} object for the source blob
* @param target a {@code BlobInfo} object for the target blob
* @return a copy request
- * @throws IllegalArgumentException if {@code target.contentType} is {@code null}
*/
- public static CopyRequest of(BlobId sourceBlobId, BlobInfo target)
- throws IllegalArgumentException {
- checkContentType(target);
+ public static CopyRequest of(BlobId sourceBlobId, BlobInfo target) {
return builder().source(sourceBlobId).target(target).build();
}
@@ -1208,14 +1218,10 @@ public static CopyRequest of(BlobId sourceBlobId, BlobId targetBlobId) {
public static Builder builder() {
return new Builder();
}
-
- private static void checkContentType(BlobInfo blobInfo) throws IllegalArgumentException {
- checkArgument(blobInfo.contentType() != null, "Blob content type can not be null");
- }
}
/**
- * Create a new bucket.
+ * Creates a new bucket.
*
* @return a complete bucket
* @throws StorageException upon failure
@@ -1223,7 +1229,7 @@ private static void checkContentType(BlobInfo blobInfo) throws IllegalArgumentEx
Bucket create(BucketInfo bucketInfo, BucketTargetOption... options);
/**
- * Create a new blob with no content.
+ * Creates a new blob with no content.
*
* @return a [@code Blob} with complete information
* @throws StorageException upon failure
@@ -1231,7 +1237,7 @@ private static void checkContentType(BlobInfo blobInfo) throws IllegalArgumentEx
Blob create(BlobInfo blobInfo, BlobTargetOption... options);
/**
- * Create a new blob. Direct upload is used to upload {@code content}. For large content,
+ * Creates a new blob. Direct upload is used to upload {@code content}. For large content,
* {@link #writer} is recommended as it uses resumable upload. MD5 and CRC32C hashes of
* {@code content} are computed and used for validating transferred data.
*
@@ -1242,7 +1248,7 @@ private static void checkContentType(BlobInfo blobInfo) throws IllegalArgumentEx
Blob create(BlobInfo blobInfo, byte[] content, BlobTargetOption... options);
/**
- * Create a new blob. Direct upload is used to upload {@code content}. For large content,
+ * Creates a new blob. Direct upload is used to upload {@code content}. For large content,
* {@link #writer} is recommended as it uses resumable upload. By default any md5 and crc32c
* values in the given {@code blobInfo} are ignored unless requested via the
* {@code BlobWriteOption.md5Match} and {@code BlobWriteOption.crc32cMatch} options. The given
@@ -1254,49 +1260,50 @@ private static void checkContentType(BlobInfo blobInfo) throws IllegalArgumentEx
Blob create(BlobInfo blobInfo, InputStream content, BlobWriteOption... options);
/**
- * Return the requested bucket or {@code null} if not found.
+ * Returns the requested bucket or {@code null} if not found.
*
* @throws StorageException upon failure
*/
Bucket get(String bucket, BucketGetOption... options);
/**
- * Return the requested blob or {@code null} if not found.
+ * Returns the requested blob or {@code null} if not found.
*
* @throws StorageException upon failure
*/
Blob get(String bucket, String blob, BlobGetOption... options);
/**
- * Return the requested blob or {@code null} if not found.
+ * Returns the requested blob or {@code null} if not found.
*
* @throws StorageException upon failure
*/
Blob get(BlobId blob, BlobGetOption... options);
/**
- * Return the requested blob or {@code null} if not found.
+ * Returns the requested blob or {@code null} if not found.
*
* @throws StorageException upon failure
*/
Blob get(BlobId blob);
/**
- * List the project's buckets.
+ * Lists the project's buckets.
*
* @throws StorageException upon failure
*/
Page list(BucketListOption... options);
/**
- * List the bucket's blobs.
+ * Lists the bucket's blobs. If the {@link BlobListOption#currentDirectory()} option is provided,
+ * results are returned in a directory-like mode.
*
* @throws StorageException upon failure
*/
Page list(String bucket, BlobListOption... options);
/**
- * Update bucket information.
+ * Updates bucket information.
*
* @return the updated bucket
* @throws StorageException upon failure
@@ -1304,7 +1311,7 @@ private static void checkContentType(BlobInfo blobInfo) throws IllegalArgumentEx
Bucket update(BucketInfo bucketInfo, BucketTargetOption... options);
/**
- * Update blob information. Original metadata are merged with metadata in the provided
+ * Updates blob information. Original metadata are merged with metadata in the provided
* {@code blobInfo}. To replace metadata instead you first have to unset them. Unsetting metadata
* can be done by setting the provided {@code blobInfo}'s metadata to {@code null}.
*
@@ -1321,7 +1328,7 @@ private static void checkContentType(BlobInfo blobInfo) throws IllegalArgumentEx
Blob update(BlobInfo blobInfo, BlobTargetOption... options);
/**
- * Update blob information. Original metadata are merged with metadata in the provided
+ * Updates blob information. Original metadata are merged with metadata in the provided
* {@code blobInfo}. To replace metadata instead you first have to unset them. Unsetting metadata
* can be done by setting the provided {@code blobInfo}'s metadata to {@code null}.
*
@@ -1338,7 +1345,7 @@ private static void checkContentType(BlobInfo blobInfo) throws IllegalArgumentEx
Blob update(BlobInfo blobInfo);
/**
- * Delete the requested bucket.
+ * Deletes the requested bucket.
*
* @return {@code true} if bucket was deleted, {@code false} if it was not found
* @throws StorageException upon failure
@@ -1346,7 +1353,7 @@ private static void checkContentType(BlobInfo blobInfo) throws IllegalArgumentEx
boolean delete(String bucket, BucketSourceOption... options);
/**
- * Delete the requested blob.
+ * Deletes the requested blob.
*
* @return {@code true} if blob was deleted, {@code false} if it was not found
* @throws StorageException upon failure
@@ -1354,7 +1361,7 @@ private static void checkContentType(BlobInfo blobInfo) throws IllegalArgumentEx
boolean delete(String bucket, String blob, BlobSourceOption... options);
/**
- * Delete the requested blob.
+ * Deletes the requested blob.
*
* @return {@code true} if blob was deleted, {@code false} if it was not found
* @throws StorageException upon failure
@@ -1362,7 +1369,7 @@ private static void checkContentType(BlobInfo blobInfo) throws IllegalArgumentEx
boolean delete(BlobId blob, BlobSourceOption... options);
/**
- * Delete the requested blob.
+ * Deletes the requested blob.
*
* @return {@code true} if blob was deleted, {@code false} if it was not found
* @throws StorageException upon failure
@@ -1370,7 +1377,7 @@ private static void checkContentType(BlobInfo blobInfo) throws IllegalArgumentEx
boolean delete(BlobId blob);
/**
- * Send a compose request.
+ * Sends a compose request.
*
* @return the composed blob
* @throws StorageException upon failure
@@ -1378,12 +1385,18 @@ private static void checkContentType(BlobInfo blobInfo) throws IllegalArgumentEx
Blob compose(ComposeRequest composeRequest);
/**
- * Sends a copy request. Returns a {@link CopyWriter} object for the provided
- * {@code CopyRequest}. If source and destination objects share the same location and storage
- * class the source blob is copied with one request and {@link CopyWriter#result()} immediately
- * returns, regardless of the {@link CopyRequest#megabytesCopiedPerChunk} parameter.
- * If source and destination have different location or storage class {@link CopyWriter#result()}
- * might issue multiple RPC calls depending on blob's size.
+ * Sends a copy request. This method copies both blob's data and information. To override source
+ * blob's information supply a {@code BlobInfo} to the
+ * {@code CopyRequest} using either
+ * {@link Storage.CopyRequest.Builder#target(BlobInfo, Storage.BlobTargetOption...)} or
+ * {@link Storage.CopyRequest.Builder#target(BlobInfo, Iterable)}.
+ *
+ * This method returns a {@link CopyWriter} object for the provided {@code CopyRequest}. If
+ * source and destination objects share the same location and storage class the source blob is
+ * copied with one request and {@link CopyWriter#result()} immediately returns, regardless of the
+ * {@link CopyRequest#megabytesCopiedPerChunk} parameter. If source and destination have different
+ * location or storage class {@link CopyWriter#result()} might issue multiple RPC calls depending
+ * on blob's size.
*
*
Example usage of copy:
*
{@code BlobInfo blob = service.copy(copyRequest).result();}
@@ -1422,7 +1435,7 @@ private static void checkContentType(BlobInfo blobInfo) throws IllegalArgumentEx
byte[] readAllBytes(BlobId blob, BlobSourceOption... options);
/**
- * Send a batch request.
+ * Sends a batch request.
*
* @return the batch response
* @throws StorageException upon failure
@@ -1430,7 +1443,7 @@ private static void checkContentType(BlobInfo blobInfo) throws IllegalArgumentEx
BatchResponse submit(BatchRequest batchRequest);
/**
- * Return a channel for reading the blob's content. The blob's latest generation is read. If the
+ * Returns a channel for reading the blob's content. The blob's latest generation is read. If the
* blob changes while reading (i.e. {@link BlobInfo#etag()} changes), subsequent calls to
* {@code blobReadChannel.read(ByteBuffer)} may throw {@link StorageException}.
*
@@ -1443,7 +1456,7 @@ private static void checkContentType(BlobInfo blobInfo) throws IllegalArgumentEx
ReadChannel reader(String bucket, String blob, BlobSourceOption... options);
/**
- * Return a channel for reading the blob's content. If {@code blob.generation()} is set
+ * Returns a channel for reading the blob's content. If {@code blob.generation()} is set
* data corresponding to that generation is read. If {@code blob.generation()} is {@code null}
* the blob's latest generation is read. If the blob changes while reading (i.e.
* {@link BlobInfo#etag()} changes), subsequent calls to {@code blobReadChannel.read(ByteBuffer)}
@@ -1459,7 +1472,7 @@ private static void checkContentType(BlobInfo blobInfo) throws IllegalArgumentEx
ReadChannel reader(BlobId blob, BlobSourceOption... options);
/**
- * Create a blob and return a channel for writing its content. By default any md5 and crc32c
+ * Creates a blob and return a channel for writing its content. By default any md5 and crc32c
* values in the given {@code blobInfo} are ignored unless requested via the
* {@code BlobWriteOption.md5Match} and {@code BlobWriteOption.crc32cMatch} options.
*
@@ -1468,23 +1481,48 @@ private static void checkContentType(BlobInfo blobInfo) throws IllegalArgumentEx
WriteChannel writer(BlobInfo blobInfo, BlobWriteOption... options);
/**
- * Generates a signed URL for a blob.
- * If you have a blob that you want to allow access to for a fixed
- * amount of time, you can use this method to generate a URL that
- * is only valid within a certain time period.
- * This is particularly useful if you don't want publicly
- * accessible blobs, but don't want to require users to explicitly log in.
+ * Generates a signed URL for a blob. If you have a blob that you want to allow access to for a
+ * fixed amount of time, you can use this method to generate a URL that is only valid within a
+ * certain time period. This is particularly useful if you don't want publicly accessible blobs,
+ * but also don't want to require users to explicitly log in. Signing a URL requires a service
+ * account and its associated private key. If a {@link ServiceAccountAuthCredentials} was passed
+ * to {@link StorageOptions.Builder#authCredentials(AuthCredentials)} or the default credentials
+ * are being used and the environment variable {@code GOOGLE_APPLICATION_CREDENTIALS} is set, then
+ * {@code signUrl} will use that service account and associated key to sign the URL. If the
+ * credentials passed to {@link StorageOptions} do not expose a private key (this is the case for
+ * App Engine credentials, Compute Engine credentials and Google Cloud SDK credentials) then
+ * {@code signUrl} will throw an {@link IllegalArgumentException} unless a service account with
+ * associated key is passed using the {@code SignUrlOption.serviceAccount()} option. The service
+ * account and private key passed with {@code SignUrlOption.serviceAccount()} have priority over
+ * any credentials set with {@link StorageOptions.Builder#authCredentials(AuthCredentials)}.
*
- * Example usage of creating a signed URL that is valid for 2 weeks:
+ *
Example usage of creating a signed URL that is valid for 2 weeks, using the default
+ * credentials for signing the URL:
*
{@code
* service.signUrl(BlobInfo.builder("bucket", "name").build(), 14, TimeUnit.DAYS);
* }
*
+ * Example usage of creating a signed URL passing the {@code SignUrlOption.serviceAccount()}
+ * option, that will be used for signing the URL:
+ *
{@code
+ * service.signUrl(BlobInfo.builder("bucket", "name").build(), 14, TimeUnit.DAYS,
+ * SignUrlOption.serviceAccount(
+ * AuthCredentials.createForJson(new FileInputStream("/path/to/key.json"))));
+ * }
+ *
* @param blobInfo the blob associated with the signed URL
* @param duration time until the signed URL expires, expressed in {@code unit}. The finest
* granularity supported is 1 second, finer granularities will be truncated
* @param unit time unit of the {@code duration} parameter
* @param options optional URL signing options
+ * @throws IllegalArgumentException if {@code SignUrlOption.serviceAccount()} was not used and no
+ * service account was provided to {@link StorageOptions}
+ * @throws IllegalArgumentException if the key associated to the provided service account is
+ * invalid
+ * @throws IllegalArgumentException if {@code SignUrlOption.withMd5()} option is used and
+ * {@code blobInfo.md5()} is {@code null}
+ * @throws IllegalArgumentException if {@code SignUrlOption.withContentType()} option is used and
+ * {@code blobInfo.contentType()} is {@code null}
* @see Signed-URLs
*/
URL signUrl(BlobInfo blobInfo, long duration, TimeUnit unit, SignUrlOption... options);
diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/StorageImpl.java b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/StorageImpl.java
index de77cba021a1..cf709ba5e293 100644
--- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/StorageImpl.java
+++ b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/StorageImpl.java
@@ -19,15 +19,15 @@
import static com.google.common.base.MoreObjects.firstNonNull;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.gcloud.RetryHelper.runWithRetries;
-import static com.google.gcloud.spi.StorageRpc.Option.DELIMITER;
-import static com.google.gcloud.spi.StorageRpc.Option.IF_GENERATION_MATCH;
-import static com.google.gcloud.spi.StorageRpc.Option.IF_GENERATION_NOT_MATCH;
-import static com.google.gcloud.spi.StorageRpc.Option.IF_METAGENERATION_MATCH;
-import static com.google.gcloud.spi.StorageRpc.Option.IF_METAGENERATION_NOT_MATCH;
-import static com.google.gcloud.spi.StorageRpc.Option.IF_SOURCE_GENERATION_MATCH;
-import static com.google.gcloud.spi.StorageRpc.Option.IF_SOURCE_GENERATION_NOT_MATCH;
-import static com.google.gcloud.spi.StorageRpc.Option.IF_SOURCE_METAGENERATION_MATCH;
-import static com.google.gcloud.spi.StorageRpc.Option.IF_SOURCE_METAGENERATION_NOT_MATCH;
+import static com.google.gcloud.storage.spi.StorageRpc.Option.DELIMITER;
+import static com.google.gcloud.storage.spi.StorageRpc.Option.IF_GENERATION_MATCH;
+import static com.google.gcloud.storage.spi.StorageRpc.Option.IF_GENERATION_NOT_MATCH;
+import static com.google.gcloud.storage.spi.StorageRpc.Option.IF_METAGENERATION_MATCH;
+import static com.google.gcloud.storage.spi.StorageRpc.Option.IF_METAGENERATION_NOT_MATCH;
+import static com.google.gcloud.storage.spi.StorageRpc.Option.IF_SOURCE_GENERATION_MATCH;
+import static com.google.gcloud.storage.spi.StorageRpc.Option.IF_SOURCE_GENERATION_NOT_MATCH;
+import static com.google.gcloud.storage.spi.StorageRpc.Option.IF_SOURCE_METAGENERATION_MATCH;
+import static com.google.gcloud.storage.spi.StorageRpc.Option.IF_SOURCE_METAGENERATION_NOT_MATCH;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.api.services.storage.model.StorageObject;
@@ -48,9 +48,9 @@
import com.google.gcloud.PageImpl.NextPageFetcher;
import com.google.gcloud.ReadChannel;
import com.google.gcloud.RetryHelper.RetryHelperException;
-import com.google.gcloud.spi.StorageRpc;
-import com.google.gcloud.spi.StorageRpc.RewriteResponse;
-import com.google.gcloud.spi.StorageRpc.Tuple;
+import com.google.gcloud.storage.spi.StorageRpc;
+import com.google.gcloud.storage.spi.StorageRpc.RewriteResponse;
+import com.google.gcloud.storage.spi.StorageRpc.Tuple;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
@@ -76,6 +76,7 @@ final class StorageImpl extends BaseService implements Storage {
private static final byte[] EMPTY_BYTE_ARRAY = {};
private static final String EMPTY_BYTE_ARRAY_MD5 = "1B2M2Y8AsgTpgAmY7PhCfg==";
private static final String EMPTY_BYTE_ARRAY_CRC32C = "AAAAAA==";
+ private static final String PATH_DELIMITER = "/";
private static final Function, Boolean> DELETE_FUNCTION =
new Function, Boolean>() {
@@ -412,15 +413,16 @@ public CopyWriter copy(final CopyRequest copyRequest) {
final StorageObject source = copyRequest.source().toPb();
final Map sourceOptions =
optionMap(copyRequest.source().generation(), null, copyRequest.sourceOptions(), true);
- final StorageObject target = copyRequest.target().toPb();
+ final StorageObject targetObject = copyRequest.target().toPb();
final Map targetOptions = optionMap(copyRequest.target().generation(),
copyRequest.target().metageneration(), copyRequest.targetOptions());
try {
RewriteResponse rewriteResponse = runWithRetries(new Callable() {
@Override
public RewriteResponse call() {
- return storageRpc.openRewrite(new StorageRpc.RewriteRequest(source, sourceOptions, target,
- targetOptions, copyRequest.megabytesCopiedPerChunk()));
+ return storageRpc.openRewrite(new StorageRpc.RewriteRequest(source, sourceOptions,
+ copyRequest.overrideInfo(), targetObject, targetOptions,
+ copyRequest.megabytesCopiedPerChunk()));
}
}, options().retryParams(), EXCEPTION_HANDLER);
return new CopyWriter(options(), rewriteResponse);
@@ -669,7 +671,7 @@ private static void addToOptionMap(StorageRpc.Option getOption, StorageRpc.O
}
Boolean value = (Boolean) temp.remove(DELIMITER);
if (Boolean.TRUE.equals(value)) {
- temp.put(DELIMITER, options().pathDelimiter());
+ temp.put(DELIMITER, PATH_DELIMITER);
}
if (useAsSource) {
addToOptionMap(IF_GENERATION_MATCH, IF_SOURCE_GENERATION_MATCH, generation, temp);
diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/StorageOptions.java b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/StorageOptions.java
index bd30cb173366..e7e1c2778fa9 100644
--- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/StorageOptions.java
+++ b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/StorageOptions.java
@@ -16,14 +16,12 @@
package com.google.gcloud.storage;
-import com.google.common.base.MoreObjects;
import com.google.common.collect.ImmutableSet;
import com.google.gcloud.ServiceOptions;
-import com.google.gcloud.spi.DefaultStorageRpc;
-import com.google.gcloud.spi.StorageRpc;
-import com.google.gcloud.spi.StorageRpcFactory;
+import com.google.gcloud.storage.spi.DefaultStorageRpc;
+import com.google.gcloud.storage.spi.StorageRpc;
+import com.google.gcloud.storage.spi.StorageRpcFactory;
-import java.util.Objects;
import java.util.Set;
public class StorageOptions extends ServiceOptions {
@@ -31,9 +29,6 @@ public class StorageOptions extends ServiceOptions SCOPES = ImmutableSet.of(GCS_SCOPE);
- private static final String DEFAULT_PATH_DELIMITER = "/";
-
- private final String pathDelimiter;
public static class DefaultStorageFactory implements StorageFactory {
@@ -58,24 +53,10 @@ public StorageRpc create(StorageOptions options) {
public static class Builder extends
ServiceOptions.Builder {
- private String pathDelimiter;
-
private Builder() {}
private Builder(StorageOptions options) {
super(options);
- pathDelimiter = options.pathDelimiter;
- }
-
- /**
- * Sets the path delimiter for the storage service.
- *
- * @param pathDelimiter the path delimiter to set
- * @return the builder
- */
- public Builder pathDelimiter(String pathDelimiter) {
- this.pathDelimiter = pathDelimiter;
- return this;
}
@Override
@@ -86,7 +67,6 @@ public StorageOptions build() {
private StorageOptions(Builder builder) {
super(StorageFactory.class, StorageRpcFactory.class, builder);
- pathDelimiter = MoreObjects.firstNonNull(builder.pathDelimiter, DEFAULT_PATH_DELIMITER);
}
@SuppressWarnings("unchecked")
@@ -106,13 +86,6 @@ protected Set scopes() {
return SCOPES;
}
- /**
- * Returns the storage service's path delimiter.
- */
- public String pathDelimiter() {
- return pathDelimiter;
- }
-
/**
* Returns a default {@code StorageOptions} instance.
*/
@@ -128,16 +101,12 @@ public Builder toBuilder() {
@Override
public int hashCode() {
- return baseHashCode() ^ Objects.hash(pathDelimiter);
+ return baseHashCode();
}
@Override
public boolean equals(Object obj) {
- if (!(obj instanceof StorageOptions)) {
- return false;
- }
- StorageOptions other = (StorageOptions) obj;
- return baseEquals(other) && Objects.equals(pathDelimiter, other.pathDelimiter);
+ return obj instanceof StorageOptions && baseEquals((StorageOptions) obj);
}
public static Builder builder() {
diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/spi/DefaultStorageRpc.java b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/spi/DefaultStorageRpc.java
similarity index 86%
rename from gcloud-java-storage/src/main/java/com/google/gcloud/spi/DefaultStorageRpc.java
rename to gcloud-java-storage/src/main/java/com/google/gcloud/storage/spi/DefaultStorageRpc.java
index dc84a1de5559..8d06832534e2 100644
--- a/gcloud-java-storage/src/main/java/com/google/gcloud/spi/DefaultStorageRpc.java
+++ b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/spi/DefaultStorageRpc.java
@@ -12,25 +12,27 @@
* the License.
*/
-package com.google.gcloud.spi;
-
-import static com.google.gcloud.spi.StorageRpc.Option.DELIMITER;
-import static com.google.gcloud.spi.StorageRpc.Option.FIELDS;
-import static com.google.gcloud.spi.StorageRpc.Option.IF_GENERATION_MATCH;
-import static com.google.gcloud.spi.StorageRpc.Option.IF_GENERATION_NOT_MATCH;
-import static com.google.gcloud.spi.StorageRpc.Option.IF_METAGENERATION_MATCH;
-import static com.google.gcloud.spi.StorageRpc.Option.IF_METAGENERATION_NOT_MATCH;
-import static com.google.gcloud.spi.StorageRpc.Option.IF_SOURCE_GENERATION_MATCH;
-import static com.google.gcloud.spi.StorageRpc.Option.IF_SOURCE_GENERATION_NOT_MATCH;
-import static com.google.gcloud.spi.StorageRpc.Option.IF_SOURCE_METAGENERATION_MATCH;
-import static com.google.gcloud.spi.StorageRpc.Option.IF_SOURCE_METAGENERATION_NOT_MATCH;
-import static com.google.gcloud.spi.StorageRpc.Option.MAX_RESULTS;
-import static com.google.gcloud.spi.StorageRpc.Option.PAGE_TOKEN;
-import static com.google.gcloud.spi.StorageRpc.Option.PREDEFINED_ACL;
-import static com.google.gcloud.spi.StorageRpc.Option.PREDEFINED_DEFAULT_OBJECT_ACL;
-import static com.google.gcloud.spi.StorageRpc.Option.PREFIX;
-import static com.google.gcloud.spi.StorageRpc.Option.VERSIONS;
+package com.google.gcloud.storage.spi;
+
+import static com.google.common.base.MoreObjects.firstNonNull;
+import static com.google.gcloud.storage.spi.StorageRpc.Option.DELIMITER;
+import static com.google.gcloud.storage.spi.StorageRpc.Option.FIELDS;
+import static com.google.gcloud.storage.spi.StorageRpc.Option.IF_GENERATION_MATCH;
+import static com.google.gcloud.storage.spi.StorageRpc.Option.IF_GENERATION_NOT_MATCH;
+import static com.google.gcloud.storage.spi.StorageRpc.Option.IF_METAGENERATION_MATCH;
+import static com.google.gcloud.storage.spi.StorageRpc.Option.IF_METAGENERATION_NOT_MATCH;
+import static com.google.gcloud.storage.spi.StorageRpc.Option.IF_SOURCE_GENERATION_MATCH;
+import static com.google.gcloud.storage.spi.StorageRpc.Option.IF_SOURCE_GENERATION_NOT_MATCH;
+import static com.google.gcloud.storage.spi.StorageRpc.Option.IF_SOURCE_METAGENERATION_MATCH;
+import static com.google.gcloud.storage.spi.StorageRpc.Option.IF_SOURCE_METAGENERATION_NOT_MATCH;
+import static com.google.gcloud.storage.spi.StorageRpc.Option.MAX_RESULTS;
+import static com.google.gcloud.storage.spi.StorageRpc.Option.PAGE_TOKEN;
+import static com.google.gcloud.storage.spi.StorageRpc.Option.PREDEFINED_ACL;
+import static com.google.gcloud.storage.spi.StorageRpc.Option.PREDEFINED_DEFAULT_OBJECT_ACL;
+import static com.google.gcloud.storage.spi.StorageRpc.Option.PREFIX;
+import static com.google.gcloud.storage.spi.StorageRpc.Option.VERSIONS;
import static java.net.HttpURLConnection.HTTP_NOT_FOUND;
+import static javax.servlet.http.HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE;
import com.google.api.client.googleapis.batch.json.JsonBatchCallback;
import com.google.api.client.googleapis.json.GoogleJsonError;
@@ -56,8 +58,9 @@
import com.google.api.services.storage.model.ComposeRequest.SourceObjects.ObjectPreconditions;
import com.google.api.services.storage.model.Objects;
import com.google.api.services.storage.model.StorageObject;
-import com.google.common.base.MoreObjects;
+import com.google.common.base.Function;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.gcloud.storage.StorageException;
@@ -66,6 +69,7 @@
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
@@ -99,7 +103,7 @@ private static StorageException translate(GoogleJsonError exception) {
}
@Override
- public Bucket create(Bucket bucket, Map options) throws StorageException {
+ public Bucket create(Bucket bucket, Map options) {
try {
return storage.buckets()
.insert(this.options.projectId(), bucket)
@@ -114,7 +118,7 @@ public Bucket create(Bucket bucket, Map options) throws StorageExcept
@Override
public StorageObject create(StorageObject storageObject, final InputStream content,
- Map options) throws StorageException {
+ Map options) {
try {
Storage.Objects.Insert insert = storage.objects()
.insert(storageObject.getBucket(), storageObject,
@@ -150,7 +154,7 @@ public Tuple> list(Map options) {
}
@Override
- public Tuple> list(String bucket, Map options) {
+ public Tuple> list(final String bucket, Map options) {
try {
Objects objects = storage.objects()
.list(bucket)
@@ -162,13 +166,30 @@ public Tuple> list(String bucket, Map
.setPageToken(PAGE_TOKEN.getString(options))
.setFields(FIELDS.getString(options))
.execute();
- return Tuple.>of(
- objects.getNextPageToken(), objects.getItems());
+ Iterable storageObjects = Iterables.concat(
+ firstNonNull(objects.getItems(), ImmutableList.of()),
+ objects.getPrefixes() != null
+ ? Lists.transform(objects.getPrefixes(), objectFromPrefix(bucket))
+ : ImmutableList.of());
+ return Tuple.of(objects.getNextPageToken(), storageObjects);
} catch (IOException ex) {
throw translate(ex);
}
}
+ private static Function objectFromPrefix(final String bucket) {
+ return new Function() {
+ @Override
+ public StorageObject apply(String prefix) {
+ return new StorageObject()
+ .set("isDirectory", true)
+ .setBucket(bucket)
+ .setName(prefix)
+ .setSize(BigInteger.ZERO);
+ }
+ };
+ }
+
@Override
public Bucket get(Bucket bucket, Map options) {
try {
@@ -296,11 +317,8 @@ private Storage.Objects.Delete deleteRequest(StorageObject blob, Map
@Override
public StorageObject compose(Iterable sources, StorageObject target,
- Map targetOptions) throws StorageException {
+ Map targetOptions) {
ComposeRequest request = new ComposeRequest();
- if (target.getContentType() == null) {
- target.setContentType("application/octet-stream");
- }
request.setDestination(target);
List sourceObjects = new ArrayList<>();
for (StorageObject source : sources) {
@@ -327,8 +345,7 @@ public StorageObject compose(Iterable sources, StorageObject targ
}
@Override
- public byte[] load(StorageObject from, Map options)
- throws StorageException {
+ public byte[] load(StorageObject from, Map options) {
try {
Storage.Objects.Get getRequest = storage.objects()
.get(from.getBucket(), from.getName())
@@ -347,7 +364,7 @@ public byte[] load(StorageObject from, Map options)
}
@Override
- public BatchResponse batch(BatchRequest request) throws StorageException {
+ public BatchResponse batch(BatchRequest request) {
List>>> partitionedToDelete =
Lists.partition(request.toDelete, MAX_BATCH_DELETES);
Iterator>>> iterator = partitionedToDelete.iterator();
@@ -437,7 +454,7 @@ public void onFailure(GoogleJsonError e, HttpHeaders responseHeaders) {
@Override
public Tuple read(StorageObject from, Map options, long position,
- int bytes) throws StorageException {
+ int bytes) {
try {
Get req = storage.objects()
.get(from.getBucket(), from.getName())
@@ -454,20 +471,32 @@ public Tuple read(StorageObject from, Map options, lo
String etag = req.getLastResponseHeaders().getETag();
return Tuple.of(etag, output.toByteArray());
} catch (IOException ex) {
- throw translate(ex);
+ StorageException serviceException = translate(ex);
+ if (serviceException.code() == SC_REQUESTED_RANGE_NOT_SATISFIABLE) {
+ return Tuple.of(null, new byte[0]);
+ }
+ throw serviceException;
}
}
@Override
public void write(String uploadId, byte[] toWrite, int toWriteOffset, long destOffset, int length,
- boolean last) throws StorageException {
+ boolean last) {
try {
+ if (length == 0 && !last) {
+ return;
+ }
GenericUrl url = new GenericUrl(uploadId);
HttpRequest httpRequest = storage.getRequestFactory().buildPutRequest(url,
new ByteArrayContent(null, toWrite, toWriteOffset, length));
long limit = destOffset + length;
StringBuilder range = new StringBuilder("bytes ");
- range.append(destOffset).append('-').append(limit - 1).append('/');
+ if (length == 0) {
+ range.append('*');
+ } else {
+ range.append(destOffset).append('-').append(limit - 1);
+ }
+ range.append('/');
if (last) {
range.append(limit);
} else {
@@ -501,8 +530,7 @@ public void write(String uploadId, byte[] toWrite, int toWriteOffset, long destO
}
@Override
- public String open(StorageObject object, Map options)
- throws StorageException {
+ public String open(StorageObject object, Map options) {
try {
Insert req = storage.objects().insert(object.getBucket(), object);
GenericUrl url = req.buildHttpRequest().getUrl();
@@ -523,7 +551,7 @@ public String open(StorageObject object, Map options)
HttpRequest httpRequest =
requestFactory.buildPostRequest(url, new JsonHttpContent(jsonFactory, object));
httpRequest.getHeaders().set("X-Upload-Content-Type",
- MoreObjects.firstNonNull(object.getContentType(), "application/octet-stream"));
+ firstNonNull(object.getContentType(), "application/octet-stream"));
HttpResponse response = httpRequest.execute();
if (response.getStatusCode() != 200) {
GoogleJsonError error = new GoogleJsonError();
@@ -538,22 +566,22 @@ public String open(StorageObject object, Map options)
}
@Override
- public RewriteResponse openRewrite(RewriteRequest rewriteRequest) throws StorageException {
+ public RewriteResponse openRewrite(RewriteRequest rewriteRequest) {
return rewrite(rewriteRequest, null);
}
@Override
- public RewriteResponse continueRewrite(RewriteResponse previousResponse) throws StorageException {
+ public RewriteResponse continueRewrite(RewriteResponse previousResponse) {
return rewrite(previousResponse.rewriteRequest, previousResponse.rewriteToken);
}
- private RewriteResponse rewrite(RewriteRequest req, String token) throws StorageException {
+ private RewriteResponse rewrite(RewriteRequest req, String token) {
try {
Long maxBytesRewrittenPerCall = req.megabytesRewrittenPerCall != null
? req.megabytesRewrittenPerCall * MEGABYTE : null;
com.google.api.services.storage.model.RewriteResponse rewriteResponse = storage.objects()
.rewrite(req.source.getBucket(), req.source.getName(), req.target.getBucket(),
- req.target.getName(), req.target.getContentType() != null ? req.target : null)
+ req.target.getName(), req.overrideInfo ? req.target : null)
.setSourceGeneration(req.source.getGeneration())
.setRewriteToken(token)
.setMaxBytesRewrittenPerCall(maxBytesRewrittenPerCall)
diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/spi/StorageRpc.java b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/spi/StorageRpc.java
similarity index 72%
rename from gcloud-java-storage/src/main/java/com/google/gcloud/spi/StorageRpc.java
rename to gcloud-java-storage/src/main/java/com/google/gcloud/storage/spi/StorageRpc.java
index e15a27114810..74f8171de87f 100644
--- a/gcloud-java-storage/src/main/java/com/google/gcloud/spi/StorageRpc.java
+++ b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/spi/StorageRpc.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.google.gcloud.spi;
+package com.google.gcloud.storage.spi;
import static com.google.common.base.MoreObjects.firstNonNull;
@@ -138,15 +138,17 @@ class RewriteRequest {
public final StorageObject source;
public final Map sourceOptions;
+ public final boolean overrideInfo;
public final StorageObject target;
public final Map targetOptions;
public final Long megabytesRewrittenPerCall;
public RewriteRequest(StorageObject source, Map sourceOptions,
- StorageObject target, Map targetOptions,
+ boolean overrideInfo, StorageObject target, Map targetOptions,
Long megabytesRewrittenPerCall) {
this.source = source;
this.sourceOptions = sourceOptions;
+ this.overrideInfo = overrideInfo;
this.target = target;
this.targetOptions = targetOptions;
this.megabytesRewrittenPerCall = megabytesRewrittenPerCall;
@@ -163,6 +165,7 @@ public boolean equals(Object obj) {
final RewriteRequest other = (RewriteRequest) obj;
return Objects.equals(this.source, other.source)
&& Objects.equals(this.sourceOptions, other.sourceOptions)
+ && Objects.equals(this.overrideInfo, other.overrideInfo)
&& Objects.equals(this.target, other.target)
&& Objects.equals(this.targetOptions, other.targetOptions)
&& Objects.equals(this.megabytesRewrittenPerCall, other.megabytesRewrittenPerCall);
@@ -170,7 +173,8 @@ public boolean equals(Object obj) {
@Override
public int hashCode() {
- return Objects.hash(source, sourceOptions, target, targetOptions, megabytesRewrittenPerCall);
+ return Objects.hash(source, sourceOptions, overrideInfo, target, targetOptions,
+ megabytesRewrittenPerCall);
}
}
@@ -217,57 +221,134 @@ public int hashCode() {
}
}
- Bucket create(Bucket bucket, Map options) throws StorageException;
+ /**
+ * Creates a new bucket.
+ *
+ * @throws StorageException upon failure
+ */
+ Bucket create(Bucket bucket, Map options);
- StorageObject create(StorageObject object, InputStream content, Map options)
- throws StorageException;
+ /**
+ * Creates a new storage object.
+ *
+ * @throws StorageException upon failure
+ */
+ StorageObject create(StorageObject object, InputStream content, Map options);
- Tuple> list(Map options) throws StorageException;
+ /**
+ * Lists the project's buckets.
+ *
+ * @throws StorageException upon failure
+ */
+ Tuple> list(Map options);
- Tuple> list(String bucket, Map options)
- throws StorageException;
+ /**
+ * Lists the bucket's blobs.
+ *
+ * @throws StorageException upon failure
+ */
+ Tuple> list(String bucket, Map options);
/**
* Returns the requested bucket or {@code null} if not found.
*
* @throws StorageException upon failure
*/
- Bucket get(Bucket bucket, Map options) throws StorageException;
+ Bucket get(Bucket bucket, Map options);
/**
* Returns the requested storage object or {@code null} if not found.
*
* @throws StorageException upon failure
*/
- StorageObject get(StorageObject object, Map options)
- throws StorageException;
+ StorageObject get(StorageObject object, Map options);
- Bucket patch(Bucket bucket, Map options) throws StorageException;
+ /**
+ * Updates bucket information.
+ *
+ * @throws StorageException upon failure
+ */
+ Bucket patch(Bucket bucket, Map options);
- StorageObject patch(StorageObject storageObject, Map options)
- throws StorageException;
+ /**
+ * Updates the storage object's information. Original metadata are merged with metadata in the
+ * provided {@code storageObject}.
+ *
+ * @throws StorageException upon failure
+ */
+ StorageObject patch(StorageObject storageObject, Map options);
- boolean delete(Bucket bucket, Map options) throws StorageException;
+ /**
+ * Deletes the requested bucket.
+ *
+ * @return {@code true} if the bucket was deleted, {@code false} if it was not found
+ * @throws StorageException upon failure
+ */
+ boolean delete(Bucket bucket, Map options);
- boolean delete(StorageObject object, Map options) throws StorageException;
+ /**
+ * Deletes the requested storage object.
+ *
+ * @return {@code true} if the storage object was deleted, {@code false} if it was not found
+ * @throws StorageException upon failure
+ */
+ boolean delete(StorageObject object, Map options);
- BatchResponse batch(BatchRequest request) throws StorageException;
+ /**
+ * Sends a batch request.
+ *
+ * @throws StorageException upon failure
+ */
+ BatchResponse batch(BatchRequest request);
+ /**
+ * Sends a compose request.
+ *
+ * @throws StorageException upon failure
+ */
StorageObject compose(Iterable sources, StorageObject target,
- Map targetOptions) throws StorageException;
+ Map targetOptions);
- byte[] load(StorageObject storageObject, Map options)
- throws StorageException;
+ /**
+ * Reads all the bytes from a storage object.
+ *
+ * @throws StorageException upon failure
+ */
+ byte[] load(StorageObject storageObject, Map options);
- Tuple read(StorageObject from, Map options, long position, int bytes)
- throws StorageException;
+ /**
+ * Reads the given amount of bytes from a storage object at the given position.
+ *
+ * @throws StorageException upon failure
+ */
+ Tuple read(StorageObject from, Map options, long position, int bytes);
- String open(StorageObject object, Map options) throws StorageException;
+ /**
+ * Opens a resumable upload channel for a given storage object.
+ *
+ * @throws StorageException upon failure
+ */
+ String open(StorageObject object, Map options);
+ /**
+ * Writes the provided bytes to a storage object at the provided location.
+ *
+ * @throws StorageException upon failure
+ */
void write(String uploadId, byte[] toWrite, int toWriteOffset, long destOffset, int length,
- boolean last) throws StorageException;
+ boolean last);
- RewriteResponse openRewrite(RewriteRequest rewriteRequest) throws StorageException;
+ /**
+ * Sends a rewrite request to open a rewrite channel.
+ *
+ * @throws StorageException upon failure
+ */
+ RewriteResponse openRewrite(RewriteRequest rewriteRequest);
- RewriteResponse continueRewrite(RewriteResponse previousResponse) throws StorageException;
+ /**
+ * Continues rewriting on an already open rewrite channel.
+ *
+ * @throws StorageException upon failure
+ */
+ RewriteResponse continueRewrite(RewriteResponse previousResponse);
}
diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/spi/StorageRpcFactory.java b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/spi/StorageRpcFactory.java
similarity index 91%
rename from gcloud-java-storage/src/main/java/com/google/gcloud/spi/StorageRpcFactory.java
rename to gcloud-java-storage/src/main/java/com/google/gcloud/storage/spi/StorageRpcFactory.java
index f4959d617d17..19b98e6273db 100644
--- a/gcloud-java-storage/src/main/java/com/google/gcloud/spi/StorageRpcFactory.java
+++ b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/spi/StorageRpcFactory.java
@@ -14,8 +14,9 @@
* limitations under the License.
*/
-package com.google.gcloud.spi;
+package com.google.gcloud.storage.spi;
+import com.google.gcloud.spi.ServiceRpcFactory;
import com.google.gcloud.storage.StorageOptions;
/**
diff --git a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BlobInfoTest.java b/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BlobInfoTest.java
index a1cc01f4287c..029181c6c07b 100644
--- a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BlobInfoTest.java
+++ b/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BlobInfoTest.java
@@ -20,7 +20,11 @@
import static com.google.gcloud.storage.Acl.Role.READER;
import static com.google.gcloud.storage.Acl.Role.WRITER;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import com.google.api.services.storage.model.StorageObject;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.gcloud.storage.Acl.Project;
@@ -28,6 +32,7 @@
import org.junit.Test;
+import java.math.BigInteger;
import java.util.List;
import java.util.Map;
@@ -76,6 +81,10 @@ public class BlobInfoTest {
.size(SIZE)
.updateTime(UPDATE_TIME)
.build();
+ private static final BlobInfo DIRECTORY_INFO = BlobInfo.builder("b", "n/")
+ .size(0L)
+ .isDirectory(true)
+ .build();
@Test
public void testToBuilder() {
@@ -118,6 +127,30 @@ public void testBuilder() {
assertEquals(SELF_LINK, BLOB_INFO.selfLink());
assertEquals(SIZE, BLOB_INFO.size());
assertEquals(UPDATE_TIME, BLOB_INFO.updateTime());
+ assertFalse(BLOB_INFO.isDirectory());
+ assertEquals("b", DIRECTORY_INFO.bucket());
+ assertEquals("n/", DIRECTORY_INFO.name());
+ assertNull(DIRECTORY_INFO.acl());
+ assertNull(DIRECTORY_INFO.componentCount());
+ assertNull(DIRECTORY_INFO.contentType());
+ assertNull(DIRECTORY_INFO.cacheControl());
+ assertNull(DIRECTORY_INFO.contentDisposition());
+ assertNull(DIRECTORY_INFO.contentEncoding());
+ assertNull(DIRECTORY_INFO.contentLanguage());
+ assertNull(DIRECTORY_INFO.crc32c());
+ assertNull(DIRECTORY_INFO.deleteTime());
+ assertNull(DIRECTORY_INFO.etag());
+ assertNull(DIRECTORY_INFO.generation());
+ assertNull(DIRECTORY_INFO.id());
+ assertNull(DIRECTORY_INFO.md5());
+ assertNull(DIRECTORY_INFO.mediaLink());
+ assertNull(DIRECTORY_INFO.metadata());
+ assertNull(DIRECTORY_INFO.metageneration());
+ assertNull(DIRECTORY_INFO.owner());
+ assertNull(DIRECTORY_INFO.selfLink());
+ assertEquals(0L, (long) DIRECTORY_INFO.size());
+ assertNull(DIRECTORY_INFO.updateTime());
+ assertTrue(DIRECTORY_INFO.isDirectory());
}
private void compareBlobs(BlobInfo expected, BlobInfo value) {
@@ -151,6 +184,35 @@ public void testToPbAndFromPb() {
compareBlobs(BLOB_INFO, BlobInfo.fromPb(BLOB_INFO.toPb()));
BlobInfo blobInfo = BlobInfo.builder(BlobId.of("b", "n")).build();
compareBlobs(blobInfo, BlobInfo.fromPb(blobInfo.toPb()));
+ StorageObject object = new StorageObject()
+ .setName("n/")
+ .setBucket("b")
+ .setSize(BigInteger.ZERO)
+ .set("isDirectory", true);
+ blobInfo = BlobInfo.fromPb(object);
+ assertEquals("b", blobInfo.bucket());
+ assertEquals("n/", blobInfo.name());
+ assertNull(blobInfo.acl());
+ assertNull(blobInfo.componentCount());
+ assertNull(blobInfo.contentType());
+ assertNull(blobInfo.cacheControl());
+ assertNull(blobInfo.contentDisposition());
+ assertNull(blobInfo.contentEncoding());
+ assertNull(blobInfo.contentLanguage());
+ assertNull(blobInfo.crc32c());
+ assertNull(blobInfo.deleteTime());
+ assertNull(blobInfo.etag());
+ assertNull(blobInfo.generation());
+ assertNull(blobInfo.id());
+ assertNull(blobInfo.md5());
+ assertNull(blobInfo.mediaLink());
+ assertNull(blobInfo.metadata());
+ assertNull(blobInfo.metageneration());
+ assertNull(blobInfo.owner());
+ assertNull(blobInfo.selfLink());
+ assertEquals(0L, (long) blobInfo.size());
+ assertNull(blobInfo.updateTime());
+ assertTrue(blobInfo.isDirectory());
}
@Test
diff --git a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BlobReadChannelTest.java b/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BlobReadChannelTest.java
index 5dc947df51f8..1b0f36a864a2 100644
--- a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BlobReadChannelTest.java
+++ b/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BlobReadChannelTest.java
@@ -30,8 +30,8 @@
import com.google.gcloud.ReadChannel;
import com.google.gcloud.RestorableState;
import com.google.gcloud.RetryParams;
-import com.google.gcloud.spi.StorageRpc;
-import com.google.gcloud.spi.StorageRpcFactory;
+import com.google.gcloud.storage.spi.StorageRpc;
+import com.google.gcloud.storage.spi.StorageRpcFactory;
import org.junit.After;
import org.junit.Before;
diff --git a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BlobTest.java b/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BlobTest.java
index c7508593f8c9..d6c97ca9ca03 100644
--- a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BlobTest.java
+++ b/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BlobTest.java
@@ -95,6 +95,10 @@ public class BlobTest {
.updateTime(UPDATE_TIME)
.build();
private static final BlobInfo BLOB_INFO = BlobInfo.builder("b", "n").metageneration(42L).build();
+ private static final BlobInfo DIRECTORY_INFO = BlobInfo.builder("b", "n/")
+ .size(0L)
+ .isDirectory(true)
+ .build();
private Storage storage;
private Blob blob;
@@ -229,6 +233,7 @@ public void testCopyToBucket() throws Exception {
assertEquals(copyWriter, returnedCopyWriter);
assertEquals(capturedCopyRequest.getValue().source(), blob.blobId());
assertEquals(capturedCopyRequest.getValue().target(), target);
+ assertFalse(capturedCopyRequest.getValue().overrideInfo());
assertTrue(capturedCopyRequest.getValue().sourceOptions().isEmpty());
assertTrue(capturedCopyRequest.getValue().targetOptions().isEmpty());
}
@@ -247,6 +252,7 @@ public void testCopyTo() throws Exception {
assertEquals(copyWriter, returnedCopyWriter);
assertEquals(capturedCopyRequest.getValue().source(), blob.blobId());
assertEquals(capturedCopyRequest.getValue().target(), target);
+ assertFalse(capturedCopyRequest.getValue().overrideInfo());
assertTrue(capturedCopyRequest.getValue().sourceOptions().isEmpty());
assertTrue(capturedCopyRequest.getValue().targetOptions().isEmpty());
}
@@ -254,9 +260,9 @@ public void testCopyTo() throws Exception {
@Test
public void testCopyToBlobId() throws Exception {
initializeExpectedBlob(2);
+ BlobInfo target = BlobInfo.builder(BlobId.of("bt", "nt")).build();
BlobId targetId = BlobId.of("bt", "nt");
CopyWriter copyWriter = createMock(CopyWriter.class);
- BlobInfo target = BlobInfo.builder(targetId).build();
Capture capturedCopyRequest = Capture.newInstance();
expect(storage.options()).andReturn(mockOptions);
expect(storage.copy(capture(capturedCopyRequest))).andReturn(copyWriter);
@@ -266,6 +272,7 @@ public void testCopyToBlobId() throws Exception {
assertEquals(copyWriter, returnedCopyWriter);
assertEquals(capturedCopyRequest.getValue().source(), blob.blobId());
assertEquals(capturedCopyRequest.getValue().target(), target);
+ assertFalse(capturedCopyRequest.getValue().overrideInfo());
assertTrue(capturedCopyRequest.getValue().sourceOptions().isEmpty());
assertTrue(capturedCopyRequest.getValue().targetOptions().isEmpty());
}
@@ -305,18 +312,20 @@ public void testSignUrl() throws Exception {
@Test
public void testToBuilder() {
- expect(storage.options()).andReturn(mockOptions).times(4);
+ expect(storage.options()).andReturn(mockOptions).times(6);
replay(storage);
Blob fullBlob = new Blob(storage, new BlobInfo.BuilderImpl(FULL_BLOB_INFO));
assertEquals(fullBlob, fullBlob.toBuilder().build());
Blob simpleBlob = new Blob(storage, new BlobInfo.BuilderImpl(BLOB_INFO));
assertEquals(simpleBlob, simpleBlob.toBuilder().build());
+ Blob directory = new Blob(storage, new BlobInfo.BuilderImpl(DIRECTORY_INFO));
+ assertEquals(directory, directory.toBuilder().build());
}
@Test
public void testBuilder() {
initializeExpectedBlob(4);
- expect(storage.options()).andReturn(mockOptions).times(2);
+ expect(storage.options()).andReturn(mockOptions).times(4);
replay(storage);
Blob.Builder builder = new Blob.Builder(new Blob(storage, new BlobInfo.BuilderImpl(BLOB_INFO)));
Blob blob = builder.acl(ACL)
@@ -360,5 +369,33 @@ public void testBuilder() {
assertEquals(SELF_LINK, blob.selfLink());
assertEquals(SIZE, blob.size());
assertEquals(UPDATE_TIME, blob.updateTime());
+ assertFalse(blob.isDirectory());
+ builder = new Blob.Builder(new Blob(storage, new BlobInfo.BuilderImpl(DIRECTORY_INFO)));
+ blob = builder.blobId(BlobId.of("b", "n/"))
+ .isDirectory(true)
+ .size(0L)
+ .build();
+ assertEquals("b", blob.bucket());
+ assertEquals("n/", blob.name());
+ assertNull(blob.acl());
+ assertNull(blob.componentCount());
+ assertNull(blob.contentType());
+ assertNull(blob.cacheControl());
+ assertNull(blob.contentDisposition());
+ assertNull(blob.contentEncoding());
+ assertNull(blob.contentLanguage());
+ assertNull(blob.crc32c());
+ assertNull(blob.deleteTime());
+ assertNull(blob.etag());
+ assertNull(blob.id());
+ assertNull(blob.md5());
+ assertNull(blob.mediaLink());
+ assertNull(blob.metadata());
+ assertNull(blob.metageneration());
+ assertNull(blob.owner());
+ assertNull(blob.selfLink());
+ assertEquals(0L, (long) blob.size());
+ assertNull(blob.updateTime());
+ assertTrue(blob.isDirectory());
}
}
diff --git a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BlobWriteChannelTest.java b/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BlobWriteChannelTest.java
index e499f6b9de52..18ec64a9575f 100644
--- a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BlobWriteChannelTest.java
+++ b/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BlobWriteChannelTest.java
@@ -34,8 +34,8 @@
import com.google.gcloud.RestorableState;
import com.google.gcloud.RetryParams;
import com.google.gcloud.WriteChannel;
-import com.google.gcloud.spi.StorageRpc;
-import com.google.gcloud.spi.StorageRpcFactory;
+import com.google.gcloud.storage.spi.StorageRpc;
+import com.google.gcloud.storage.spi.StorageRpcFactory;
import org.easymock.Capture;
import org.easymock.CaptureType;
diff --git a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BucketTest.java b/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BucketTest.java
index 236411e0c2d8..53056c39c0dc 100644
--- a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BucketTest.java
+++ b/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BucketTest.java
@@ -293,16 +293,16 @@ public void testCreate() throws Exception {
}
@Test
- public void testCreateNullContentType() throws Exception {
+ public void testCreateNoContentType() throws Exception {
initializeExpectedBucket(5);
- BlobInfo info = BlobInfo.builder("b", "n").contentType(Storage.DEFAULT_CONTENT_TYPE).build();
+ BlobInfo info = BlobInfo.builder("b", "n").build();
Blob expectedBlob = new Blob(serviceMockReturnsOptions, new BlobInfo.BuilderImpl(info));
byte[] content = {0xD, 0xE, 0xA, 0xD};
expect(storage.options()).andReturn(mockOptions);
expect(storage.create(info, content)).andReturn(expectedBlob);
replay(storage);
initializeBucket();
- Blob blob = bucket.create("n", content, null);
+ Blob blob = bucket.create("n", content);
assertEquals(expectedBlob, blob);
}
@@ -388,9 +388,9 @@ public void testCreateFromStream() throws Exception {
}
@Test
- public void testCreateFromStreamNullContentType() throws Exception {
+ public void testCreateFromStreamNoContentType() throws Exception {
initializeExpectedBucket(5);
- BlobInfo info = BlobInfo.builder("b", "n").contentType(Storage.DEFAULT_CONTENT_TYPE).build();
+ BlobInfo info = BlobInfo.builder("b", "n").build();
Blob expectedBlob = new Blob(serviceMockReturnsOptions, new BlobInfo.BuilderImpl(info));
byte[] content = {0xD, 0xE, 0xA, 0xD};
InputStream streamContent = new ByteArrayInputStream(content);
@@ -398,7 +398,7 @@ public void testCreateFromStreamNullContentType() throws Exception {
expect(storage.create(info, streamContent)).andReturn(expectedBlob);
replay(storage);
initializeBucket();
- Blob blob = bucket.create("n", streamContent, null);
+ Blob blob = bucket.create("n", streamContent);
assertEquals(expectedBlob, blob);
}
diff --git a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/CopyRequestTest.java b/gcloud-java-storage/src/test/java/com/google/gcloud/storage/CopyRequestTest.java
index b7e8d14e53a1..9f8edfb84162 100644
--- a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/CopyRequestTest.java
+++ b/gcloud-java-storage/src/test/java/com/google/gcloud/storage/CopyRequestTest.java
@@ -18,6 +18,8 @@
import static com.google.gcloud.storage.Storage.PredefinedAcl.PUBLIC_READ;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import com.google.common.collect.ImmutableList;
import com.google.gcloud.storage.Storage.BlobSourceOption;
@@ -53,6 +55,7 @@ public void testCopyRequest() {
assertEquals(1, copyRequest1.sourceOptions().size());
assertEquals(BlobSourceOption.generationMatch(1), copyRequest1.sourceOptions().get(0));
assertEquals(TARGET_BLOB_INFO, copyRequest1.target());
+ assertTrue(copyRequest1.overrideInfo());
assertEquals(1, copyRequest1.targetOptions().size());
assertEquals(BlobTargetOption.predefinedAcl(PUBLIC_READ), copyRequest1.targetOptions().get(0));
@@ -62,6 +65,7 @@ public void testCopyRequest() {
.build();
assertEquals(SOURCE_BLOB_ID, copyRequest2.source());
assertEquals(BlobInfo.builder(TARGET_BLOB_ID).build(), copyRequest2.target());
+ assertFalse(copyRequest2.overrideInfo());
Storage.CopyRequest copyRequest3 = Storage.CopyRequest.builder()
.source(SOURCE_BLOB_ID)
@@ -69,6 +73,7 @@ public void testCopyRequest() {
.build();
assertEquals(SOURCE_BLOB_ID, copyRequest3.source());
assertEquals(TARGET_BLOB_INFO, copyRequest3.target());
+ assertTrue(copyRequest3.overrideInfo());
assertEquals(ImmutableList.of(BlobTargetOption.predefinedAcl(PUBLIC_READ)),
copyRequest3.targetOptions());
}
@@ -78,52 +83,36 @@ public void testCopyRequestOf() {
Storage.CopyRequest copyRequest1 = Storage.CopyRequest.of(SOURCE_BLOB_ID, TARGET_BLOB_INFO);
assertEquals(SOURCE_BLOB_ID, copyRequest1.source());
assertEquals(TARGET_BLOB_INFO, copyRequest1.target());
+ assertTrue(copyRequest1.overrideInfo());
Storage.CopyRequest copyRequest2 = Storage.CopyRequest.of(SOURCE_BLOB_ID, TARGET_BLOB_NAME);
assertEquals(SOURCE_BLOB_ID, copyRequest2.source());
- assertEquals(BlobInfo.builder(SOURCE_BUCKET_NAME, TARGET_BLOB_NAME).build(),
+ assertEquals(BlobInfo.builder(BlobId.of(SOURCE_BUCKET_NAME, TARGET_BLOB_NAME)).build(),
copyRequest2.target());
+ assertFalse(copyRequest2.overrideInfo());
Storage.CopyRequest copyRequest3 =
Storage.CopyRequest.of(SOURCE_BUCKET_NAME, SOURCE_BLOB_NAME, TARGET_BLOB_INFO);
assertEquals(SOURCE_BLOB_ID, copyRequest3.source());
assertEquals(TARGET_BLOB_INFO, copyRequest3.target());
+ assertTrue(copyRequest3.overrideInfo());
Storage.CopyRequest copyRequest4 =
Storage.CopyRequest.of(SOURCE_BUCKET_NAME, SOURCE_BLOB_NAME, TARGET_BLOB_NAME);
assertEquals(SOURCE_BLOB_ID, copyRequest4.source());
- assertEquals(BlobInfo.builder(SOURCE_BUCKET_NAME, TARGET_BLOB_NAME).build(),
+ assertEquals(BlobInfo.builder(BlobId.of(SOURCE_BUCKET_NAME, TARGET_BLOB_NAME)).build(),
copyRequest4.target());
+ assertFalse(copyRequest4.overrideInfo());
Storage.CopyRequest copyRequest5 = Storage.CopyRequest.of(SOURCE_BLOB_ID, TARGET_BLOB_ID);
assertEquals(SOURCE_BLOB_ID, copyRequest5.source());
assertEquals(BlobInfo.builder(TARGET_BLOB_ID).build(), copyRequest5.target());
+ assertFalse(copyRequest5.overrideInfo());
Storage.CopyRequest copyRequest6 =
Storage.CopyRequest.of(SOURCE_BUCKET_NAME, SOURCE_BLOB_NAME, TARGET_BLOB_ID);
assertEquals(SOURCE_BLOB_ID, copyRequest6.source());
assertEquals(BlobInfo.builder(TARGET_BLOB_ID).build(), copyRequest6.target());
- }
-
- @Test
- public void testCopyRequestFail() {
- thrown.expect(IllegalArgumentException.class);
- Storage.CopyRequest.builder()
- .source(SOURCE_BLOB_ID)
- .target(BlobInfo.builder(TARGET_BLOB_ID).build())
- .build();
- }
-
- @Test
- public void testCopyRequestOfBlobInfoFail() {
- thrown.expect(IllegalArgumentException.class);
- Storage.CopyRequest.of(SOURCE_BLOB_ID, BlobInfo.builder(TARGET_BLOB_ID).build());
- }
-
- @Test
- public void testCopyRequestOfStringFail() {
- thrown.expect(IllegalArgumentException.class);
- Storage.CopyRequest.of(
- SOURCE_BUCKET_NAME, SOURCE_BLOB_NAME, BlobInfo.builder(TARGET_BLOB_ID).build());
+ assertFalse(copyRequest6.overrideInfo());
}
}
diff --git a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/CopyWriterTest.java b/gcloud-java-storage/src/test/java/com/google/gcloud/storage/CopyWriterTest.java
index 1b1ffd987de6..8ccb81688b65 100644
--- a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/CopyWriterTest.java
+++ b/gcloud-java-storage/src/test/java/com/google/gcloud/storage/CopyWriterTest.java
@@ -27,10 +27,10 @@
import com.google.common.collect.ImmutableMap;
import com.google.gcloud.RestorableState;
import com.google.gcloud.RetryParams;
-import com.google.gcloud.spi.StorageRpc;
-import com.google.gcloud.spi.StorageRpc.RewriteRequest;
-import com.google.gcloud.spi.StorageRpc.RewriteResponse;
-import com.google.gcloud.spi.StorageRpcFactory;
+import com.google.gcloud.storage.spi.StorageRpc;
+import com.google.gcloud.storage.spi.StorageRpc.RewriteRequest;
+import com.google.gcloud.storage.spi.StorageRpc.RewriteResponse;
+import com.google.gcloud.storage.spi.StorageRpcFactory;
import org.easymock.EasyMock;
import org.junit.After;
@@ -48,20 +48,29 @@ public class CopyWriterTest {
private static final BlobId BLOB_ID = BlobId.of(SOURCE_BUCKET_NAME, SOURCE_BLOB_NAME);
private static final BlobInfo BLOB_INFO =
BlobInfo.builder(DESTINATION_BUCKET_NAME, DESTINATION_BLOB_NAME).build();
- private static final BlobInfo RESULT =
+ private static final BlobInfo RESULT_INFO =
BlobInfo.builder(DESTINATION_BUCKET_NAME, DESTINATION_BLOB_NAME).contentType("type").build();
private static final Map EMPTY_OPTIONS = ImmutableMap.of();
- private static final RewriteRequest REQUEST = new StorageRpc.RewriteRequest(BLOB_ID.toPb(),
- EMPTY_OPTIONS, BLOB_INFO.toPb(), EMPTY_OPTIONS, null);
- private static final RewriteResponse RESPONSE = new StorageRpc.RewriteResponse(REQUEST,
- null, 42L, false, "token", 21L);
- private static final RewriteResponse RESPONSE_DONE = new StorageRpc.RewriteResponse(REQUEST,
- RESULT.toPb(), 42L, true, "token", 42L);
+ private static final RewriteRequest REQUEST_WITH_OBJECT =
+ new StorageRpc.RewriteRequest(BLOB_ID.toPb(), EMPTY_OPTIONS, true, BLOB_INFO.toPb(),
+ EMPTY_OPTIONS, null);
+ private static final RewriteRequest REQUEST_WITHOUT_OBJECT =
+ new StorageRpc.RewriteRequest(BLOB_ID.toPb(), EMPTY_OPTIONS, false, BLOB_INFO.toPb(),
+ EMPTY_OPTIONS, null);
+ private static final RewriteResponse RESPONSE_WITH_OBJECT = new RewriteResponse(
+ REQUEST_WITH_OBJECT, null, 42L, false, "token", 21L);
+ private static final RewriteResponse RESPONSE_WITHOUT_OBJECT = new RewriteResponse(
+ REQUEST_WITHOUT_OBJECT, null, 42L, false, "token", 21L);
+ private static final RewriteResponse RESPONSE_WITH_OBJECT_DONE =
+ new RewriteResponse(REQUEST_WITH_OBJECT, RESULT_INFO.toPb(), 42L, true, "token", 42L);
+ private static final RewriteResponse RESPONSE_WITHOUT_OBJECT_DONE =
+ new RewriteResponse(REQUEST_WITHOUT_OBJECT, RESULT_INFO.toPb(), 42L, true, "token", 42L);
private StorageOptions options;
private StorageRpcFactory rpcFactoryMock;
private StorageRpc storageRpcMock;
private CopyWriter copyWriter;
+ private Blob result;
@Before
public void setUp() {
@@ -75,6 +84,7 @@ public void setUp() {
.serviceRpcFactory(rpcFactoryMock)
.retryParams(RetryParams.noRetries())
.build();
+ result = new Blob(options.service(), new BlobInfo.BuilderImpl(RESULT_INFO));
}
@After
@@ -83,41 +93,111 @@ public void tearDown() throws Exception {
}
@Test
- public void testRewrite() {
- EasyMock.expect(storageRpcMock.continueRewrite(RESPONSE)).andReturn(RESPONSE_DONE);
+ public void testRewriteWithObject() {
+ EasyMock.expect(storageRpcMock.continueRewrite(RESPONSE_WITH_OBJECT))
+ .andReturn(RESPONSE_WITH_OBJECT_DONE);
EasyMock.replay(storageRpcMock);
- copyWriter = new CopyWriter(options, RESPONSE);
- assertEquals(RESULT, copyWriter.result());
+ copyWriter = new CopyWriter(options, RESPONSE_WITH_OBJECT);
+ assertEquals(result, copyWriter.result());
assertTrue(copyWriter.isDone());
assertEquals(42L, copyWriter.totalBytesCopied());
assertEquals(42L, copyWriter.blobSize());
}
@Test
- public void testRewriteMultipleRequests() {
- EasyMock.expect(storageRpcMock.continueRewrite(RESPONSE)).andReturn(RESPONSE);
- EasyMock.expect(storageRpcMock.continueRewrite(RESPONSE)).andReturn(RESPONSE_DONE);
+ public void testRewriteWithoutObject() {
+ EasyMock.expect(storageRpcMock.continueRewrite(RESPONSE_WITHOUT_OBJECT))
+ .andReturn(RESPONSE_WITHOUT_OBJECT_DONE);
EasyMock.replay(storageRpcMock);
- copyWriter = new CopyWriter(options, RESPONSE);
- assertEquals(RESULT, copyWriter.result());
+ copyWriter = new CopyWriter(options, RESPONSE_WITHOUT_OBJECT);
+ assertEquals(result, copyWriter.result());
assertTrue(copyWriter.isDone());
assertEquals(42L, copyWriter.totalBytesCopied());
assertEquals(42L, copyWriter.blobSize());
}
@Test
- public void testSaveAndRestore() {
- EasyMock.expect(storageRpcMock.continueRewrite(RESPONSE)).andReturn(RESPONSE);
- EasyMock.expect(storageRpcMock.continueRewrite(RESPONSE)).andReturn(RESPONSE_DONE);
+ public void testRewriteWithObjectMultipleRequests() {
+ EasyMock.expect(storageRpcMock.continueRewrite(RESPONSE_WITH_OBJECT))
+ .andReturn(RESPONSE_WITH_OBJECT);
+ EasyMock.expect(storageRpcMock.continueRewrite(RESPONSE_WITH_OBJECT))
+ .andReturn(RESPONSE_WITH_OBJECT_DONE);
EasyMock.replay(storageRpcMock);
- copyWriter = new CopyWriter(options, RESPONSE);
+ copyWriter = new CopyWriter(options, RESPONSE_WITH_OBJECT);
+ assertEquals(result, copyWriter.result());
+ assertTrue(copyWriter.isDone());
+ assertEquals(42L, copyWriter.totalBytesCopied());
+ assertEquals(42L, copyWriter.blobSize());
+ }
+
+ @Test
+ public void testRewriteWithoutObjectMultipleRequests() {
+ EasyMock.expect(storageRpcMock.continueRewrite(RESPONSE_WITHOUT_OBJECT))
+ .andReturn(RESPONSE_WITHOUT_OBJECT);
+ EasyMock.expect(storageRpcMock.continueRewrite(RESPONSE_WITHOUT_OBJECT))
+ .andReturn(RESPONSE_WITHOUT_OBJECT_DONE);
+ EasyMock.replay(storageRpcMock);
+ copyWriter = new CopyWriter(options, RESPONSE_WITHOUT_OBJECT);
+ assertEquals(result, copyWriter.result());
+ assertTrue(copyWriter.isDone());
+ assertEquals(42L, copyWriter.totalBytesCopied());
+ assertEquals(42L, copyWriter.blobSize());
+ }
+
+ @Test
+ public void testSaveAndRestoreWithObject() {
+ EasyMock.expect(storageRpcMock.continueRewrite(RESPONSE_WITH_OBJECT))
+ .andReturn(RESPONSE_WITH_OBJECT);
+ EasyMock.expect(storageRpcMock.continueRewrite(RESPONSE_WITH_OBJECT))
+ .andReturn(RESPONSE_WITH_OBJECT_DONE);
+ EasyMock.replay(storageRpcMock);
+ copyWriter = new CopyWriter(options, RESPONSE_WITH_OBJECT);
+ copyWriter.copyChunk();
+ assertTrue(!copyWriter.isDone());
+ assertEquals(21L, copyWriter.totalBytesCopied());
+ assertEquals(42L, copyWriter.blobSize());
+ RestorableState rewriterState = copyWriter.capture();
+ CopyWriter restoredRewriter = rewriterState.restore();
+ assertEquals(result, restoredRewriter.result());
+ assertTrue(restoredRewriter.isDone());
+ assertEquals(42L, restoredRewriter.totalBytesCopied());
+ assertEquals(42L, restoredRewriter.blobSize());
+ }
+
+ @Test
+ public void testSaveAndRestoreWithoutObject() {
+ EasyMock.expect(storageRpcMock.continueRewrite(RESPONSE_WITHOUT_OBJECT))
+ .andReturn(RESPONSE_WITHOUT_OBJECT);
+ EasyMock.expect(storageRpcMock.continueRewrite(RESPONSE_WITHOUT_OBJECT))
+ .andReturn(RESPONSE_WITHOUT_OBJECT_DONE);
+ EasyMock.replay(storageRpcMock);
+ copyWriter = new CopyWriter(options, RESPONSE_WITHOUT_OBJECT);
copyWriter.copyChunk();
assertTrue(!copyWriter.isDone());
assertEquals(21L, copyWriter.totalBytesCopied());
assertEquals(42L, copyWriter.blobSize());
RestorableState rewriterState = copyWriter.capture();
CopyWriter restoredRewriter = rewriterState.restore();
- assertEquals(RESULT, restoredRewriter.result());
+ assertEquals(result, restoredRewriter.result());
+ assertTrue(restoredRewriter.isDone());
+ assertEquals(42L, restoredRewriter.totalBytesCopied());
+ assertEquals(42L, restoredRewriter.blobSize());
+ }
+
+ @Test
+ public void testSaveAndRestoreWithResult() {
+ EasyMock.expect(storageRpcMock.continueRewrite(RESPONSE_WITH_OBJECT))
+ .andReturn(RESPONSE_WITH_OBJECT_DONE);
+ EasyMock.replay(storageRpcMock);
+ copyWriter = new CopyWriter(options, RESPONSE_WITH_OBJECT);
+ copyWriter.copyChunk();
+ assertEquals(result, copyWriter.result());
+ assertTrue(copyWriter.isDone());
+ assertEquals(42L, copyWriter.totalBytesCopied());
+ assertEquals(42L, copyWriter.blobSize());
+ RestorableState rewriterState = copyWriter.capture();
+ CopyWriter restoredRewriter = rewriterState.restore();
+ assertEquals(result, restoredRewriter.result());
assertTrue(restoredRewriter.isDone());
assertEquals(42L, restoredRewriter.totalBytesCopied());
assertEquals(42L, restoredRewriter.blobSize());
diff --git a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/OptionTest.java b/gcloud-java-storage/src/test/java/com/google/gcloud/storage/OptionTest.java
index 2703ddb401c5..5924174ab138 100644
--- a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/OptionTest.java
+++ b/gcloud-java-storage/src/test/java/com/google/gcloud/storage/OptionTest.java
@@ -18,7 +18,7 @@
import static org.junit.Assert.assertEquals;
-import com.google.gcloud.spi.StorageRpc;
+import com.google.gcloud.storage.spi.StorageRpc;
import org.junit.Test;
diff --git a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/RemoteGcsHelperTest.java b/gcloud-java-storage/src/test/java/com/google/gcloud/storage/RemoteGcsHelperTest.java
index 154554a029fe..146922a9dae9 100644
--- a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/RemoteGcsHelperTest.java
+++ b/gcloud-java-storage/src/test/java/com/google/gcloud/storage/RemoteGcsHelperTest.java
@@ -132,7 +132,8 @@ public void testForceDelete() throws InterruptedException, ExecutionException {
@Test
public void testForceDeleteTimeout() throws InterruptedException, ExecutionException {
Storage storageMock = EasyMock.createMock(Storage.class);
- EasyMock.expect(storageMock.list(BUCKET_NAME)).andReturn(blobPage).anyTimes();
+ EasyMock.expect(storageMock.list(BUCKET_NAME, BlobListOption.versions(true)))
+ .andReturn(blobPage).anyTimes();
for (BlobInfo info : blobList) {
EasyMock.expect(storageMock.delete(info.blobId())).andReturn(true).anyTimes();
}
diff --git a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/SerializationTest.java b/gcloud-java-storage/src/test/java/com/google/gcloud/storage/SerializationTest.java
index c9b957bb936a..613cb81c3549 100644
--- a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/SerializationTest.java
+++ b/gcloud-java-storage/src/test/java/com/google/gcloud/storage/SerializationTest.java
@@ -16,31 +16,20 @@
package com.google.gcloud.storage;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotSame;
-
import com.google.common.collect.ImmutableMap;
import com.google.gcloud.AuthCredentials;
+import com.google.gcloud.BaseSerializationTest;
import com.google.gcloud.PageImpl;
import com.google.gcloud.ReadChannel;
-import com.google.gcloud.RestorableState;
-import com.google.gcloud.RetryParams;
-import com.google.gcloud.WriteChannel;
-import com.google.gcloud.spi.StorageRpc;
+import com.google.gcloud.Restorable;
import com.google.gcloud.storage.Acl.Project.ProjectRole;
+import com.google.gcloud.storage.spi.StorageRpc;
-import org.junit.Test;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Collections;
import java.util.Map;
-public class SerializationTest {
+public class SerializationTest extends BaseSerializationTest {
private static final Storage STORAGE = StorageOptions.builder().projectId("p").build().service();
private static final Acl.Domain ACL_DOMAIN = new Acl.Domain("domain");
@@ -63,8 +52,9 @@ public class SerializationTest {
Collections.>emptyList());
private static final PageImpl PAGE_RESULT =
new PageImpl<>(null, "c", Collections.singletonList(BLOB));
+ private static final StorageException STORAGE_EXCEPTION = new StorageException(42, "message");
private static final Storage.BlobListOption BLOB_LIST_OPTIONS =
- Storage.BlobListOption.maxResults(100);
+ Storage.BlobListOption.pageSize(100);
private static final Storage.BlobSourceOption BLOB_SOURCE_OPTIONS =
Storage.BlobSourceOption.generationMatch(1);
private static final Storage.BlobTargetOption BLOB_TARGET_OPTIONS =
@@ -77,83 +67,32 @@ public class SerializationTest {
Storage.BucketTargetOption.metagenerationNotMatch();
private static final Map EMPTY_RPC_OPTIONS = ImmutableMap.of();
- @Test
- public void testServiceOptions() throws Exception {
+ @Override
+ protected Serializable[] serializableObjects() {
StorageOptions options = StorageOptions.builder()
.projectId("p1")
.authCredentials(AuthCredentials.createForAppEngine())
.build();
- StorageOptions serializedCopy = serializeAndDeserialize(options);
- assertEquals(options, serializedCopy);
-
- options = options.toBuilder()
+ StorageOptions otherOptions = options.toBuilder()
.projectId("p2")
- .retryParams(RetryParams.defaultInstance())
.authCredentials(null)
- .pathDelimiter(":")
.build();
- serializedCopy = serializeAndDeserialize(options);
- assertEquals(options, serializedCopy);
- }
-
- @Test
- public void testModelAndRequests() throws Exception {
- Serializable[] objects = {ACL_DOMAIN, ACL_GROUP, ACL_PROJECT_, ACL_USER, ACL_RAW, ACL,
+ return new Serializable[]{ACL_DOMAIN, ACL_GROUP, ACL_PROJECT_, ACL_USER, ACL_RAW, ACL,
BLOB_INFO, BLOB, BUCKET_INFO, BUCKET, ORIGIN, CORS, BATCH_REQUEST, BATCH_RESPONSE,
PAGE_RESULT, BLOB_LIST_OPTIONS, BLOB_SOURCE_OPTIONS, BLOB_TARGET_OPTIONS,
- BUCKET_LIST_OPTIONS, BUCKET_SOURCE_OPTIONS, BUCKET_TARGET_OPTIONS};
- for (Serializable obj : objects) {
- Object copy = serializeAndDeserialize(obj);
- assertEquals(obj, obj);
- assertEquals(obj, copy);
- assertNotSame(obj, copy);
- assertEquals(copy, copy);
- }
+ BUCKET_LIST_OPTIONS, BUCKET_SOURCE_OPTIONS, BUCKET_TARGET_OPTIONS, STORAGE_EXCEPTION,
+ options, otherOptions};
}
- @Test
- public void testReadChannelState() throws IOException, ClassNotFoundException {
- StorageOptions options = StorageOptions.builder()
- .projectId("p2")
- .retryParams(RetryParams.defaultInstance())
- .build();
+ @Override
+ protected Restorable>[] restorableObjects() {
+ StorageOptions options = StorageOptions.builder().projectId("p2").build();
ReadChannel reader =
new BlobReadChannel(options, BlobId.of("b", "n"), EMPTY_RPC_OPTIONS);
- RestorableState state = reader.capture();
- RestorableState deserializedState = serializeAndDeserialize(state);
- assertEquals(state, deserializedState);
- assertEquals(state.hashCode(), deserializedState.hashCode());
- assertEquals(state.toString(), deserializedState.toString());
- reader.close();
- }
-
- @Test
- public void testWriteChannelState() throws IOException, ClassNotFoundException {
- StorageOptions options = StorageOptions.builder()
- .projectId("p2")
- .retryParams(RetryParams.defaultInstance())
- .build();
// avoid closing when you don't want partial writes to GCS upon failure
@SuppressWarnings("resource")
BlobWriteChannel writer =
new BlobWriteChannel(options, BlobInfo.builder(BlobId.of("b", "n")).build(), "upload-id");
- RestorableState state = writer.capture();
- RestorableState deserializedState = serializeAndDeserialize(state);
- assertEquals(state, deserializedState);
- assertEquals(state.hashCode(), deserializedState.hashCode());
- assertEquals(state.toString(), deserializedState.toString());
- }
-
- @SuppressWarnings("unchecked")
- private T serializeAndDeserialize(T obj)
- throws IOException, ClassNotFoundException {
- ByteArrayOutputStream bytes = new ByteArrayOutputStream();
- try (ObjectOutputStream output = new ObjectOutputStream(bytes)) {
- output.writeObject(obj);
- }
- try (ObjectInputStream input =
- new ObjectInputStream(new ByteArrayInputStream(bytes.toByteArray()))) {
- return (T) input.readObject();
- }
+ return new Restorable>[]{reader, writer};
}
}
diff --git a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/StorageImplTest.java b/gcloud-java-storage/src/test/java/com/google/gcloud/storage/StorageImplTest.java
index 612664de14ae..3cc99e3bf884 100644
--- a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/StorageImplTest.java
+++ b/gcloud-java-storage/src/test/java/com/google/gcloud/storage/StorageImplTest.java
@@ -37,10 +37,10 @@
import com.google.gcloud.RetryParams;
import com.google.gcloud.ServiceOptions;
import com.google.gcloud.WriteChannel;
-import com.google.gcloud.spi.StorageRpc;
-import com.google.gcloud.spi.StorageRpc.Tuple;
-import com.google.gcloud.spi.StorageRpcFactory;
import com.google.gcloud.storage.Storage.CopyRequest;
+import com.google.gcloud.storage.spi.StorageRpc;
+import com.google.gcloud.storage.spi.StorageRpc.Tuple;
+import com.google.gcloud.storage.spi.StorageRpcFactory;
import org.easymock.Capture;
import org.easymock.EasyMock;
@@ -181,8 +181,8 @@ public class StorageImplTest {
StorageRpc.Option.IF_SOURCE_GENERATION_MATCH, BLOB_SOURCE_GENERATION.value());
// Bucket list options
- private static final Storage.BucketListOption BUCKET_LIST_MAX_RESULT =
- Storage.BucketListOption.maxResults(42L);
+ private static final Storage.BucketListOption BUCKET_LIST_PAGE_SIZE =
+ Storage.BucketListOption.pageSize(42L);
private static final Storage.BucketListOption BUCKET_LIST_PREFIX =
Storage.BucketListOption.prefix("prefix");
private static final Storage.BucketListOption BUCKET_LIST_FIELDS =
@@ -190,12 +190,12 @@ public class StorageImplTest {
private static final Storage.BucketListOption BUCKET_LIST_EMPTY_FIELDS =
Storage.BucketListOption.fields();
private static final Map BUCKET_LIST_OPTIONS = ImmutableMap.of(
- StorageRpc.Option.MAX_RESULTS, BUCKET_LIST_MAX_RESULT.value(),
+ StorageRpc.Option.MAX_RESULTS, BUCKET_LIST_PAGE_SIZE.value(),
StorageRpc.Option.PREFIX, BUCKET_LIST_PREFIX.value());
// Blob list options
- private static final Storage.BlobListOption BLOB_LIST_MAX_RESULT =
- Storage.BlobListOption.maxResults(42L);
+ private static final Storage.BlobListOption BLOB_LIST_PAGE_SIZE =
+ Storage.BlobListOption.pageSize(42L);
private static final Storage.BlobListOption BLOB_LIST_PREFIX =
Storage.BlobListOption.prefix("prefix");
private static final Storage.BlobListOption BLOB_LIST_FIELDS =
@@ -205,7 +205,7 @@ public class StorageImplTest {
private static final Storage.BlobListOption BLOB_LIST_EMPTY_FIELDS =
Storage.BlobListOption.fields();
private static final Map BLOB_LIST_OPTIONS = ImmutableMap.of(
- StorageRpc.Option.MAX_RESULTS, BLOB_LIST_MAX_RESULT.value(),
+ StorageRpc.Option.MAX_RESULTS, BLOB_LIST_PAGE_SIZE.value(),
StorageRpc.Option.PREFIX, BLOB_LIST_PREFIX.value(),
StorageRpc.Option.VERSIONS, BLOB_LIST_VERSIONS.value());
@@ -567,7 +567,7 @@ public void testListBucketsWithOptions() {
EasyMock.replay(storageRpcMock);
initializeService();
ImmutableList bucketList = ImmutableList.of(expectedBucket1, expectedBucket2);
- Page page = storage.list(BUCKET_LIST_MAX_RESULT, BUCKET_LIST_PREFIX);
+ Page page = storage.list(BUCKET_LIST_PAGE_SIZE, BUCKET_LIST_PREFIX);
assertEquals(cursor, page.nextPageCursor());
assertArrayEquals(bucketList.toArray(), Iterables.toArray(page.values(), Bucket.class));
}
@@ -654,7 +654,7 @@ public void testListBlobsWithOptions() {
initializeService();
ImmutableList blobList = ImmutableList.of(expectedBlob1, expectedBlob2);
Page page =
- storage.list(BUCKET_NAME1, BLOB_LIST_MAX_RESULT, BLOB_LIST_PREFIX, BLOB_LIST_VERSIONS);
+ storage.list(BUCKET_NAME1, BLOB_LIST_PAGE_SIZE, BLOB_LIST_PREFIX, BLOB_LIST_VERSIONS);
assertEquals(cursor, page.nextPageCursor());
assertArrayEquals(blobList.toArray(), Iterables.toArray(page.values(), Blob.class));
}
@@ -673,9 +673,9 @@ public void testListBlobsWithSelectedFields() {
initializeService();
ImmutableList blobList = ImmutableList.of(expectedBlob1, expectedBlob2);
Page page =
- storage.list(BUCKET_NAME1, BLOB_LIST_MAX_RESULT, BLOB_LIST_PREFIX, BLOB_LIST_FIELDS);
- assertEquals(BLOB_LIST_MAX_RESULT.value(),
- capturedOptions.getValue().get(BLOB_LIST_MAX_RESULT.rpcOption()));
+ storage.list(BUCKET_NAME1, BLOB_LIST_PAGE_SIZE, BLOB_LIST_PREFIX, BLOB_LIST_FIELDS);
+ assertEquals(BLOB_LIST_PAGE_SIZE.value(),
+ capturedOptions.getValue().get(BLOB_LIST_PAGE_SIZE.rpcOption()));
assertEquals(BLOB_LIST_PREFIX.value(),
capturedOptions.getValue().get(BLOB_LIST_PREFIX.rpcOption()));
String selector = (String) capturedOptions.getValue().get(BLOB_LIST_FIELDS.rpcOption());
@@ -704,9 +704,9 @@ public void testListBlobsWithEmptyFields() {
initializeService();
ImmutableList blobList = ImmutableList.of(expectedBlob1, expectedBlob2);
Page page =
- storage.list(BUCKET_NAME1, BLOB_LIST_MAX_RESULT, BLOB_LIST_PREFIX, BLOB_LIST_EMPTY_FIELDS);
- assertEquals(BLOB_LIST_MAX_RESULT.value(),
- capturedOptions.getValue().get(BLOB_LIST_MAX_RESULT.rpcOption()));
+ storage.list(BUCKET_NAME1, BLOB_LIST_PAGE_SIZE, BLOB_LIST_PREFIX, BLOB_LIST_EMPTY_FIELDS);
+ assertEquals(BLOB_LIST_PAGE_SIZE.value(),
+ capturedOptions.getValue().get(BLOB_LIST_PAGE_SIZE.rpcOption()));
assertEquals(BLOB_LIST_PREFIX.value(),
capturedOptions.getValue().get(BLOB_LIST_PREFIX.rpcOption()));
String selector = (String) capturedOptions.getValue().get(BLOB_LIST_EMPTY_FIELDS.rpcOption());
@@ -719,6 +719,22 @@ public void testListBlobsWithEmptyFields() {
assertArrayEquals(blobList.toArray(), Iterables.toArray(page.values(), Blob.class));
}
+ @Test
+ public void testListBlobsCurrentDirectory() {
+ String cursor = "cursor";
+ Map options = ImmutableMap.of(StorageRpc.Option.DELIMITER, "/");
+ ImmutableList blobInfoList = ImmutableList.of(BLOB_INFO1, BLOB_INFO2);
+ Tuple> result =
+ Tuple.of(cursor, Iterables.transform(blobInfoList, BlobInfo.INFO_TO_PB_FUNCTION));
+ EasyMock.expect(storageRpcMock.list(BUCKET_NAME1, options)).andReturn(result);
+ EasyMock.replay(storageRpcMock);
+ initializeService();
+ ImmutableList