diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Blob.java b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Blob.java
index a4a817ead2df..bc75408997f4 100644
--- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Blob.java
+++ b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Blob.java
@@ -190,6 +190,16 @@ public Blob reload(BlobSourceOption... options) {
* made on the metadata generation of the current blob. If you want to update the information only
* if the current blob metadata are at their latest version use the {@code metagenerationMatch}
* option: {@code blob.update(newInfo, BlobTargetOption.metagenerationMatch())}.
+ *
+ * Original metadata are merged with metadata in the provided {@code blobInfo}. To replace
+ * metadata instead you first have to unset them. Unsetting metadata can be done by setting the
+ * provided {@code blobInfo}'s metadata to {@code null}.
+ *
+ *
+ * Example usage of replacing blob's metadata:
+ *
{@code blob.update(blob.info().toBuilder().metadata(null).build());}
+ * {@code blob.update(blob.info().toBuilder().metadata(newMetadata).build());}
+ *
*
* @param blobInfo new blob's information. Bucket and blob names must match the current ones
* @param options update options
@@ -306,8 +316,7 @@ public Storage storage() {
}
/**
- * Gets the requested blobs. If {@code infos.length == 0} an empty list is returned. If
- * {@code infos.length > 1} a batch request is used to fetch blobs.
+ * Gets the requested blobs. A batch request is used to fetch blobs.
*
* @param storage the storage service used to issue the request
* @param blobs the blobs to get
@@ -331,8 +340,12 @@ public Blob apply(BlobInfo f) {
}
/**
- * Updates the requested blobs. If {@code infos.length == 0} an empty list is returned. If
- * {@code infos.length > 1} a batch request is used to update blobs.
+ * Updates the requested blobs. A batch request is used to update blobs. Original metadata are
+ * merged with metadata in the provided {@code BlobInfo} objects. To replace metadata instead
+ * you first have to unset them. Unsetting metadata can be done by setting the provided
+ * {@code BlobInfo} objects metadata to {@code null}. See
+ * {@link #update(com.google.gcloud.storage.BlobInfo,
+ * com.google.gcloud.storage.Storage.BlobTargetOption...) } for a code example.
*
* @param storage the storage service used to issue the request
* @param infos the blobs to update
@@ -356,8 +369,7 @@ public Blob apply(BlobInfo f) {
}
/**
- * Deletes the requested blobs. If {@code infos.length == 0} an empty list is returned. If
- * {@code infos.length > 1} a batch request is used to delete blobs.
+ * Deletes the requested blobs. A batch request is used to delete blobs.
*
* @param storage the storage service used to issue the request
* @param blobs the blobs to delete
diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BlobInfo.java b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BlobInfo.java
index 8e6921bbc20d..ec3ef36708cb 100644
--- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BlobInfo.java
+++ b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BlobInfo.java
@@ -27,14 +27,19 @@
import com.google.common.base.Function;
import com.google.common.base.MoreObjects;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
import java.io.Serializable;
import java.math.BigInteger;
+import java.util.AbstractMap;
+import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.Set;
/**
* Google Storage object metadata.
@@ -81,6 +86,17 @@ public StorageObject apply(BlobInfo blobInfo) {
private final String contentLanguage;
private final Integer componentCount;
+ /**
+ * This class is meant for internal use only. Users are discouraged from using this class.
+ */
+ public static final class ImmutableEmptyMap extends AbstractMap {
+
+ @Override
+ public Set> entrySet() {
+ return ImmutableSet.of();
+ }
+ }
+
public static final class Builder {
private BlobId blobId;
@@ -99,7 +115,7 @@ public static final class Builder {
private String md5;
private String crc32c;
private String mediaLink;
- private ImmutableMap metadata;
+ private Map metadata;
private Long generation;
private Long metageneration;
private Long deleteTime;
@@ -188,7 +204,8 @@ Builder mediaLink(String mediaLink) {
}
public Builder metadata(Map metadata) {
- this.metadata = metadata != null ? ImmutableMap.copyOf(metadata) : null;
+ this.metadata = metadata != null ?
+ new HashMap(metadata) : Data.