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;
@@ -749,24 +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.
- */
- @GcpLaunchStage.Beta
+ /** Returns the Cloud KMS key used to encrypt the blob, if any. */
public String getKmsKeyName() {
return kmsKeyName;
}
/**
- * Returns a builder for the current blob.
+ * 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 Data.isNull(eventBasedHold) ? null : eventBasedHold;
+ }
+
+ /**
+ * 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 Data.isNull(temporaryHold) ? null : temporaryHold;
+ }
+
+ /**
+ * 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 Data.isNull(retentionExpirationTime) ? null : retentionExpirationTime;
+ }
+
+ /** Returns a builder for the current blob. */
public Builder toBuilder() {
return new BuilderImpl(this);
}
@@ -792,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));
@@ -829,15 +879,20 @@ 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) {
storageObject.setCustomerEncryption(customerEncryption.toPb());
}
+ if (retentionExpirationTime != null) {
+ storageObject.setRetentionExpirationTime(new DateTime(retentionExpirationTime));
+ }
storageObject.setKmsKeyName(kmsKeyName);
+ storageObject.setEventBasedHold(eventBasedHold);
+ storageObject.setTemporaryHold(temporaryHold);
storageObject.setMetadata(pbMetadata);
storageObject.setCacheControl(cacheControl);
storageObject.setContentEncoding(contentEncoding);
@@ -855,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);
}
@@ -950,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);
@@ -971,6 +1018,15 @@ public Acl apply(ObjectAccessControl objectAccessControl) {
if (storageObject.getKmsKeyName() != null) {
builder.setKmsKeyName(storageObject.getKmsKeyName());
}
+ if (storageObject.getEventBasedHold() != null) {
+ builder.setEventBasedHold(storageObject.getEventBasedHold());
+ }
+ if (storageObject.getTemporaryHold() != null) {
+ builder.setTemporaryHold(storageObject.getTemporaryHold());
+ }
+ if (storageObject.getRetentionExpirationTime() != null) {
+ builder.setRetentionExpirationTime(storageObject.getRetentionExpirationTime().getValue());
+ }
return builder.build();
}
}
diff --git a/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/Bucket.java b/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/Bucket.java
index 179e8a5cbfef..c06ed6f6958a 100644
--- a/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/Bucket.java
+++ b/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/Bucket.java
@@ -628,13 +628,36 @@ public Builder setLabels(Map labels) {
return this;
}
- @GcpLaunchStage.Beta
@Override
public Builder setDefaultKmsKeyName(String defaultKmsKeyName) {
infoBuilder.setDefaultKmsKeyName(defaultKmsKeyName);
return this;
}
+ @Override
+ public Builder setDefaultEventBasedHold(Boolean defaultEventBasedHold) {
+ infoBuilder.setDefaultEventBasedHold(defaultEventBasedHold);
+ return this;
+ }
+
+ @Override
+ Builder setRetentionEffectiveTime(Long retentionEffectiveTime) {
+ infoBuilder.setRetentionEffectiveTime(retentionEffectiveTime);
+ return this;
+ }
+
+ @Override
+ Builder setRetentionPolicyIsLocked(Boolean retentionIsLocked) {
+ infoBuilder.setRetentionPolicyIsLocked(retentionIsLocked);
+ return this;
+ }
+
+ @Override
+ public Builder setRetentionPeriod(Long retentionPeriod) {
+ infoBuilder.setRetentionPeriod(retentionPeriod);
+ return this;
+ }
+
@Override
public Bucket build() {
return new Bucket(storage, infoBuilder);
@@ -1111,6 +1134,29 @@ public List listDefaultAcls() {
return storage.listDefaultAcls(getName());
}
+ /**
+ * Locks bucket retention policy. Requires a local metageneration value in the request. Review example below.
+ *
+ * Accepts an optional userProject {@link BucketTargetOption} option which defines the project id
+ * to assign operational costs.
+ *
+ *
Warning: Once a retention policy is locked, it can't be unlocked, removed, or shortened.
+ *
+ *
Example of locking a retention policy on a bucket, only if its local metageneration value matches the bucket's
+ * service metageneration otherwise a {@link StorageException} is thrown.
+ *
{@code
+ * String bucketName = "my_unique_bucket";
+ * Bucket bucket = storage.get(bucketName, BucketGetOption.fields(BucketField.METAGENERATION));
+ * storage.lockRetentionPolicy(bucket, BucketTargetOption.metagenerationMatch());
+ * }
+ *
+ * @return a {@code Bucket} object of the locked bucket
+ * @throws StorageException upon failure
+ */
+ public Bucket lockRetentionPolicy(BucketTargetOption... options) {
+ return storage.lockRetentionPolicy(this, options);
+ }
+
/**
* Returns the bucket's {@code Storage} object used to issue requests.
*/
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 6e0b3e2c38e9..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 {
@@ -88,6 +88,10 @@ public com.google.api.services.storage.model.Bucket apply(BucketInfo bucketInfo)
private final StorageClass storageClass;
private final Map labels;
private final String defaultKmsKeyName;
+ private final Boolean defaultEventBasedHold;
+ private final Long retentionEffectiveTime;
+ private final Boolean retentionPolicyIsLocked;
+ private final Long retentionPeriod;
/**
* Base class for bucket's delete rules. Allows to configure automatic deletion of blobs and blobs
@@ -102,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) {
@@ -179,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);
@@ -218,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);
}
@@ -306,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);
@@ -324,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);
@@ -343,10 +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.
- *
- * GcpLaunchStage.Alpha
+ * 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);
@@ -362,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);
/**
@@ -376,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);
@@ -397,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);
@@ -406,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);
@@ -416,25 +414,31 @@ 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.
- */
- @GcpLaunchStage.Beta
+ /** 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. */
+ public abstract Builder setDefaultEventBasedHold(Boolean defaultEventBasedHold);
+
+ abstract Builder setRetentionEffectiveTime(Long retentionEffectiveTime);
+
+ abstract Builder setRetentionPolicyIsLocked(Boolean retentionPolicyIsLocked);
+
/**
- * Creates a {@code BucketInfo} object.
+ * 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. */
public abstract BucketInfo build();
}
@@ -459,6 +463,10 @@ static final class BuilderImpl extends Builder {
private List defaultAcl;
private Map labels;
private String defaultKmsKeyName;
+ private Boolean defaultEventBasedHold;
+ private Long retentionEffectiveTime;
+ private Boolean retentionPolicyIsLocked;
+ private Long retentionPeriod;
BuilderImpl(String name) {
this.name = name;
@@ -484,6 +492,10 @@ static final class BuilderImpl extends Builder {
labels = bucketInfo.labels;
requesterPays = bucketInfo.requesterPays;
defaultKmsKeyName = bucketInfo.defaultKmsKeyName;
+ defaultEventBasedHold = bucketInfo.defaultEventBasedHold;
+ retentionEffectiveTime = bucketInfo.retentionEffectiveTime;
+ retentionPolicyIsLocked = bucketInfo.retentionPolicyIsLocked;
+ retentionPeriod = bucketInfo.retentionPeriod;
}
@Override
@@ -516,7 +528,6 @@ public Builder setVersioningEnabled(Boolean enable) {
return this;
}
- /** GcpLaunchStage.Alpha */
@Override
public Builder setRequesterPays(Boolean enable) {
this.requesterPays = firstNonNull(enable, Data.nullOf(Boolean.class));
@@ -595,11 +606,37 @@ public Builder setLabels(Map labels) {
return this;
}
- @GcpLaunchStage.Beta
@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));
+ return this;
+ }
+
+ @Override
+ Builder setRetentionEffectiveTime(Long retentionEffectiveTime) {
+ this.retentionEffectiveTime =
+ firstNonNull(retentionEffectiveTime, Data.nullOf(Long.class));
+ return this;
+ }
+
+ @Override
+ Builder setRetentionPolicyIsLocked(Boolean retentionPolicyIsLocked) {
+ this.retentionPolicyIsLocked =
+ firstNonNull(retentionPolicyIsLocked, Data.nullOf(Boolean.class));
+ return this;
+ }
+
+ @Override
+ public Builder setRetentionPeriod(Long retentionPeriod) {
+ this.retentionPeriod = firstNonNull(retentionPeriod, Data.nullOf(Long.class));
return this;
}
@@ -630,49 +667,72 @@ public BucketInfo build() {
labels = builder.labels;
requesterPays = builder.requesterPays;
defaultKmsKeyName = builder.defaultKmsKeyName;
+ defaultEventBasedHold = builder.defaultEventBasedHold;
+ retentionEffectiveTime = builder.retentionEffectiveTime;
+ retentionPolicyIsLocked = builder.retentionPolicyIsLocked;
+ 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.
*
- * GcpLaunchStage.Alpha
+ *
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;
@@ -686,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;
}
@@ -711,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;
}
@@ -748,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;
@@ -775,24 +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.
- */
- @GcpLaunchStage.Beta
+ /** Returns the default Cloud KMS key to be applied to newly inserted objects in this bucket. */
public String getDefaultKmsKeyName() {
return defaultKmsKeyName;
}
/**
- * Returns a builder for the current 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 Data.isNull(defaultEventBasedHold) ? null : defaultEventBasedHold;
+ }
+
+ /**
+ * Returns the retention effective time a policy took effect if a retention policy is defined as a
+ * {@code Long}.
+ */
+ public Long getRetentionEffectiveTime() {
+ return retentionEffectiveTime;
+ }
+
+ /**
+ * 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 a builder for the current bucket. */
public Builder toBuilder() {
return new BuilderImpl(this);
}
@@ -806,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() {
@@ -839,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()));
@@ -874,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) {
@@ -888,19 +1003,36 @@ public Rule apply(DeleteRule deleteRule) {
if (defaultKmsKeyName != null) {
bucketPb.setEncryption(new Encryption().setDefaultKmsKeyName(defaultKmsKeyName));
}
+ if (defaultEventBasedHold != null) {
+ bucketPb.setDefaultEventBasedHold(defaultEventBasedHold);
+ }
+ 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);
+
+ }
+ }
+
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);
}
@@ -932,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()));
@@ -960,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());
@@ -976,9 +1115,26 @@ 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) {
+ builder.setDefaultEventBasedHold(bucketPb.getDefaultEventBasedHold());
+ }
+ Bucket.RetentionPolicy retentionPolicy = bucketPb.getRetentionPolicy();
+ if (retentionPolicy != null) {
+ if (retentionPolicy.getEffectiveTime() != null) {
+ builder.setRetentionEffectiveTime(retentionPolicy.getEffectiveTime().getValue());
+ }
+ if (retentionPolicy.getIsLocked() != null) {
+ builder.setRetentionPolicyIsLocked(retentionPolicy.getIsLocked());
+ }
+ if (retentionPolicy.getRetentionPeriod() != null) {
+ builder.setRetentionPeriod(retentionPolicy.getRetentionPeriod());
+ }
+ }
return builder.build();
}
}
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 78f9390a67e4..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
@@ -93,9 +93,10 @@ enum BucketField implements FieldSelector {
CORS("cors"),
STORAGE_CLASS("storageClass"),
ETAG("etag"),
- @GcpLaunchStage.Beta
ENCRYPTION("encryption"),
- BILLING("billing");
+ BILLING("billing"),
+ DEFAULT_EVENT_BASED_HOLD("defaultEventBasedHold"),
+ RETENTION_POLICY("retentionPolicy");
static final List extends FieldSelector> REQUIRED_FIELDS = ImmutableList.of(NAME);
@@ -136,8 +137,11 @@ enum BlobField implements FieldSelector {
SIZE("size"),
STORAGE_CLASS("storageClass"),
TIME_DELETED("timeDeleted"),
- @GcpLaunchStage.Beta
+ TIME_CREATED("timeCreated"),
KMS_KEY_NAME("kmsKeyName"),
+ EVENT_BASED_HOLD("eventBasedHold"),
+ TEMPORARY_HOLD("temporaryHold"),
+ RETENTION_EXPIRATION_TIME("retentionExpirationTime"),
UPDATED("updated");
static final List extends FieldSelector> REQUIRED_FIELDS = ImmutableList.of(BUCKET, NAME);
@@ -388,7 +392,6 @@ public static BlobTargetOption encryptionKey(String key) {
/**
* Returns an option to set a customer-managed key for server-side encryption of the blob.
*/
- @GcpLaunchStage.Beta
public static BlobTargetOption kmsKeyName(String kmsKeyName) {
return new BlobTargetOption(StorageRpc.Option.KMS_KEY_NAME, kmsKeyName);
}
@@ -550,7 +553,6 @@ public static BlobWriteOption encryptionKey(String key) {
*
* @param kmsKeyName the KMS key resource id
*/
- @GcpLaunchStage.Beta
public static BlobWriteOption kmsKeyName(String kmsKeyName) {
return new BlobWriteOption(Option.KMS_KEY_NAME, kmsKeyName);
}
@@ -1537,6 +1539,27 @@ public static Builder newBuilder() {
*/
Bucket get(String bucket, BucketGetOption... options);
+ /**
+ * Locks bucket retention policy. Requires a local metageneration value in the request. Review example below.
+ *
+ * Accepts an optional userProject {@link BucketTargetOption} option which defines the project id
+ * to assign operational costs.
+ *
+ *
Warning: Once a retention policy is locked, it can't be unlocked, removed, or shortened.
+ *
+ *
Example of locking a retention policy on a bucket, only if its local metageneration value matches the bucket's
+ * service metageneration otherwise a {@link StorageException} is thrown.
+ *
{@code
+ * String bucketName = "my_unique_bucket";
+ * Bucket bucket = storage.get(bucketName, BucketGetOption.fields(BucketField.METAGENERATION));
+ * storage.lockRetentionPolicy(bucket, BucketTargetOption.metagenerationMatch());
+ * }
+ *
+ * @return a {@code Bucket} object of the locked bucket
+ * @throws StorageException upon failure
+ */
+ Bucket lockRetentionPolicy(BucketInfo bucket, BucketTargetOption... options);
+
/**
* Returns the requested blob or {@code null} if not found.
*
diff --git a/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/StorageImpl.java b/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/StorageImpl.java
index e49445d13646..787388006571 100644
--- a/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/StorageImpl.java
+++ b/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/StorageImpl.java
@@ -983,6 +983,23 @@ public Boolean apply(String permission) {
}
}
+ @Override
+ public Bucket lockRetentionPolicy(BucketInfo bucketInfo, BucketTargetOption... options) {
+ final com.google.api.services.storage.model.Bucket bucketPb = bucketInfo.toPb();
+ final Map optionsMap = optionMap(bucketInfo, options);
+ try {
+ return Bucket.fromPb(this, runWithRetries(
+ new Callable() {
+ @Override
+ public com.google.api.services.storage.model.Bucket call() {
+ return storageRpc.lockRetentionPolicy(bucketPb, optionsMap);
+ }
+ }, getOptions().getRetrySettings(), EXCEPTION_HANDLER, getOptions().getClock()));
+ } catch (RetryHelperException e) {
+ throw StorageException.translateAndThrow(e);
+ }
+ }
+
@Override
public ServiceAccount getServiceAccount(final String projectId) {
try {
diff --git a/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/spi/v1/HttpStorageRpc.java b/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/spi/v1/HttpStorageRpc.java
index c6b2e668742d..19c9324518d7 100644
--- a/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/spi/v1/HttpStorageRpc.java
+++ b/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/spi/v1/HttpStorageRpc.java
@@ -1208,6 +1208,23 @@ public Notification createNotification(String bucket, Notification notification)
}
}
+ @Override
+ public Bucket lockRetentionPolicy(Bucket bucket, Map