diff --git a/src/main/java/com/google/gcloud/storage/Acl.java b/src/main/java/com/google/gcloud/storage/Acl.java index 138ba4f3fa3a..b5bb685334c1 100644 --- a/src/main/java/com/google/gcloud/storage/Acl.java +++ b/src/main/java/com/google/gcloud/storage/Acl.java @@ -20,6 +20,7 @@ import com.google.api.services.storage.model.ObjectAccessControl; import java.io.Serializable; +import java.util.Objects; /** * Access Control List on for buckets or blobs. @@ -59,6 +60,19 @@ protected String value() { return value; } + @Override + public int hashCode() { + return Objects.hash(type, value); + } + + @Override + public boolean equals(Object obj) { + if (obj == null || !getClass().isAssignableFrom(obj.getClass())) { + return false; + } + return Objects.equals(toPb(), ((Entity)obj).toPb()); + } + @Override public String toString() { return toPb(); @@ -91,13 +105,12 @@ static Entity fromPb(String entity) { } } - public static class Domain extends Entity { + public static final class Domain extends Entity { private static final long serialVersionUID = -3033025857280447253L; public Domain(String domain) { super(Type.DOMAIN, domain); - } public String domain() { @@ -105,7 +118,7 @@ public String domain() { } } - public static class Group extends Entity { + public static final class Group extends Entity { private static final long serialVersionUID = -1660987136294408826L; @@ -118,7 +131,7 @@ public String email() { } } - public static class User extends Entity { + public static final class User extends Entity { private static final long serialVersionUID = 3076518036392737008L; private static final String ALL_USERS = "allUsers"; @@ -154,7 +167,7 @@ public static User ofAllAuthenticatedUsers() { } } - public static class Project extends Entity { + public static final class Project extends Entity { private static final long serialVersionUID = 7933776866530023027L; @@ -180,7 +193,7 @@ public String projectId() { } } - public static class RawEntity extends Entity { + public static final class RawEntity extends Entity { private static final long serialVersionUID = 3966205614223053950L; diff --git a/src/main/java/com/google/gcloud/storage/BatchRequest.java b/src/main/java/com/google/gcloud/storage/BatchRequest.java index 9fbee876cf45..6f62d5c51ae4 100644 --- a/src/main/java/com/google/gcloud/storage/BatchRequest.java +++ b/src/main/java/com/google/gcloud/storage/BatchRequest.java @@ -17,51 +17,56 @@ package com.google.gcloud.storage; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Lists; import com.google.gcloud.storage.StorageService.BlobSourceOption; import com.google.gcloud.storage.StorageService.BlobTargetOption; import java.io.Serializable; import java.util.LinkedHashMap; import java.util.Map; +import java.util.Objects; /** * Google storage batch request. */ -public class BatchRequest implements Serializable { +public final class BatchRequest implements Serializable { private static final long serialVersionUID = -1527992265939800345L; - private final Map toDelete; - private final Map toUpdate; - private final Map toGet; + private final Map> toDelete; + private final Map> toUpdate; + private final Map> toGet; public static class Builder { - private Map toDelete = new LinkedHashMap<>(); - private Map toUpdate = new LinkedHashMap<>(); - private Map toGet = new LinkedHashMap<>(); + private Map> toDelete = new LinkedHashMap<>(); + private Map> toUpdate = new LinkedHashMap<>(); + private Map> toGet = new LinkedHashMap<>(); private Builder() {} /** * Delete the given blob. */ - public void delete(String bucket, String blob, BlobSourceOption... options) { - toDelete.put(Blob.of(bucket, blob), options); + public Builder delete(String bucket, String blob, BlobSourceOption... options) { + toDelete.put(Blob.of(bucket, blob), Lists.newArrayList(options)); + return this; } /** * Update the given blob. */ - public void update(Blob blob, BlobTargetOption... options) { - toUpdate.put(blob, options); + public Builder update(Blob blob, BlobTargetOption... options) { + toUpdate.put(blob, Lists.newArrayList(options)); + return this; } /** * Retrieve metadata for the given blob. */ - public void get(String bucket, String blob, BlobSourceOption... options) { - toGet.put(Blob.of(bucket, blob), options); + public Builder get(String bucket, String blob, BlobSourceOption... options) { + toGet.put(Blob.of(bucket, blob), Lists.newArrayList(options)); + return this; } public BatchRequest build() { @@ -75,15 +80,31 @@ private BatchRequest(Builder builder) { toGet = ImmutableMap.copyOf(builder.toGet); } - Map toDelete() { + @Override + public int hashCode() { + return Objects.hash(toDelete, toUpdate, toGet); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof BatchRequest)) { + return false; + } + BatchRequest other = (BatchRequest) obj; + return Objects.equals(toDelete, other.toDelete) + && Objects.equals(toUpdate, other.toUpdate) + && Objects.equals(toGet, other.toGet); + } + + Map> toDelete() { return toDelete; } - Map toUpdate() { + Map> toUpdate() { return toUpdate; } - Map toGet() { + Map> toGet() { return toGet; } diff --git a/src/main/java/com/google/gcloud/storage/BatchResponse.java b/src/main/java/com/google/gcloud/storage/BatchResponse.java index fab4d964e1c6..c45b0623eb9b 100644 --- a/src/main/java/com/google/gcloud/storage/BatchResponse.java +++ b/src/main/java/com/google/gcloud/storage/BatchResponse.java @@ -21,11 +21,12 @@ import java.io.Serializable; import java.util.List; +import java.util.Objects; /** * Google Storage batch response. */ -public class BatchResponse implements Serializable { +public final class BatchResponse implements Serializable { private static final long serialVersionUID = 1057416839397037706L; @@ -78,6 +79,21 @@ public boolean failed() { return exception != null; } + @Override + public int hashCode() { + return Objects.hash(value, exception); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof Result)) { + return false; + } + Result other = (Result) obj; + return Objects.equals(value, other.value) + && Objects.equals(exception, other.exception); + } + @Override public String toString() { return MoreObjects.toStringHelper(this) @@ -99,6 +115,22 @@ static Result empty() { this.getResult = ImmutableList.copyOf(getResult); } + @Override + public int hashCode() { + return Objects.hash(deleteResult, updateResult, getResult); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof BatchResponse)) { + return false; + } + BatchResponse other = (BatchResponse) obj; + return Objects.equals(deleteResult, other.deleteResult) + && Objects.equals(updateResult, other.updateResult) + && Objects.equals(updateResult, other.updateResult); + } + /** * Returns the results for the delete operations using the request order. */ diff --git a/src/main/java/com/google/gcloud/storage/Blob.java b/src/main/java/com/google/gcloud/storage/Blob.java index ffaec524c67d..7ef81bf2b240 100644 --- a/src/main/java/com/google/gcloud/storage/Blob.java +++ b/src/main/java/com/google/gcloud/storage/Blob.java @@ -32,13 +32,14 @@ import java.math.BigInteger; import java.util.List; import java.util.Map; +import java.util.Objects; /** * A Google Storage object. * * @see Concepts and Terminology */ -public class Blob implements Serializable { +public final class Blob implements Serializable { private static final long serialVersionUID = 2228487739943277159L; @@ -386,6 +387,19 @@ public static Builder builder(String bucket, String name) { return new Builder().bucket(bucket).name(name); } + @Override + public int hashCode() { + return Objects.hash(bucket, name); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof Blob)) { + return false; + } + return Objects.equals(toPb(), ((Blob)obj).toPb()); + } + StorageObject toPb() { StorageObject storageObject = new StorageObject(); if (acl != null) { diff --git a/src/main/java/com/google/gcloud/storage/Bucket.java b/src/main/java/com/google/gcloud/storage/Bucket.java index 7f68704f9e6a..c36b24c80269 100644 --- a/src/main/java/com/google/gcloud/storage/Bucket.java +++ b/src/main/java/com/google/gcloud/storage/Bucket.java @@ -39,6 +39,7 @@ import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.List; +import java.util.Objects; /** * A Google Storage bucket. @@ -559,6 +560,19 @@ public Builder toBuilder() { .deleteRules(deleteRules); } + @Override + public int hashCode() { + return Objects.hash(name); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof Bucket)) { + return false; + } + return Objects.equals(toPb(), ((Bucket) obj).toPb()); + } + @Override public String toString() { return MoreObjects.toStringHelper(this) diff --git a/src/main/java/com/google/gcloud/storage/Cors.java b/src/main/java/com/google/gcloud/storage/Cors.java index 366c8d9223bd..9cedbc0d3b4c 100644 --- a/src/main/java/com/google/gcloud/storage/Cors.java +++ b/src/main/java/com/google/gcloud/storage/Cors.java @@ -29,6 +29,7 @@ import java.net.URI; import java.net.URISyntaxException; import java.util.List; +import java.util.Objects; /** * Cross-Origin Resource Sharing (CORS) configuration for a bucket. @@ -60,7 +61,7 @@ public enum Method { ANY, GET, HEAD, PUT, POST, DELETE } - public static class Origin implements Serializable { + public static final class Origin implements Serializable { private static final long serialVersionUID = -4447958124895577993L; private static final String ANY_URI = "*"; @@ -91,6 +92,19 @@ public static Origin of(String value) { return new Origin(value); } + @Override + public int hashCode() { + return value.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof Origin)) { + return false; + } + return value.equals(((Origin)obj).value); + } + @Override public String toString() { return value(); @@ -166,6 +180,23 @@ public Builder toBuilder() { .responseHeaders(responseHeaders); } + @Override + public int hashCode() { + return Objects.hash(maxAgeSeconds, methods, origins, responseHeaders); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof Cors)) { + return false; + } + Cors other = (Cors) obj; + return Objects.equals(maxAgeSeconds, other.maxAgeSeconds) + && Objects.equals(methods, other.methods) + && Objects.equals(origins, other.origins) + && Objects.equals(responseHeaders, other.responseHeaders); + } + public static Builder builder() { return new Builder(); } diff --git a/src/main/java/com/google/gcloud/storage/ListResult.java b/src/main/java/com/google/gcloud/storage/ListResult.java index 180e5f7a4155..dd843020376e 100644 --- a/src/main/java/com/google/gcloud/storage/ListResult.java +++ b/src/main/java/com/google/gcloud/storage/ListResult.java @@ -18,11 +18,12 @@ import java.io.Serializable; import java.util.Iterator; +import java.util.Objects; /** * Google Cloud storage list result. */ -public class ListResult implements Iterable, Serializable { +public final class ListResult implements Iterable, Serializable { private static final long serialVersionUID = -6937287874908527950L; @@ -42,4 +43,19 @@ public String nextPageCursor() { public Iterator iterator() { return results.iterator(); } + + @Override + public int hashCode() { + return Objects.hash(cursor, results); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof ListResult)) { + return false; + } + ListResult other = (ListResult) obj; + return Objects.equals(cursor, other.cursor) + && Objects.equals(results, other.results); + } } diff --git a/src/main/java/com/google/gcloud/storage/StorageServiceImpl.java b/src/main/java/com/google/gcloud/storage/StorageServiceImpl.java index 02bbb97a2fe5..1f66401105d0 100644 --- a/src/main/java/com/google/gcloud/storage/StorageServiceImpl.java +++ b/src/main/java/com/google/gcloud/storage/StorageServiceImpl.java @@ -302,24 +302,27 @@ public byte[] call() { public BatchResponse apply(BatchRequest batchRequest) { List>> toDelete = Lists.newArrayListWithCapacity(batchRequest.toDelete().size()); - for (Map.Entry entry : batchRequest.toDelete().entrySet()) { + for (Map.Entry> entry : batchRequest.toDelete().entrySet()) { Blob blob = entry.getKey(); - Map optionsMap = optionMap(blob, entry.getValue()); + Map optionsMap = + optionMap(blob.generation(), blob.metageneration(), entry.getValue()); StorageObject storageObject = blob.toPb(); toDelete.add(Tuple.>of(storageObject, optionsMap)); } List>> toUpdate = Lists.newArrayListWithCapacity(batchRequest.toUpdate().size()); - for (Map.Entry entry : batchRequest.toUpdate().entrySet()) { + for (Map.Entry> entry : batchRequest.toUpdate().entrySet()) { Blob blob = entry.getKey(); - Map optionsMap = optionMap(blob, entry.getValue()); + Map optionsMap = + optionMap(blob.generation(), blob.metageneration(), entry.getValue()); toUpdate.add(Tuple.>of(blob.toPb(), optionsMap)); } List>> toGet = Lists.newArrayListWithCapacity(batchRequest.toGet().size()); - for (Map.Entry entry : batchRequest.toGet().entrySet()) { + for (Map.Entry> entry : batchRequest.toGet().entrySet()) { Blob blob = entry.getKey(); - Map optionsMap = optionMap(blob, entry.getValue()); + Map optionsMap = + optionMap(blob.generation(), blob.metageneration(), entry.getValue()); toGet.add(Tuple.>of(blob.toPb(), optionsMap)); } StorageRpc.BatchResponse response = diff --git a/src/test/java/com/google/gcloud/storage/SerializationTest.java b/src/test/java/com/google/gcloud/storage/SerializationTest.java index 7e3cb3e89258..365462d3f69a 100644 --- a/src/test/java/com/google/gcloud/storage/SerializationTest.java +++ b/src/test/java/com/google/gcloud/storage/SerializationTest.java @@ -17,9 +17,11 @@ package com.google.gcloud.storage; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotSame; import com.google.gcloud.AuthCredentials; import com.google.gcloud.RetryParams; +import com.google.gcloud.storage.Acl.Project.ProjectRole; import org.junit.Test; @@ -28,9 +30,40 @@ import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.Collections; public class SerializationTest { + private static final Acl.Domain ACL_DOMAIN = new Acl.Domain("domain"); + private static final Acl.Group ACL_GROUP = new Acl.Group("group"); + private static final Acl.Project ACL_PROJECT_ = new Acl.Project(ProjectRole.VIEWERS, "pid"); + private static final Acl.User ACL_USER = new Acl.User("user"); + private static final Acl.RawEntity ACL_RAW = new Acl.RawEntity("raw"); + private static final Blob BLOB = Blob.of("b", "n"); + private static final Bucket BUCKET = Bucket.of("b"); + private static final Cors.Origin ORIGIN = Cors.Origin.any(); + private static final Cors CORS = + Cors.builder().maxAgeSeconds(1).origins(Collections.singleton(ORIGIN)).build(); + private static final BatchRequest BATCH_REQUEST = BatchRequest.builder().delete("B", "N").build(); + private static final BatchResponse BATCH_RESPONSE = new BatchResponse( + Collections.singletonList(new BatchResponse.Result<>(true)), + Collections.>emptyList(), + Collections.>emptyList()); + private static final ListResult LIST_RESULT = + new ListResult<>("c", Collections.singletonList(Blob.of("b", "n"))); + private static StorageService.BlobListOption BLOB_LIST_OPTIONS = + StorageService.BlobListOption.maxResults(100); + private static StorageService.BlobSourceOption BLOB_SOURCE_OPTIONS = + StorageService.BlobSourceOption.generationMatch(1); + private static StorageService.BlobTargetOption BLOB_TARGET_OPTIONS = + StorageService.BlobTargetOption.generationMatch(); + private static StorageService.BucketListOption BUCKET_LIST_OPTIONS = + StorageService.BucketListOption.prefix("bla"); + private static StorageService.BucketSourceOption BUCKET_SOURCE_OPTIONS = + StorageService.BucketSourceOption.metagenerationMatch(1); + private static StorageService.BucketTargetOption BUCKET_TARGET_OPTIONS = + StorageService.BucketTargetOption.metagenerationNotMatch(); @Test public void testServiceOptions() throws Exception { @@ -52,8 +85,18 @@ public void testServiceOptions() throws Exception { } @Test - public void testTypes() throws Exception { - // todo: implement + public void testModelAndRequests() throws Exception { + Serializable[] objects = {ACL_DOMAIN, ACL_GROUP, ACL_PROJECT_, ACL_USER, ACL_RAW, BLOB, BUCKET, + ORIGIN, CORS, BATCH_REQUEST,BATCH_RESPONSE, LIST_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); + } } @SuppressWarnings("unchecked")