diff --git a/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/BlobInfo.java b/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/BlobInfo.java index b130b880ce55..d356a8d813ff 100644 --- a/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/BlobInfo.java +++ b/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/BlobInfo.java @@ -47,7 +47,7 @@ * Google Storage object metadata. * * @see Concepts and - * Terminology + * Terminology */ public class BlobInfo implements Serializable { @@ -89,9 +89,7 @@ public StorageObject apply(BlobInfo blobInfo) { private final Boolean temporaryHold; private final Long retentionExpirationTime; - /** - * This class is meant for internal use only. Users are discouraged from using this class. - */ + /** This class is meant for internal use only. Users are discouraged from using this class. */ public static final class ImmutableEmptyMap extends AbstractMap { @Override @@ -116,16 +114,12 @@ public static class CustomerEncryption implements Serializable { this.keySha256 = keySha256; } - /** - * Returns the algorithm used to encrypt the blob. - */ + /** Returns the algorithm used to encrypt the blob. */ public String getEncryptionAlgorithm() { return encryptionAlgorithm; } - /** - * Returns the SHA256 hash of the encryption key. - */ + /** Returns the SHA256 hash of the encryption key. */ public String getKeySha256() { return keySha256; } @@ -147,8 +141,8 @@ public final int hashCode() { public final boolean equals(Object obj) { return obj == this || obj != null - && obj.getClass().equals(CustomerEncryption.class) - && Objects.equals(toPb(), ((CustomerEncryption) obj).toPb()); + && obj.getClass().equals(CustomerEncryption.class) + && Objects.equals(toPb(), ((CustomerEncryption) obj).toPb()); } StorageObject.CustomerEncryption toPb() { @@ -158,19 +152,15 @@ StorageObject.CustomerEncryption toPb() { } static CustomerEncryption fromPb(StorageObject.CustomerEncryption customerEncryptionPb) { - return new CustomerEncryption(customerEncryptionPb.getEncryptionAlgorithm(), - customerEncryptionPb.getKeySha256()); + return new CustomerEncryption( + customerEncryptionPb.getEncryptionAlgorithm(), customerEncryptionPb.getKeySha256()); } } - /** - * Builder for {@code BlobInfo}. - */ + /** Builder for {@code BlobInfo}. */ public abstract static class Builder { - /** - * Sets the blob identity. - */ + /** Sets the blob identity. */ public abstract Builder setBlobId(BlobId blobId); abstract Builder setGeneratedId(String generatedId); @@ -216,7 +206,7 @@ public abstract static class Builder { * Sets the blob's access control configuration. * * @see + * href="https://cloud.google.com/storage/docs/access-control#About-Access-Control-Lists"> * About Access Control Lists */ public abstract Builder setAcl(List acl); @@ -232,31 +222,27 @@ public abstract static class Builder { /** * Sets the MD5 hash of blob's data. MD5 value must be encoded in base64. * - * @see - * Hashes and ETags: Best Practices + * @see Hashes and ETags: + * Best Practices */ public abstract Builder setMd5(String md5); /** - * Sets the CRC32C checksum of blob's data as described in - * RFC 4960, Appendix B; encoded in + * Sets the CRC32C checksum of blob's data as described in RFC 4960, Appendix B; encoded in * base64 in big-endian order. * - * @see - * Hashes and ETags: Best Practices + * @see Hashes and ETags: + * Best Practices */ public abstract Builder setCrc32c(String crc32c); abstract Builder setMediaLink(String mediaLink); - /** - * Sets the blob's storage class. - */ + /** Sets the blob's storage class. */ public abstract Builder setStorageClass(StorageClass storageClass); - /** - * Sets the blob's user provided metadata. - */ + /** Sets the blob's user provided metadata. */ public abstract Builder setMetadata(Map metadata); abstract Builder setMetageneration(Long metageneration); @@ -273,21 +259,15 @@ public abstract static class Builder { abstract Builder setKmsKeyName(String kmsKeyName); - /** - * Sets the blob's event based hold. - */ + /** Sets the blob's event-based hold. */ public abstract Builder setEventBasedHold(Boolean eventBasedHold); - /** - * Sets the blob's temporary hold. - */ + /** Sets the blob's temporary hold. */ public abstract Builder setTemporaryHold(Boolean temporaryHold); abstract Builder setRetentionExpirationTime(Long retentionExpirationTime); - /** - * Creates a {@code BlobInfo} object. - */ + /** Creates a {@code BlobInfo} object. */ public abstract BlobInfo build(); } @@ -455,8 +435,10 @@ Builder setMediaLink(String mediaLink) { @Override public Builder setMetadata(Map metadata) { - this.metadata = metadata != null - ? new HashMap<>(metadata) : Data.>nullOf(ImmutableEmptyMap.class); + this.metadata = + metadata != null + ? new HashMap<>(metadata) + : Data.>nullOf(ImmutableEmptyMap.class); return this; } @@ -564,30 +546,22 @@ public BlobInfo build() { retentionExpirationTime = builder.retentionExpirationTime; } - /** - * Returns the blob's identity. - */ + /** Returns the blob's identity. */ public BlobId getBlobId() { return blobId; } - /** - * Returns the name of the containing bucket. - */ + /** Returns the name of the containing bucket. */ public String getBucket() { return getBlobId().getBucket(); } - /** - * Returns the service-generated for the blob. - */ + /** Returns the service-generated for the blob. */ public String getGeneratedId() { return generatedId; } - /** - * Returns the blob's name. - */ + /** Returns the blob's name. */ public String getName() { return getBlobId().getName(); } @@ -611,9 +585,7 @@ public List getAcl() { return acl; } - /** - * Returns the blob's owner. This will always be the uploader of the blob. - */ + /** Returns the blob's owner. This will always be the uploader of the blob. */ public Acl.Entity getOwner() { return owner; } @@ -664,9 +636,9 @@ public String getContentLanguage() { } /** - * Returns the number of components that make up this blob. Components are accumulated through - * the {@link Storage#compose(Storage.ComposeRequest)} operation and are limited to a count of - * 1024, counting 1 for each non-composite component blob and componentCount for each composite + * Returns the number of components that make up this blob. Components are accumulated through the + * {@link Storage#compose(Storage.ComposeRequest)} operation and are limited to a count of 1024, + * counting 1 for each non-composite component blob and componentCount for each composite * component blob. This value is set only for composite blobs. * * @see Component Count @@ -685,9 +657,7 @@ public String getEtag() { return etag; } - /** - * Returns the URI of this blob as a string. - */ + /** Returns the URI of this blob as a string. */ public String getSelfLink() { return selfLink; } @@ -695,83 +665,71 @@ public String getSelfLink() { /** * Returns the MD5 hash of blob's data encoded in base64. * - * @see - * Hashes and ETags: Best Practices + * @see Hashes and ETags: + * Best Practices */ public String getMd5() { return Data.isNull(md5) ? null : md5; } /** - * Returns the CRC32C checksum of blob's data as described in - * RFC 4960, Appendix B; encoded in + * Returns the CRC32C checksum of blob's data as described in RFC 4960, Appendix B; encoded in * base64 in big-endian order. * - * @see - * Hashes and ETags: Best Practices + * @see Hashes and ETags: + * Best Practices */ public String getCrc32c() { return Data.isNull(crc32c) ? null : crc32c; } - /** - * Returns the blob's media download link. - */ + /** Returns the blob's media download link. */ public String getMediaLink() { return mediaLink; } - /** - * Returns blob's user provided metadata. - */ + /** Returns blob's user provided metadata. */ public Map getMetadata() { return metadata == null || Data.isNull(metadata) ? null : Collections.unmodifiableMap(metadata); } - /** - * Returns blob's data generation. Used for blob versioning. - */ + /** Returns blob's data generation. Used for blob versioning. */ public Long getGeneration() { return getBlobId().getGeneration(); } /** - * Returns blob's metageneration. Used for preconditions and for detecting changes in metadata. - * A metageneration number is only meaningful in the context of a particular generation of a + * Returns blob's metageneration. Used for preconditions and for detecting changes in metadata. A + * metageneration number is only meaningful in the context of a particular generation of a * particular blob. */ public Long getMetageneration() { return metageneration; } - /** - * Returns the deletion time of the blob. - */ + /** Returns the deletion time of the blob. */ public Long getDeleteTime() { return deleteTime; } - /** - * Returns the last modification time of the blob's metadata. - */ + /** Returns the last modification time of the blob's metadata. */ public Long getUpdateTime() { return updateTime; } - /** - * Returns the creation time of the blob. - */ + /** Returns the creation time of the blob. */ public Long getCreateTime() { return createTime; } /** * Returns {@code true} if the current blob represents a directory. This can only happen if the - * blob is returned by {@link Storage#list(String, Storage.BlobListOption...)} when the - * {@link Storage.BlobListOption#currentDirectory()} option is used. When this is the case only - * {@link #getBlobId()} and {@link #getSize()} are set for the current blob: - * {@link BlobId#getName()} ends with the '/' character, {@link BlobId#getGeneration()} returns - * {@code null} and {@link #getSize()} is {@code 0}. + * blob is returned by {@link Storage#list(String, Storage.BlobListOption...)} when the {@link + * Storage.BlobListOption#currentDirectory()} option is used. When this is the case only {@link + * #getBlobId()} and {@link #getSize()} are set for the current blob: {@link BlobId#getName()} + * ends with the '/' character, {@link BlobId#getGeneration()} returns {@code null} and {@link + * #getSize()} is {@code 0}. */ public boolean isDirectory() { return isDirectory; @@ -785,44 +743,77 @@ public CustomerEncryption getCustomerEncryption() { return customerEncryption; } - /** - * Returns the storage class of the blob. - */ + /** Returns the storage class of the blob. */ public StorageClass getStorageClass() { return storageClass; } - /** - * Returns the Cloud KMS key used to encrypt the blob, if any. - */ + /** Returns the Cloud KMS key used to encrypt the blob, if any. */ public String getKmsKeyName() { return kmsKeyName; } /** - * Returns the event based hold status of the blob, if any. + * Returns a {@code Boolean} with either {@code true}, {@code null} and in certain cases {@code + * false}. + * + *

Case 1: {@code true} the field {@link + * com.google.cloud.storage.Storage.BlobField#EVENT_BASED_HOLD} is selected in a {@link + * Storage#get(BlobId, Storage.BlobGetOption...)} and event-based hold for the blob is enabled. + * + *

Case 2.1: {@code null} the field {@link + * com.google.cloud.storage.Storage.BlobField#EVENT_BASED_HOLD} is selected in a {@link + * Storage#get(BlobId, Storage.BlobGetOption...)}, but event-based hold for the blob is not + * enabled. This case can be considered implicitly {@code false}. + * + *

Case 2.2: {@code null} the field {@link + * com.google.cloud.storage.Storage.BlobField#EVENT_BASED_HOLD} is not selected in a {@link + * Storage#get(BlobId, Storage.BlobGetOption...)}, and the state for this field is unknown. + * + *

Case 3: {@code false} event-based hold is explicitly set to false using in a {@link + * Builder#setEventBasedHold(Boolean)} client side for a follow-up request e.g. {@link + * Storage#update(BlobInfo, Storage.BlobTargetOption...)} in which case the value of event-based + * hold will remain {@code false} for the given instance. */ public Boolean getEventBasedHold() { - return eventBasedHold; + return Data.isNull(eventBasedHold) ? null : eventBasedHold; } /** - * Returns the temporary hold status of the blob, if any. + * Returns a {@code Boolean} with either {@code true}, {@code null} and in certain cases {@code + * false}. + * + *

Case 1: {@code true} the field {@link + * com.google.cloud.storage.Storage.BlobField#TEMPORARY_HOLD} is selected in a {@link + * Storage#get(BlobId, Storage.BlobGetOption...)} and temporary hold for the blob is enabled. + * + *

Case 2.1: {@code null} the field {@link + * com.google.cloud.storage.Storage.BlobField#TEMPORARY_HOLD} is selected in a {@link + * Storage#get(BlobId, Storage.BlobGetOption...)}, but temporary hold for the blob is not enabled. + * This case can be considered implicitly {@code false}. + * + *

Case 2.2: {@code null} the field {@link + * com.google.cloud.storage.Storage.BlobField#TEMPORARY_HOLD} is not selected in a {@link + * Storage#get(BlobId, Storage.BlobGetOption...)}, and the state for this field is unknown. + * + *

Case 3: {@code false} event-based hold is explicitly set to false using in a {@link + * Builder#setEventBasedHold(Boolean)} client side for a follow-up request e.g. {@link + * Storage#update(BlobInfo, Storage.BlobTargetOption...)} in which case the value of temporary + * hold will remain {@code false} for the given instance. */ public Boolean getTemporaryHold() { - return temporaryHold; + return Data.isNull(temporaryHold) ? null : temporaryHold; } /** - * Returns the retention expiration time of the blob, if a retention period is defined. + * Returns the retention expiration time of the blob as {@code Long}, if a retention period is + * defined. If retention period is not defined this value returns {@code null} */ public Long getRetentionExpirationTime() { - return retentionExpirationTime; + return Data.isNull(retentionExpirationTime) ? null : retentionExpirationTime; } - /** - * Returns a builder for the current blob. - */ + /** Returns a builder for the current blob. */ public Builder toBuilder() { return new BuilderImpl(this); } @@ -848,19 +839,22 @@ public int hashCode() { public boolean equals(Object obj) { return obj == this || obj != null - && obj.getClass().equals(BlobInfo.class) - && Objects.equals(toPb(), ((BlobInfo) obj).toPb()); + && obj.getClass().equals(BlobInfo.class) + && Objects.equals(toPb(), ((BlobInfo) obj).toPb()); } StorageObject toPb() { StorageObject storageObject = blobId.toPb(); if (acl != null) { - storageObject.setAcl(Lists.transform(acl, new Function() { - @Override - public ObjectAccessControl apply(Acl acl) { - return acl.toObjectPb(); - } - })); + storageObject.setAcl( + Lists.transform( + acl, + new Function() { + @Override + public ObjectAccessControl apply(Acl acl) { + return acl.toObjectPb(); + } + })); } if (deleteTime != null) { storageObject.setTimeDeleted(new DateTime(deleteTime)); @@ -885,8 +879,8 @@ public ObjectAccessControl apply(Acl acl) { if (metadata != null && !Data.isNull(metadata)) { pbMetadata = Maps.newHashMapWithExpectedSize(metadata.size()); for (Map.Entry entry : metadata.entrySet()) { - pbMetadata.put(entry.getKey(), - firstNonNull(entry.getValue(), Data.nullOf(String.class))); + pbMetadata.put( + entry.getKey(), firstNonNull(entry.getValue(), Data.nullOf(String.class))); } } if (customerEncryption != null) { @@ -916,37 +910,27 @@ public ObjectAccessControl apply(Acl acl) { return storageObject; } - /** - * Returns a {@code BlobInfo} builder where blob identity is set using the provided values. - */ + /** Returns a {@code BlobInfo} builder where blob identity is set using the provided values. */ public static Builder newBuilder(BucketInfo bucketInfo, String name) { return newBuilder(bucketInfo.getName(), name); } - /** - * Returns a {@code BlobInfo} builder where blob identity is set using the provided values. - */ + /** Returns a {@code BlobInfo} builder where blob identity is set using the provided values. */ public static Builder newBuilder(String bucket, String name) { return newBuilder(BlobId.of(bucket, name)); } - /** - * Returns a {@code BlobInfo} builder where blob identity is set using the provided values. - */ + /** Returns a {@code BlobInfo} builder where blob identity is set using the provided values. */ public static Builder newBuilder(BucketInfo bucketInfo, String name, Long generation) { return newBuilder(bucketInfo.getName(), name, generation); } - /** - * Returns a {@code BlobInfo} builder where blob identity is set using the provided values. - */ + /** Returns a {@code BlobInfo} builder where blob identity is set using the provided values. */ public static Builder newBuilder(String bucket, String name, Long generation) { return newBuilder(BlobId.of(bucket, name, generation)); } - /** - * Returns a {@code BlobInfo} builder where blob identity is set using the provided value. - */ + /** Returns a {@code BlobInfo} builder where blob identity is set using the provided value. */ public static Builder newBuilder(BlobId blobId) { return new BuilderImpl(blobId); } @@ -1011,13 +995,15 @@ static BlobInfo fromPb(StorageObject storageObject) { builder.setOwner(Acl.Entity.fromPb(storageObject.getOwner().getEntity())); } if (storageObject.getAcl() != null) { - builder.setAcl(Lists.transform(storageObject.getAcl(), - new Function() { - @Override - public Acl apply(ObjectAccessControl objectAccessControl) { - return Acl.fromPb(objectAccessControl); - } - })); + builder.setAcl( + Lists.transform( + storageObject.getAcl(), + new Function() { + @Override + public Acl apply(ObjectAccessControl objectAccessControl) { + return Acl.fromPb(objectAccessControl); + } + })); } if (storageObject.containsKey("isDirectory")) { builder.setIsDirectory(Boolean.TRUE); diff --git a/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/BucketInfo.java b/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/BucketInfo.java index e24a5a7a89eb..0c417a8aee0d 100644 --- a/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/BucketInfo.java +++ b/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/BucketInfo.java @@ -50,7 +50,7 @@ * Google Storage bucket metadata; * * @see Concepts and - * Terminology + * Terminology */ public class BucketInfo implements Serializable { @@ -106,7 +106,11 @@ public abstract static class DeleteRule implements Serializable { private final Type type; public enum Type { - AGE, CREATE_BEFORE, NUM_NEWER_VERSIONS, IS_LIVE, UNKNOWN + AGE, + CREATE_BEFORE, + NUM_NEWER_VERSIONS, + IS_LIVE, + UNKNOWN } DeleteRule(Type type) { @@ -183,8 +187,8 @@ public static class AgeDeleteRule extends DeleteRule { * Creates an {@code AgeDeleteRule} object. * * @param daysToLive blobs' Time To Live expressed in days. The time when the age condition is - * considered to be satisfied is computed by adding {@code daysToLive} days to the - * midnight following blob's creation time in UTC. + * considered to be satisfied is computed by adding {@code daysToLive} days to the midnight + * following blob's creation time in UTC. */ public AgeDeleteRule(int daysToLive) { super(Type.AGE); @@ -222,8 +226,7 @@ private void writeObject(ObjectOutputStream out) throws IOException { out.writeUTF(rule.toString()); } - private void readObject(ObjectInputStream in) throws IOException, - ClassNotFoundException { + private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); rule = new JacksonFactory().fromString(in.readUTF(), Rule.class); } @@ -310,8 +313,8 @@ public static class IsLiveDeleteRule extends DeleteRule { /** * Creates an {@code IsLiveDeleteRule} object. * - * @param isLive if set to {@code true} live blobs meet the delete condition. If set to - * {@code false} delete condition is met by archived blobs. + * @param isLive if set to {@code true} live blobs meet the delete condition. If set to {@code + * false} delete condition is met by archived blobs. */ public IsLiveDeleteRule(boolean isLive) { super(Type.IS_LIVE); @@ -328,16 +331,11 @@ void populateCondition(Rule.Condition condition) { } } - /** - * Builder for {@code BucketInfo}. - */ + /** Builder for {@code BucketInfo}. */ public abstract static class Builder { - Builder() { - } + Builder() {} - /** - * Sets the bucket's name. - */ + /** Sets the bucket's name. */ public abstract Builder setName(String name); abstract Builder setGeneratedId(String generatedId); @@ -347,9 +345,8 @@ public abstract static class Builder { abstract Builder setSelfLink(String selfLink); /** - * Sets whether a user accessing the bucket or an object it contains should assume the transit costs - * related to the access. - * + * Sets whether a user accessing the bucket or an object it contains should assume the transit + * costs related to the access. */ public abstract Builder setRequesterPays(Boolean requesterPays); @@ -365,9 +362,7 @@ public abstract static class Builder { */ public abstract Builder setIndexPage(String indexPage); - /** - * Sets the custom object to return when a requested resource is not found. - */ + /** Sets the custom object to return when a requested resource is not found. */ public abstract Builder setNotFoundPage(String notFoundPage); /** @@ -379,15 +374,15 @@ public abstract static class Builder { /** * Sets the bucket's storage class. This defines how blobs in the bucket are stored and - * determines the SLA and the cost of storage. A list of supported values is available - * here. + * determines the SLA and the cost of storage. A list of supported values is available here. */ public abstract Builder setStorageClass(StorageClass storageClass); /** * Sets the bucket's location. Data for blobs in the bucket resides in physical storage within - * this region. A list of supported values is available - * here. + * this region. A list of supported values is available here. */ public abstract Builder setLocation(String location); @@ -400,8 +395,8 @@ public abstract static class Builder { /** * Sets the bucket's Cross-Origin Resource Sharing (CORS) configuration. * - * @see - * Cross-Origin Resource Sharing (CORS) + * @see Cross-Origin Resource + * Sharing (CORS) */ public abstract Builder setCors(Iterable cors); @@ -409,7 +404,7 @@ public abstract static class Builder { * Sets the bucket's access control configuration. * * @see + * href="https://cloud.google.com/storage/docs/access-control#About-Access-Control-Lists"> * About Access Control Lists */ public abstract Builder setAcl(Iterable acl); @@ -419,24 +414,18 @@ public abstract static class Builder { * configuration is specified. * * @see + * href="https://cloud.google.com/storage/docs/access-control#About-Access-Control-Lists"> * About Access Control Lists */ public abstract Builder setDefaultAcl(Iterable acl); - /** - * Sets the label of this bucket. - */ + /** Sets the label of this bucket. */ public abstract Builder setLabels(Map labels); - /** - * Sets the default Cloud KMS key name for this bucket. - */ + /** Sets the default Cloud KMS key name for this bucket. */ public abstract Builder setDefaultKmsKeyName(String defaultKmsKeyName); - /** - * Sets the default event based hold for this bucket. - */ + /** Sets the default event-based hold for this bucket. */ public abstract Builder setDefaultEventBasedHold(Boolean defaultEventBasedHold); abstract Builder setRetentionEffectiveTime(Long retentionEffectiveTime); @@ -444,14 +433,12 @@ public abstract static class Builder { abstract Builder setRetentionPolicyIsLocked(Boolean retentionPolicyIsLocked); /** - * If policy is not locked this value can be cleared, increased, and decreased. - * If policy is locked the retention period can only be increased. + * If policy is not locked this value can be cleared, increased, and decreased. If policy is + * locked the retention period can only be increased. */ public abstract Builder setRetentionPeriod(Long retentionPeriod); - /** - * Creates a {@code BucketInfo} object. - */ + /** Creates a {@code BucketInfo} object. */ public abstract BucketInfo build(); } @@ -621,26 +608,29 @@ public Builder setLabels(Map labels) { @Override public Builder setDefaultKmsKeyName(String defaultKmsKeyName) { - this.defaultKmsKeyName = defaultKmsKeyName != null - ? defaultKmsKeyName : Data.nullOf(String.class); + this.defaultKmsKeyName = + defaultKmsKeyName != null ? defaultKmsKeyName : Data.nullOf(String.class); return this; } @Override public Builder setDefaultEventBasedHold(Boolean defaultEventBasedHold) { - this.defaultEventBasedHold = firstNonNull(defaultEventBasedHold, Data.nullOf(Boolean.class)); + this.defaultEventBasedHold = + firstNonNull(defaultEventBasedHold, Data.nullOf(Boolean.class)); return this; } @Override Builder setRetentionEffectiveTime(Long retentionEffectiveTime) { - this.retentionEffectiveTime = firstNonNull(retentionEffectiveTime, Data.nullOf(Long.class)); + this.retentionEffectiveTime = + firstNonNull(retentionEffectiveTime, Data.nullOf(Long.class)); return this; } @Override Builder setRetentionPolicyIsLocked(Boolean retentionPolicyIsLocked) { - this.retentionPolicyIsLocked = firstNonNull(retentionPolicyIsLocked, Data.nullOf(Boolean.class)); + this.retentionPolicyIsLocked = + firstNonNull(retentionPolicyIsLocked, Data.nullOf(Boolean.class)); return this; } @@ -683,45 +673,66 @@ public BucketInfo build() { retentionPeriod = builder.retentionPeriod; } - /** - * Returns the service-generated id for the bucket. - */ + /** Returns the service-generated id for the bucket. */ public String getGeneratedId() { return generatedId; } - /** - * Returns the bucket's name. - */ + /** Returns the bucket's name. */ public String getName() { return name; } - /** - * Returns the bucket's owner. This is always the project team's owner group. - */ + /** Returns the bucket's owner. This is always the project team's owner group. */ public Entity getOwner() { return owner; } - /** - * Returns the URI of this bucket as a string. - */ + /** Returns the URI of this bucket as a string. */ public String getSelfLink() { return selfLink; } /** - * Returns {@code true} if versioning is fully enabled for this bucket, {@code false} otherwise. + * Returns a {@code Boolean} with either {@code true}, {@code null} and in certain cases {@code + * false}. + * + *

Case 1: {@code true} the field {@link + * com.google.cloud.storage.Storage.BucketField#VERSIONING} is selected in a {@link + * Storage#get(String, Storage.BucketGetOption...)} and versions for the bucket is enabled. + * + *

Case 2.1: {@code null} the field {@link + * com.google.cloud.storage.Storage.BucketField#VERSIONING} is selected in a {@link + * Storage#get(String, Storage.BucketGetOption...)}, but versions for the bucket is not enabled. + * This case can be considered implicitly {@code false}. + * + *

Case 2.2: {@code null} the field {@link + * com.google.cloud.storage.Storage.BucketField#VERSIONING} is not selected in a {@link + * Storage#get(String, Storage.BucketGetOption...)}, and the state for this field is unknown. + * + *

Case 3: {@code false} versions is explicitly set to false client side for a follow-up + * request for example {@link Storage#update(BucketInfo, Storage.BucketTargetOption...)} in which + * case the value of versions will remain {@code false} for for the given instance. */ public Boolean versioningEnabled() { return Data.isNull(versioningEnabled) ? null : versioningEnabled; } /** - * Returns {@code true} if a user accessing the bucket or an object it contains should assume the transit costs - * related to the access, {@code false} otherwise. + * Returns a {@code Boolean} with either {@code true}, {@code false}, and in a specific case + * {@code null}. + * + *

Case 1: {@code true} the field {@link com.google.cloud.storage.Storage.BucketField#BILLING} + * is selected in a {@link Storage#get(String, Storage.BucketGetOption...)} and requester pays for + * the bucket is enabled. + * + *

Case 2: {@code false} the field {@link com.google.cloud.storage.Storage.BucketField#BILLING} + * in a {@link Storage#get(String, Storage.BucketGetOption...)} is selected and requester pays for + * the bucket is disable. * + *

Case 3: {@code null} the field {@link com.google.cloud.storage.Storage.BucketField#BILLING} + * in a {@link Storage#get(String, Storage.BucketGetOption...)} is not selected, the value is + * unknown. */ public Boolean requesterPays() { return Data.isNull(requesterPays) ? null : requesterPays; @@ -735,9 +746,7 @@ public String getIndexPage() { return indexPage; } - /** - * Returns the custom object to return when a requested resource is not found. - */ + /** Returns the custom object to return when a requested resource is not found. */ public String getNotFoundPage() { return notFoundPage; } @@ -760,16 +769,12 @@ public String getEtag() { return etag; } - /** - * Returns the time at which the bucket was created. - */ + /** Returns the time at which the bucket was created. */ public Long getCreateTime() { return createTime; } - /** - * Returns the metadata generation of this bucket. - */ + /** Returns the metadata generation of this bucket. */ public Long getMetageneration() { return metageneration; } @@ -797,8 +802,8 @@ public StorageClass getStorageClass() { /** * Returns the bucket's Cross-Origin Resource Sharing (CORS) configuration. * - * @see - * Cross-Origin Resource Sharing (CORS) + * @see Cross-Origin Resource Sharing + * (CORS) */ public List getCors() { return cors; @@ -824,45 +829,78 @@ public List getDefaultAcl() { return defaultAcl; } - /** - * Returns the labels for this bucket. - */ + /** Returns the labels for this bucket. */ public Map getLabels() { return labels; } - /** - * Returns the default Cloud KMS key to be applied to newly inserted objects in this bucket. - */ + /** Returns the default Cloud KMS key to be applied to newly inserted objects in this bucket. */ public String getDefaultKmsKeyName() { return defaultKmsKeyName; } /** - * Returns the default event based hold value used for inserted objects in this bucket. + * Returns a {@code Boolean} with either {@code true}, {@code null} and in certain cases {@code + * false}. + * + *

Case 1: {@code true} the field {@link + * com.google.cloud.storage.Storage.BucketField#DEFAULT_EVENT_BASED_HOLD} is selected in a {@link + * Storage#get(String, Storage.BucketGetOption...)} and default event-based hold for the bucket is + * enabled. + * + *

Case 2.1: {@code null} the field {@link + * com.google.cloud.storage.Storage.BucketField#DEFAULT_EVENT_BASED_HOLD} is selected in a {@link + * Storage#get(String, Storage.BucketGetOption...)}, but default event-based hold for the bucket + * is not enabled. This case can be considered implicitly {@code false}. + * + *

Case 2.2: {@code null} the field {@link + * com.google.cloud.storage.Storage.BucketField#DEFAULT_EVENT_BASED_HOLD} is not selected in a + * {@link Storage#get(String, Storage.BucketGetOption...)}, and the state for this field is + * unknown. + * + *

Case 3: {@code false} default event-based hold is explicitly set to false using in a {@link + * Builder#setDefaultEventBasedHold(Boolean)} client side for a follow-up request e.g. {@link + * Storage#update(BucketInfo, Storage.BucketTargetOption...)} in which case the value of default + * event-based hold will remain {@code false} for the given instance. */ - public Boolean getDefaultEventBasedHold() { return defaultEventBasedHold; } + public Boolean getDefaultEventBasedHold() { + return Data.isNull(defaultEventBasedHold) ? null : defaultEventBasedHold; + } /** - * Returns the retention effective time a policy took effect if a retention policy is defined. + * Returns the retention effective time a policy took effect if a retention policy is defined as a + * {@code Long}. */ - public Long getRetentionEffectiveTime() { return retentionEffectiveTime; } + public Long getRetentionEffectiveTime() { + return retentionEffectiveTime; + } /** - * Returns {@code true} if the bucket retention policy is locked, {@code false} otherwise. + * Returns a {@code Boolean} with either {@code true} or {@code null}. + * + *

Case 1: {@code true} the field {@link + * com.google.cloud.storage.Storage.BucketField#RETENTION_POLICY} is selected in a {@link + * Storage#get(String, Storage.BucketGetOption...)} and retention policy for the bucket is locked. + * + *

Case 2.1: {@code null} the field {@link + * com.google.cloud.storage.Storage.BucketField#RETENTION_POLICY} is selected in a {@link + * Storage#get(String, Storage.BucketGetOption...)}, but retention policy for the bucket is not + * locked. This case can be considered implicitly {@code false}. + * + *

Case 2.2: {@code null} the field {@link + * com.google.cloud.storage.Storage.BucketField#RETENTION_POLICY} is not selected in a {@link + * Storage#get(String, Storage.BucketGetOption...)}, and the state for this field is unknown. */ public Boolean retentionPolicyIsLocked() { return Data.isNull(retentionPolicyIsLocked) ? null : retentionPolicyIsLocked; } - /** - * Returns the retention policy retention period. - */ - public Long getRetentionPeriod() { return retentionPeriod; } + /** Returns the retention policy retention period. */ + public Long getRetentionPeriod() { + return retentionPeriod; + } - /** - * Returns a builder for the current bucket. - */ + /** Returns a builder for the current bucket. */ public Builder toBuilder() { return new BuilderImpl(this); } @@ -876,15 +914,13 @@ public int hashCode() { public boolean equals(Object obj) { return obj == this || obj != null - && obj.getClass().equals(BucketInfo.class) - && Objects.equals(toPb(), ((BucketInfo) obj).toPb()); + && obj.getClass().equals(BucketInfo.class) + && Objects.equals(toPb(), ((BucketInfo) obj).toPb()); } @Override public String toString() { - return MoreObjects.toStringHelper(this) - .add("name", name) - .toString(); + return MoreObjects.toStringHelper(this).add("name", name).toString(); } com.google.api.services.storage.model.Bucket toPb() { @@ -909,20 +945,26 @@ com.google.api.services.storage.model.Bucket toPb() { bucketPb.setCors(transform(cors, Cors.TO_PB_FUNCTION)); } if (acl != null) { - bucketPb.setAcl(transform(acl, new Function() { - @Override - public BucketAccessControl apply(Acl acl) { - return acl.toBucketPb(); - } - })); + bucketPb.setAcl( + transform( + acl, + new Function() { + @Override + public BucketAccessControl apply(Acl acl) { + return acl.toBucketPb(); + } + })); } if (defaultAcl != null) { - bucketPb.setDefaultObjectAcl(transform(defaultAcl, new Function() { - @Override - public ObjectAccessControl apply(Acl acl) { - return acl.toObjectPb(); - } - })); + bucketPb.setDefaultObjectAcl( + transform( + defaultAcl, + new Function() { + @Override + public ObjectAccessControl apply(Acl acl) { + return acl.toObjectPb(); + } + })); } if (owner != null) { bucketPb.setOwner(new Owner().setEntity(owner.toPb())); @@ -944,12 +986,15 @@ public ObjectAccessControl apply(Acl acl) { } if (deleteRules != null) { Lifecycle lifecycle = new Lifecycle(); - lifecycle.setRule(transform(deleteRules, new Function() { - @Override - public Rule apply(DeleteRule deleteRule) { - return deleteRule.toPb(); - } - })); + lifecycle.setRule( + transform( + deleteRules, + new Function() { + @Override + public Rule apply(DeleteRule deleteRule) { + return deleteRule.toPb(); + } + })); bucketPb.setLifecycle(lifecycle); } if (labels != null) { @@ -961,33 +1006,33 @@ public Rule apply(DeleteRule deleteRule) { if (defaultEventBasedHold != null) { bucketPb.setDefaultEventBasedHold(defaultEventBasedHold); } - if (retentionPeriod != null || retentionEffectiveTime != null || retentionPolicyIsLocked != null) { - Bucket.RetentionPolicy retentionPolicy = new Bucket.RetentionPolicy(); - if (retentionPeriod != null) { + if (retentionPeriod != null) { + if (Data.isNull(retentionPeriod)) { + bucketPb.setRetentionPolicy( + Data.nullOf(Bucket.RetentionPolicy.class)); + } else { + Bucket.RetentionPolicy retentionPolicy = new Bucket.RetentionPolicy(); retentionPolicy.setRetentionPeriod(retentionPeriod); + if (retentionEffectiveTime != null) { + retentionPolicy.setEffectiveTime(new DateTime(retentionEffectiveTime)); + } + if (retentionPolicyIsLocked != null) { + retentionPolicy.setIsLocked(retentionPolicyIsLocked); + } + bucketPb.setRetentionPolicy(retentionPolicy); + } - if (retentionEffectiveTime != null) { - retentionPolicy.setEffectiveTime(new DateTime(retentionEffectiveTime)); - } - if (retentionPolicyIsLocked != null) { - retentionPolicy.setIsLocked(retentionPolicyIsLocked); - } - bucketPb.setRetentionPolicy(retentionPolicy); } return bucketPb; } - /** - * Creates a {@code BucketInfo} object for the provided bucket name. - */ + /** Creates a {@code BucketInfo} object for the provided bucket name. */ public static BucketInfo of(String name) { return newBuilder(name).build(); } - /** - * Returns a {@code BucketInfo} builder where the bucket's name is set to the provided name. - */ + /** Returns a {@code BucketInfo} builder where the bucket's name is set to the provided name. */ public static Builder newBuilder(String name) { return new BuilderImpl(name); } @@ -1019,21 +1064,26 @@ static BucketInfo fromPb(com.google.api.services.storage.model.Bucket bucketPb) builder.setCors(transform(bucketPb.getCors(), Cors.FROM_PB_FUNCTION)); } if (bucketPb.getAcl() != null) { - builder.setAcl(transform(bucketPb.getAcl(), new Function() { - @Override - public Acl apply(BucketAccessControl bucketAccessControl) { - return Acl.fromPb(bucketAccessControl); - } - })); + builder.setAcl( + transform( + bucketPb.getAcl(), + new Function() { + @Override + public Acl apply(BucketAccessControl bucketAccessControl) { + return Acl.fromPb(bucketAccessControl); + } + })); } if (bucketPb.getDefaultObjectAcl() != null) { - builder.setDefaultAcl(transform(bucketPb.getDefaultObjectAcl(), - new Function() { - @Override - public Acl apply(ObjectAccessControl objectAccessControl) { - return Acl.fromPb(objectAccessControl); - } - })); + builder.setDefaultAcl( + transform( + bucketPb.getDefaultObjectAcl(), + new Function() { + @Override + public Acl apply(ObjectAccessControl objectAccessControl) { + return Acl.fromPb(objectAccessControl); + } + })); } if (bucketPb.getOwner() != null) { builder.setOwner(Entity.fromPb(bucketPb.getOwner().getEntity())); @@ -1047,13 +1097,15 @@ public Acl apply(ObjectAccessControl objectAccessControl) { builder.setNotFoundPage(website.getNotFoundPage()); } if (bucketPb.getLifecycle() != null && bucketPb.getLifecycle().getRule() != null) { - builder.setDeleteRules(transform(bucketPb.getLifecycle().getRule(), - new Function() { - @Override - public DeleteRule apply(Rule rule) { - return DeleteRule.fromPb(rule); - } - })); + builder.setDeleteRules( + transform( + bucketPb.getLifecycle().getRule(), + new Function() { + @Override + public DeleteRule apply(Rule rule) { + return DeleteRule.fromPb(rule); + } + })); } if (bucketPb.getLabels() != null) { builder.setLabels(bucketPb.getLabels()); @@ -1063,7 +1115,9 @@ public DeleteRule apply(Rule rule) { builder.setRequesterPays(billing.getRequesterPays()); } Encryption encryption = bucketPb.getEncryption(); - if (encryption != null && encryption.getDefaultKmsKeyName() != null && !encryption.getDefaultKmsKeyName().isEmpty()) { + if (encryption != null + && encryption.getDefaultKmsKeyName() != null + && !encryption.getDefaultKmsKeyName().isEmpty()) { builder.setDefaultKmsKeyName(encryption.getDefaultKmsKeyName()); } if (bucketPb.getDefaultEventBasedHold() != null) { diff --git a/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/Storage.java b/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/Storage.java index b67fe2645541..c00ef60e94aa 100644 --- a/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/Storage.java +++ b/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/Storage.java @@ -137,6 +137,7 @@ enum BlobField implements FieldSelector { SIZE("size"), STORAGE_CLASS("storageClass"), TIME_DELETED("timeDeleted"), + TIME_CREATED("timeCreated"), KMS_KEY_NAME("kmsKeyName"), EVENT_BASED_HOLD("eventBasedHold"), TEMPORARY_HOLD("temporaryHold"), diff --git a/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/testing/RemoteStorageHelper.java b/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/testing/RemoteStorageHelper.java index f637f5cee4cf..666b9ed6449b 100644 --- a/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/testing/RemoteStorageHelper.java +++ b/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/testing/RemoteStorageHelper.java @@ -20,6 +20,7 @@ import com.google.api.gax.retrying.RetrySettings; import com.google.auth.oauth2.GoogleCredentials; import com.google.cloud.http.HttpTransportOptions; +import com.google.cloud.storage.Blob; import com.google.cloud.storage.BlobId; import com.google.cloud.storage.BlobInfo; import com.google.cloud.storage.Bucket; @@ -80,6 +81,12 @@ public void run() { for (Bucket bucket : buckets.iterateAll()) { if (bucket.getCreateTime() < olderThan) { try { + for (Blob blob : bucket.list(BlobListOption.fields(Storage.BlobField.EVENT_BASED_HOLD, + Storage.BlobField.TEMPORARY_HOLD)).iterateAll()) { + if(blob.getEventBasedHold() == true || blob.getTemporaryHold() == true) { + storage.update(blob.toBuilder().setTemporaryHold(false).setEventBasedHold(false).build()); + } + } forceDelete(storage, bucket.getName()); } catch (Exception e) { // Ignore the exception, maybe the bucket is being deleted by someone else. diff --git a/google-cloud-clients/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITStorageTest.java b/google-cloud-clients/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITStorageTest.java index a771abe4704e..5b407b30bf32 100644 --- a/google-cloud-clients/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITStorageTest.java +++ b/google-cloud-clients/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITStorageTest.java @@ -2058,6 +2058,8 @@ public void testRetentionPolicyNoLock() throws ExecutionException, InterruptedEx assertNotNull(remoteBlob.getRetentionExpirationTime()); remoteBucket = remoteBucket.toBuilder().setRetentionPeriod(null).build().update(); assertNull(remoteBucket.getRetentionPeriod()); + remoteBucket = remoteBucket.toBuilder().setRetentionPeriod(null).build().update(); + assertNull(remoteBucket.getRetentionPeriod()); } finally { RemoteStorageHelper.forceDelete(storage, bucketName, 5, TimeUnit.SECONDS); } @@ -2081,7 +2083,7 @@ private void retentionPolicyLockRequesterPays(boolean requesterPays) throws Exec } Bucket remoteBucket = storage.create(bucketInfo); try { - assertFalse(remoteBucket.retentionPolicyIsLocked()); + assertNull(remoteBucket.retentionPolicyIsLocked()); assertNotNull(remoteBucket.getRetentionEffectiveTime()); assertNotNull(remoteBucket.getMetageneration()); if (requesterPays) { @@ -2186,7 +2188,7 @@ public void testAttemptDeletionObjectTemporaryHold() { } @Test - public void testGetServiceAccount() throws InterruptedException { + public void testGetServiceAccount() { String projectId = remoteStorageHelper.getOptions().getProjectId(); ServiceAccount serviceAccount = storage.getServiceAccount(projectId); assertNotNull(serviceAccount); diff --git a/google-cloud-examples/src/main/java/com/google/cloud/examples/storage/snippets/StorageSnippets.java b/google-cloud-examples/src/main/java/com/google/cloud/examples/storage/snippets/StorageSnippets.java index 9d943c87331e..0a9a14599cf2 100644 --- a/google-cloud-examples/src/main/java/com/google/cloud/examples/storage/snippets/StorageSnippets.java +++ b/google-cloud-examples/src/main/java/com/google/cloud/examples/storage/snippets/StorageSnippets.java @@ -57,6 +57,7 @@ import com.google.cloud.storage.StorageClass; import com.google.cloud.storage.StorageException; import com.google.cloud.storage.StorageOptions; +import java.util.Date; import java.io.ByteArrayInputStream; import java.io.FileInputStream; @@ -1113,4 +1114,280 @@ public Bucket setDefaultKmsKey(String bucketName, String kmsKeyName) throws Stor // [END storage_set_bucket_default_kms_key] return bucket; } + + /** Example of displaying Blob metadata */ + public void getBlobMetadata(String bucketName, String blobName) throws StorageException { + // [START storage_get_metadata] + // Instantiate a Google Cloud Storage client + Storage storage = StorageOptions.getDefaultInstance().getService(); + + // The name of a bucket, e.g. "my-bucket" + // String bucketName = "my-bucket"; + + // The name of a blob, e.g. "my-blob" + // String blobName = "my-blob"; + + // Select all fields + // Fields can be selected individually e.g. Storage.BlobField.CACHE_CONTROL + Blob blob = storage.get(bucketName, blobName, BlobGetOption.fields(Storage.BlobField.values())); + + // Print blob metadata + System.out.println("Bucket: " + blob.getBucket()); + System.out.println("CacheControl: " + blob.getCacheControl()); + System.out.println("ComponentCount: " + blob.getComponentCount()); + System.out.println("ContentDisposition: " + blob.getContentDisposition()); + System.out.println("ContentEncoding: " + blob.getContentEncoding()); + System.out.println("ContentLanguage: " + blob.getContentLanguage()); + System.out.println("ContentType: " + blob.getContentType()); + System.out.println("Crc32c: " + blob.getCrc32c()); + System.out.println("ETag: " + blob.getEtag()); + System.out.println("Generation: " + blob.getGeneration()); + System.out.println("Id: " + blob.getBlobId()); + System.out.println("KmsKeyName: " + blob.getKmsKeyName()); + System.out.println("Md5Hash: " + blob.getMd5()); + System.out.println("MediaLink: " + blob.getMediaLink()); + System.out.println("Metageneration: " + blob.getMetageneration()); + System.out.println("Name: " + blob.getName()); + System.out.println("Size: " + blob.getSize()); + System.out.println("StorageClass: " + blob.getStorageClass()); + System.out.println("TimeCreated: " + new Date(blob.getCreateTime())); + System.out.println("Last Metadata Update: " + new Date(blob.getUpdateTime())); + Boolean temporaryHoldIsEnabled = (blob.getTemporaryHold() != null && blob.getTemporaryHold()); + System.out.println("temporaryHold: " + (temporaryHoldIsEnabled ? "enabled" : "disabled")); + Boolean eventBasedHoldIsEnabled = (blob.getEventBasedHold() != null && blob.getEventBasedHold()); + System.out.println("eventBasedHold: " + (eventBasedHoldIsEnabled ? "enabled" : "disabled")); + if (blob.getRetentionExpirationTime() != null) { + System.out.println("retentionExpirationTime: " + new Date(blob.getRetentionExpirationTime())); + } + if (blob.getMetadata() != null) { + System.out.println("\n\n\nUser metadata:"); + for (Map.Entry userMetadata : blob.getMetadata().entrySet()) { + System.out.println(userMetadata.getKey() + "=" + userMetadata.getValue()); + } + } + // [END storage_get_metadata] + } + + /** Example of setting a retention policy on a bucket */ + public Bucket setRetentionPolicy(String bucketName, Long retentionPeriod) + throws StorageException { + // [START storage_set_retention_policy] + // Instantiate a Google Cloud Storage client + Storage storage = StorageOptions.getDefaultInstance().getService(); + + // The name of a bucket, e.g. "my-bucket" + // String bucketName = "my-bucket"; + + // The retention period for objects in bucket + // Long retentionPeriod = 3600L; // 1 hour in seconds + + Bucket bucketWithRetentionPolicy = + storage.update( + BucketInfo.newBuilder(bucketName).setRetentionPeriod(retentionPeriod).build()); + + System.out.println( + "Retention period for " + bucketName + " is now " + bucketWithRetentionPolicy.getRetentionPeriod()); + // [END storage_set_retention_policy] + return bucketWithRetentionPolicy; + } + + /** Example of removing a retention policy on a bucket */ + public Bucket removeRetentionPolicy(String bucketName) throws StorageException, IllegalArgumentException { + // [START storage_remove_retention_policy] + // Instantiate a Google Cloud Storage client + Storage storage = StorageOptions.getDefaultInstance().getService(); + + // The name of a bucket, e.g. "my-bucket" + // String bucketName = "my-bucket"; + + Bucket bucket = storage.get(bucketName, BucketGetOption.fields(BucketField.RETENTION_POLICY)); + if (bucket.retentionPolicyIsLocked() != null && bucket.retentionPolicyIsLocked()) { + throw new IllegalArgumentException("Unable to remove retention period as retention policy is locked."); + } + + Bucket bucketWithoutRetentionPolicy = bucket.toBuilder().setRetentionPeriod(null).build().update(); + + System.out.println("Retention period for " + bucketName + " has been removed"); + // [END storage_remove_retention_policy] + return bucketWithoutRetentionPolicy; + } + + /** Example of removing a retention policy on a bucket */ + public Bucket getRetentionPolicy(String bucketName) throws StorageException { + // [START storage_get_retention_policy] + // Instantiate a Google Cloud Storage client + Storage storage = StorageOptions.getDefaultInstance().getService(); + + // The name of a bucket, e.g. "my-bucket" + // String bucketName = "my-bucket"; + + Bucket bucket = storage.get(bucketName, BucketGetOption.fields(BucketField.RETENTION_POLICY)); + + System.out.println("Retention Policy for " + bucketName); + System.out.println("Retention Period: " + bucket.getRetentionPeriod()); + if (bucket.retentionPolicyIsLocked() != null && bucket.retentionPolicyIsLocked()) { + System.out.println("Retention Policy is locked"); + } + if (bucket.getRetentionEffectiveTime() != null) { + System.out.println("Effective Time: " + new Date(bucket.getRetentionEffectiveTime())); + } + // [END storage_get_retention_policy] + return bucket; + } + + /** Example of how to lock a bucket retention policy */ + public Bucket lockRetentionPolicy(String bucketName) throws StorageException { + // [START storage_lock_retention_policy] + // Instantiate a Google Cloud Storage client + Storage storage = StorageOptions.getDefaultInstance().getService(); + + // The name of a bucket, e.g. "my-bucket" + // String bucketName = "my-bucket"; + + Bucket bucket = + storage.get(bucketName, Storage.BucketGetOption.fields(BucketField.METAGENERATION)); + Bucket lockedBucket = + bucket.lockRetentionPolicy(Storage.BucketTargetOption.metagenerationMatch()); + + System.out.println("Retention period for " + bucketName + " is now locked"); + System.out.println( + "Retention policy effective as of " + new Date(lockedBucket.getRetentionEffectiveTime())); + // [END storage_lock_retention_policy] + return lockedBucket; + } + + /** Example of how to enable default event-based hold for a bucket */ + public Bucket enableDefaultEventBasedHold(String bucketName) throws StorageException { + // [START storage_enable_default_event_based_hold] + // Instantiate a Google Cloud Storage client + Storage storage = StorageOptions.getDefaultInstance().getService(); + + // The name of a bucket, e.g. "my-bucket" + // String bucketName = "my-bucket"; + + Bucket bucket = + storage.update(BucketInfo.newBuilder(bucketName).setDefaultEventBasedHold(true).build()); + + System.out.println("Default event-based hold was enabled for " + bucketName); + // [END storage_enable_default_event_based_hold] + return bucket; + } + + /** Example of how to disable default event-based hold for a bucket */ + public Bucket disableDefaultEventBasedHold(String bucketName) throws StorageException { + // [START storage_disable_default_event_based_hold] + // Instantiate a Google Cloud Storage client + Storage storage = StorageOptions.getDefaultInstance().getService(); + + // The name of a bucket, e.g. "my-bucket" + // String bucketName = "my-bucket"; + + Bucket bucket = + storage.update(BucketInfo.newBuilder(bucketName).setDefaultEventBasedHold(false).build()); + + System.out.println("Default event-based hold was disabled for " + bucketName); + // [END storage_disable_default_event_based_hold] + return bucket; + } + + /** Example of how to get default event-based hold for a bucket */ + public Bucket getDefaultEventBasedHold(String bucketName) throws StorageException { + // [START storage_get_default_event_based_hold] + // Instantiate a Google Cloud Storage client + Storage storage = StorageOptions.getDefaultInstance().getService(); + + // The name of a bucket, e.g. "my-bucket" + // String bucketName = "my-bucket"; + + Bucket bucket = + storage.get(bucketName, BucketGetOption.fields(BucketField.DEFAULT_EVENT_BASED_HOLD)); + + if (bucket.getDefaultEventBasedHold() != null && bucket.getDefaultEventBasedHold()) { + System.out.println("Default event-based hold is enabled for " + bucketName); + } else { + System.out.println("Default event-based hold is not enabled for " + bucketName); + } + // [END storage_get_default_event_based_hold] + return bucket; + } + + /** Example of how to set event-based hold for a blob */ + public Blob setEventBasedHold(String bucketName, String blobName) throws StorageException { + // [START storage_set_event_based_hold] + // Instantiate a Google Cloud Storage client + Storage storage = StorageOptions.getDefaultInstance().getService(); + + // The name of a bucket, e.g. "my-bucket" + // String bucketName = "my-bucket"; + + // The name of a blob, e.g. "my-blob" + // String blobName = "my-blob"; + + BlobId blobId = BlobId.of(bucketName, blobName); + Blob blob = storage.update(BlobInfo.newBuilder(blobId).setEventBasedHold(true).build()); + + System.out.println("Event-based hold was set for " + blobName); + // [END storage_set_event_based_hold] + return blob; + } + + /** Example of how to release event-based hold for a blob */ + public Blob releaseEventBasedHold(String bucketName, String blobName) throws StorageException { + // [START storage_release_event_based_hold] + // Instantiate a Google Cloud Storage client + Storage storage = StorageOptions.getDefaultInstance().getService(); + + // The name of a bucket, e.g. "my-bucket" + // String bucketName = "my-bucket"; + + // The name of a blob, e.g. "my-blob" + // String blobName = "my-blob"; + + BlobId blobId = BlobId.of(bucketName, blobName); + Blob blob = storage.update(BlobInfo.newBuilder(blobId).setEventBasedHold(false).build()); + + System.out.println("Event-based hold was released for " + blobName); + // [END storage_release_event_based_hold] + return blob; + } + + /** Example of how to set a temporary hold for a blob */ + public Blob setTemporaryHold(String bucketName, String blobName) throws StorageException { + // [START storage_set_temporary_hold] + // Instantiate a Google Cloud Storage client + Storage storage = StorageOptions.getDefaultInstance().getService(); + + // The name of a bucket, e.g. "my-bucket" + // String bucketName = "my-bucket"; + + // The name of a blob, e.g. "my-blob" + // String blobName = "my-blob"; + + BlobId blobId = BlobId.of(bucketName, blobName); + Blob blob = storage.update(BlobInfo.newBuilder(blobId).setTemporaryHold(true).build()); + + System.out.println("Temporary hold was set for " + blobName); + // [END storage_set_temporary_hold] + return blob; + } + + /** Example of how to release a temporary hold for a blob */ + public Blob releaseTemporaryHold(String bucketName, String blobName) throws StorageException { + // [START storage_release_temporary_hold] + // Instantiate a Google Cloud Storage client + Storage storage = StorageOptions.getDefaultInstance().getService(); + + // The name of a bucket, e.g. "my-bucket" + // String bucketName = "my-bucket"; + + // The name of a blob, e.g. "my-blob" + // String blobName = "my-blob"; + + BlobId blobId = BlobId.of(bucketName, blobName); + Blob blob = storage.update(BlobInfo.newBuilder(blobId).setTemporaryHold(false).build()); + + System.out.println("Temporary hold was released for " + blobName); + // [END storage_release_temporary_hold] + return blob; + } } diff --git a/google-cloud-examples/src/test/java/com/google/cloud/examples/storage/snippets/ITStorageSnippets.java b/google-cloud-examples/src/test/java/com/google/cloud/examples/storage/snippets/ITStorageSnippets.java index fffd0c3eb5b5..edfbdad30514 100644 --- a/google-cloud-examples/src/test/java/com/google/cloud/examples/storage/snippets/ITStorageSnippets.java +++ b/google-cloud-examples/src/test/java/com/google/cloud/examples/storage/snippets/ITStorageSnippets.java @@ -38,6 +38,7 @@ import com.google.cloud.storage.Storage; import com.google.cloud.storage.StorageException; import com.google.cloud.storage.testing.RemoteStorageHelper; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterators; import com.google.common.collect.Sets; @@ -48,13 +49,15 @@ import org.junit.rules.ExpectedException; import org.junit.rules.Timeout; +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; +import java.io.PrintStream; import java.net.URL; import java.net.URLConnection; import java.nio.file.Files; import java.nio.file.Paths; -import java.util.ArrayList; +import java.util.Date; import java.util.HashSet; import java.util.Iterator; import java.util.List; @@ -74,7 +77,7 @@ public class ITStorageSnippets { private static final String KMS_KEY_NAME = "projects/gcloud-devel/locations/us/" + "keyRings/gcs_kms_key_ring_us/cryptoKeys/key"; - + private static final Long RETENTION_PERIOD = 5L; // 5 seconds private static Storage storage; private static StorageSnippets storageSnippets; private static List bucketsToCleanUp; @@ -87,20 +90,22 @@ public class ITStorageSnippets { public static void beforeClass() { RemoteStorageHelper helper = RemoteStorageHelper.create(); storage = helper.getOptions().getService(); - bucketsToCleanUp = new ArrayList(); storageSnippets = new StorageSnippets(storage); storageSnippets.createBucket(BUCKET); - bucketsToCleanUp.add(BUCKET); } @AfterClass public static void afterClass() throws ExecutionException, InterruptedException { if (storage != null) { - for (String bucket : bucketsToCleanUp) { - boolean wasDeleted = RemoteStorageHelper.forceDelete(storage, bucket, 5, TimeUnit.SECONDS); - if (!wasDeleted && log.isLoggable(Level.WARNING)) { - log.log(Level.WARNING, "Deletion of bucket {0} timed out, bucket is not empty", bucket); - } + // In beforeClass, we make buckets auto-delete blobs older than a day old. + // Here, delete all buckets older than 2 days. They should already be empty and easy. + long cleanTime = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(2); + long cleanTimeout = System.currentTimeMillis() - TimeUnit.MINUTES.toMillis(1); + RemoteStorageHelper.cleanBuckets(storage, cleanTime, cleanTimeout); + + boolean wasDeleted = RemoteStorageHelper.forceDelete(storage, BUCKET, 1, TimeUnit.MINUTES); + if (!wasDeleted && log.isLoggable(Level.WARNING)) { + log.log(Level.WARNING, "Deletion of bucket {0} timed out, bucket is not empty", BUCKET); } } } @@ -109,7 +114,6 @@ public static void afterClass() throws ExecutionException, InterruptedException public void testCreateBucketWithStorageClassAndLocation() throws ExecutionException, InterruptedException { String tempBucket = RemoteStorageHelper.generateBucketName(); - bucketsToCleanUp.add(tempBucket); Bucket bucket = storageSnippets.createBucketWithStorageClassAndLocation(tempBucket); @@ -435,6 +439,45 @@ public void testBlobDownload() throws Exception { assertArrayEquals(BLOB_BYTE_CONTENT, readBytes); } + @Test + public void testGetBlobMetadata() { + String blobName = "test-create-empty-blob"; + BlobId blobId = BlobId.of(BUCKET, blobName); + BlobInfo blobInfo = BlobInfo.newBuilder(blobId).setMetadata(ImmutableMap.of("k", "v")).build(); + Blob remoteBlob = storage.create(blobInfo, BLOB_BYTE_CONTENT); + assertNotNull(remoteBlob); + final ByteArrayOutputStream snippetOutputCapture = new ByteArrayOutputStream(); + System.setOut(new PrintStream(snippetOutputCapture)); + storageSnippets.getBlobMetadata(BUCKET, blobName); + String snippetOutput = snippetOutputCapture.toString(); + System.setOut(System.out); + assertTrue(snippetOutput.contains("Bucket: " + remoteBlob.getBucket())); + assertTrue(snippetOutput.contains("Bucket: " + remoteBlob.getBucket())); + assertTrue(snippetOutput.contains("CacheControl: " + remoteBlob.getCacheControl())); + assertTrue(snippetOutput.contains("ComponentCount: " + remoteBlob.getComponentCount())); + assertTrue(snippetOutput.contains("ContentDisposition: " + remoteBlob.getContentDisposition())); + assertTrue(snippetOutput.contains("ContentEncoding: " + remoteBlob.getContentEncoding())); + assertTrue(snippetOutput.contains("ContentLanguage: " + remoteBlob.getContentLanguage())); + assertTrue(snippetOutput.contains("ContentType: " + remoteBlob.getContentType())); + assertTrue(snippetOutput.contains("Crc32c: " + remoteBlob.getCrc32c())); + assertTrue(snippetOutput.contains("ETag: " + remoteBlob.getEtag())); + assertTrue(snippetOutput.contains("Generation: " + remoteBlob.getGeneration())); + assertTrue(snippetOutput.contains("Id: " + remoteBlob.getBlobId())); + assertTrue(snippetOutput.contains("KmsKeyName: " + remoteBlob.getKmsKeyName())); + assertTrue(snippetOutput.contains("Md5Hash: " + remoteBlob.getMd5())); + assertTrue(snippetOutput.contains("MediaLink: " + remoteBlob.getMediaLink())); + assertTrue(snippetOutput.contains("Metageneration: " + remoteBlob.getMetageneration())); + assertTrue(snippetOutput.contains("Name: " + remoteBlob.getName())); + assertTrue(snippetOutput.contains("Size: " + remoteBlob.getSize())); + assertTrue(snippetOutput.contains("StorageClass: " + remoteBlob.getStorageClass())); + assertTrue(snippetOutput.contains("TimeCreated: " + new Date(remoteBlob.getCreateTime()))); + assertTrue(snippetOutput.contains("Last Metadata Update: " + new Date(remoteBlob.getUpdateTime()))); + assertTrue(snippetOutput.contains("temporaryHold: disabled")); + assertTrue(snippetOutput.contains("eventBasedHold: disabled")); + assertTrue(snippetOutput.contains("User metadata:")); + assertTrue(snippetOutput.contains("k=v")); + } + @Test public void testRequesterPays() throws Exception { Bucket bucket = storageSnippets.enableRequesterPays(BUCKET); @@ -461,4 +504,47 @@ public void testDefaultKMSKey() { // Remove default key storageSnippets.setDefaultKmsKey(BUCKET, null); } + + @Test + public void testBucketRetention() { + Bucket bucket = storageSnippets.setRetentionPolicy(BUCKET, RETENTION_PERIOD); + assertEquals(bucket.getRetentionPeriod(), RETENTION_PERIOD); + assertNotNull(bucket.getRetentionEffectiveTime()); + bucket = storageSnippets.getRetentionPolicy(BUCKET); + assertEquals(bucket.getRetentionPeriod(), RETENTION_PERIOD); + assertNotNull(bucket.getRetentionEffectiveTime()); + assertNull(bucket.retentionPolicyIsLocked()); + bucket = storageSnippets.enableDefaultEventBasedHold(BUCKET); + assertTrue(bucket.getDefaultEventBasedHold()); + bucket = storageSnippets.getDefaultEventBasedHold(BUCKET); + assertTrue(bucket.getDefaultEventBasedHold()); + String blobName = "test-create-empty-blob-retention-policy"; + Blob remoteBlob = bucket.create(blobName, BLOB_BYTE_CONTENT); + assertTrue(remoteBlob.getEventBasedHold()); + remoteBlob = storageSnippets.setEventBasedHold(BUCKET, blobName); + assertTrue(remoteBlob.getEventBasedHold()); + remoteBlob = storageSnippets.releaseEventBasedHold(BUCKET, blobName); + assertFalse(remoteBlob.getEventBasedHold()); + assertNotNull(remoteBlob.getRetentionExpirationTime()); + bucket = storageSnippets.removeRetentionPolicy(BUCKET); + assertNull(bucket.getRetentionPeriod()); + assertNull(bucket.getRetentionEffectiveTime()); + bucket = storageSnippets.disableDefaultEventBasedHold(BUCKET); + assertFalse(bucket.getDefaultEventBasedHold()); + remoteBlob = storageSnippets.setTemporaryHold(BUCKET, blobName); + assertTrue(remoteBlob.getTemporaryHold()); + remoteBlob = storageSnippets.releaseTemporaryHold(BUCKET, blobName); + assertFalse(remoteBlob.getTemporaryHold()); + } + + @Test + public void testLockRetentionPolicy() { + String tempBucket = RemoteStorageHelper.generateBucketName(); + Bucket bucket = storageSnippets.createBucket(tempBucket); + assertNotNull(bucket); + bucket = storageSnippets.setRetentionPolicy(tempBucket, RETENTION_PERIOD); + assertEquals(bucket.getRetentionPeriod(), RETENTION_PERIOD); + bucket = storageSnippets.lockRetentionPolicy(tempBucket); + assertTrue(bucket.retentionPolicyIsLocked()); + } }