From 84b2921449942245194a192f0e87637da6b978a3 Mon Sep 17 00:00:00 2001 From: Marco Ziccardi Date: Wed, 11 May 2016 16:23:17 +0200 Subject: [PATCH 1/2] Add tests and javadoc for Message and ByteArray --- .../main/java/com/google/cloud/ByteArray.java | 61 ++++---- .../java/com/google/cloud/ServiceOptions.java | 2 +- .../java/com/google/cloud/ByteArrayTest.java | 116 +++++++++++++++ .../java/com/google/cloud/pubsub/Message.java | 124 +++++++++++++--- .../com/google/cloud/pubsub/MessageTest.java | 133 ++++++++++++++++++ 5 files changed, 387 insertions(+), 49 deletions(-) create mode 100644 gcloud-java-core/src/test/java/com/google/cloud/ByteArrayTest.java create mode 100644 gcloud-java-pubsub/src/test/java/com/google/cloud/pubsub/MessageTest.java diff --git a/gcloud-java-core/src/main/java/com/google/cloud/ByteArray.java b/gcloud-java-core/src/main/java/com/google/cloud/ByteArray.java index f23fb0876b41..38c2ca5dc96b 100644 --- a/gcloud-java-core/src/main/java/com/google/cloud/ByteArray.java +++ b/gcloud-java-core/src/main/java/com/google/cloud/ByteArray.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 Google Inc. All Rights Reserved. + * Copyright 2016 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,8 +20,6 @@ import com.google.common.base.MoreObjects.ToStringHelper; import com.google.protobuf.ByteString; -import java.io.BufferedInputStream; -import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.Serializable; @@ -35,6 +33,7 @@ public class ByteArray implements Iterable, Serializable { private static final long serialVersionUID = -1908809133893782840L; + private final ByteString byteString; protected ByteArray(ByteString byteString) { @@ -75,43 +74,38 @@ public final boolean equals(Object obj) { } /** - * Returns the size of this blob. + * Returns the number of bytes in this {@code ByteArray}. */ public final int length() { return byteString.size(); } /** - * Returns a copy as byte array. + * Returns a copy of this {@code ByteArray} as an array of bytes. */ public final byte[] toByteArray() { return byteString.toByteArray(); } /** - * Returns the content as {@code UTF-8} string. + * Returns a copy of this {@code ByteArray} as an {@code UTF-8} string. */ public final String toStringUtf8() { return byteString.toStringUtf8(); } /** - * Returns a read-only {@link ByteBuffer} for this blob content. + * Returns the content of this {@code ByteArray} as a read-only {@link ByteBuffer}. */ public final ByteBuffer asReadOnlyByteBuffer() { return byteString.asReadOnlyByteBuffer(); } /** - * Returns an {@link InputStream} for this blob content. + * Returns an {@link InputStream} for this {@code ByteArray} content. */ public final InputStream asInputStream() { - final ByteBuffer byteBuffer = asReadOnlyByteBuffer(); - return new InputStream() { - @Override public int read() { - return !byteBuffer.hasRemaining() ? -1 : byteBuffer.get() & 0xFF; - } - }; + return byteString.newInput(); } protected ByteString byteString() { @@ -119,47 +113,52 @@ protected ByteString byteString() { } /** - * Copies bytes into a ByteBuffer. + * Copies the content of this {@code ByteArray} into an existing {@code ByteBuffer}. * * @throws java.nio.ReadOnlyBufferException if the target is read-only - * @throws java.nio.BufferOverflowException if the target's remaining() space is not large - * enough to hold the data + * @throws java.nio.BufferOverflowException if the target's {@link ByteBuffer#remaining()} space + * is not large enough to hold the data */ public final void copyTo(ByteBuffer target) { byteString.copyTo(target); } /** - * Copies bytes into a buffer. + * Copies the content of this {@code ByteArray} into an array of bytes. * - * @throws IndexOutOfBoundsException if an offset or size is negative or too large + * @throws IndexOutOfBoundsException if the target is not large enough to hold the data */ public final void copyTo(byte[] target) { byteString.copyTo(target, 0, 0, length()); } - public static final ByteArray copyFrom(byte[] bytes) { + /** + * Creates a {@code ByteArray} object given an array of bytes. The bytes are copied. + */ + public static ByteArray copyFrom(byte[] bytes) { return new ByteArray(ByteString.copyFrom(bytes)); } /** - * Copy the bytes using {@code UTF-8} decoding. + * Creates a {@code ByteArray} object given a string. The string is encoded in {@code UTF-8}. The + * bytes are copied. */ - public static final ByteArray copyFrom(String string) { + public static ByteArray copyFrom(String string) { return new ByteArray(ByteString.copyFrom(string, StandardCharsets.UTF_8)); } - public static final ByteArray copyFrom(ByteBuffer bytes) { + /** + * Creates a {@code ByteArray} object given a {@link ByteBuffer}. The bytes are copied. + */ + public static ByteArray copyFrom(ByteBuffer bytes) { return new ByteArray(ByteString.copyFrom(bytes)); } - public static final ByteArray copyFrom(InputStream input) throws IOException { - BufferedInputStream bufferedInput = new BufferedInputStream(input); - ByteArrayOutputStream bytes = new ByteArrayOutputStream(); - int value; - while ((value = bufferedInput.read()) != -1) { - bytes.write(value); - } - return copyFrom(bytes.toByteArray()); + /** + * Creates a {@code ByteArray} object given an {@link InputStream}. The stream is read into the + * created object. + */ + public static ByteArray copyFrom(InputStream input) throws IOException { + return new ByteArray(ByteString.readFrom(input)); } } diff --git a/gcloud-java-core/src/main/java/com/google/cloud/ServiceOptions.java b/gcloud-java-core/src/main/java/com/google/cloud/ServiceOptions.java index e08d0cd9d155..194f61b45c42 100644 --- a/gcloud-java-core/src/main/java/com/google/cloud/ServiceOptions.java +++ b/gcloud-java-core/src/main/java/com/google/cloud/ServiceOptions.java @@ -26,9 +26,9 @@ import com.google.api.client.http.HttpTransport; import com.google.api.client.http.javanet.NetHttpTransport; import com.google.auth.http.HttpCredentialsAdapter; +import com.google.cloud.spi.ServiceRpcFactory; import com.google.common.collect.Iterables; import com.google.common.io.Files; -import com.google.cloud.spi.ServiceRpcFactory; import org.json.JSONException; import org.json.JSONObject; diff --git a/gcloud-java-core/src/test/java/com/google/cloud/ByteArrayTest.java b/gcloud-java-core/src/test/java/com/google/cloud/ByteArrayTest.java new file mode 100644 index 000000000000..0adf2fe8be79 --- /dev/null +++ b/gcloud-java-core/src/test/java/com/google/cloud/ByteArrayTest.java @@ -0,0 +1,116 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; + +import com.google.common.io.ByteStreams; +import com.google.protobuf.ByteString; + +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; + +public class ByteArrayTest { + + private static final String STRING_CONTENT = "Hello, ByteArray!"; + private static final byte[] BYTES_CONTENT = STRING_CONTENT.getBytes(StandardCharsets.UTF_8); + private static final ByteBuffer BYTE_BUFFER_CONTENT = ByteBuffer.wrap(BYTES_CONTENT); + private static final InputStream STREAM_CONTENT = new ByteArrayInputStream(BYTES_CONTENT); + private static final ByteArray STRING_ARRAY = ByteArray.copyFrom(STRING_CONTENT); + private static final ByteArray BYTES_ARRAY = ByteArray.copyFrom(BYTES_CONTENT); + private static final ByteArray BYTE_BUFFER_ARRAY = ByteArray.copyFrom(BYTE_BUFFER_CONTENT); + private static final ByteArray ARRAY = new ByteArray(ByteString.copyFrom(BYTES_CONTENT)); + + private static ByteArray streamArray; + + @BeforeClass + public static void beforeClass() throws IOException { + streamArray = ByteArray.copyFrom(STREAM_CONTENT); + BYTE_BUFFER_CONTENT.flip(); + } + + @Test + public void testCopyFromString() throws IOException { + assertEquals(STRING_CONTENT, STRING_ARRAY.toStringUtf8()); + assertArrayEquals(BYTES_CONTENT, STRING_ARRAY.toByteArray()); + assertEquals(BYTE_BUFFER_CONTENT.asReadOnlyBuffer(), STRING_ARRAY.asReadOnlyByteBuffer()); + assertArrayEquals(BYTES_CONTENT, ByteStreams.toByteArray(STRING_ARRAY.asInputStream())); + } + + @Test + public void testCopyFromByteArray() throws IOException { + assertEquals(STRING_CONTENT, BYTES_ARRAY.toStringUtf8()); + assertArrayEquals(BYTES_CONTENT, BYTES_ARRAY.toByteArray()); + assertEquals(BYTE_BUFFER_CONTENT.asReadOnlyBuffer(), BYTES_ARRAY.asReadOnlyByteBuffer()); + assertArrayEquals(BYTES_CONTENT, ByteStreams.toByteArray(BYTES_ARRAY.asInputStream())); + } + + @Test + public void testCopyFromByteBuffer() throws IOException { + assertEquals(STRING_CONTENT, BYTE_BUFFER_ARRAY.toStringUtf8()); + assertArrayEquals(BYTES_CONTENT, BYTE_BUFFER_ARRAY.toByteArray()); + assertEquals(BYTE_BUFFER_CONTENT.asReadOnlyBuffer(), BYTE_BUFFER_ARRAY.asReadOnlyByteBuffer()); + assertArrayEquals(BYTES_CONTENT, ByteStreams.toByteArray(BYTE_BUFFER_ARRAY.asInputStream())); + } + + @Test + public void testCopyFromStream() throws IOException { + assertEquals(STRING_CONTENT, streamArray.toStringUtf8()); + assertArrayEquals(BYTES_CONTENT, streamArray.toByteArray()); + assertEquals(BYTE_BUFFER_CONTENT.asReadOnlyBuffer(), streamArray.asReadOnlyByteBuffer()); + assertArrayEquals(BYTES_CONTENT, ByteStreams.toByteArray(streamArray.asInputStream())); + } + + @Test + public void testLength() { + assertEquals(BYTES_CONTENT.length, ARRAY.length()); + } + + @Test + public void testToStringUtf8() { + assertEquals(STRING_CONTENT, ARRAY.toStringUtf8()); + } + + @Test + public void testToByteArray() { + assertArrayEquals(BYTES_CONTENT, ARRAY.toByteArray()); + } + + @Test + public void testAsReadOnlyByteBuffer() { + assertEquals(BYTE_BUFFER_CONTENT.asReadOnlyBuffer(), ARRAY.asReadOnlyByteBuffer()); + } + + @Test + public void testAsInputStream() throws IOException { + assertArrayEquals(BYTES_CONTENT, ByteStreams.toByteArray(ARRAY.asInputStream())); + } + + @Test + public void testHashCode() { + assertEquals(STRING_ARRAY.hashCode(), BYTES_ARRAY.hashCode()); + assertEquals(BYTES_ARRAY.hashCode(), BYTE_BUFFER_ARRAY.hashCode()); + assertEquals(BYTE_BUFFER_ARRAY.hashCode(), streamArray.hashCode()); + } +} diff --git a/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/Message.java b/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/Message.java index 830f9115a41d..c8d5ec7500da 100644 --- a/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/Message.java +++ b/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/Message.java @@ -17,6 +17,7 @@ import static com.google.common.base.Preconditions.checkNotNull; import com.google.cloud.ByteArray; +import com.google.common.base.Function; import com.google.common.base.MoreObjects; import com.google.common.collect.ImmutableMap; import com.google.protobuf.ByteString; @@ -29,10 +30,27 @@ import java.util.Objects; /** - * Pub/Sub message. + * A Google Cloud Pub/Sub message. A message is the combination of data and (optional) attributes + * that a publisher sends to a topic and is eventually delivered to subscribers. + * + *

Message attributes are key-value pairs that a publisher can define for a message. For example, + * a key {@code iana.org/language_tag} and value {@code en} could be added to messages to mark them + * as readable by an English-speaking subscriber. + * + *

To be published a message must have a non-empty payload, or at least one attribute. + * + * @see Pub/Sub Data Model */ public class Message implements Serializable { + static final Function TO_PB_FUNCTION = + new Function() { + @Override + public PubsubMessage apply(Message message) { + return message.toPb(); + } + }; + private static final long serialVersionUID = -1436515787233340634L; private static final long NANOS_PER_MILLISECOND = 1000000; private static final long MILLIS_PER_SECOND = 1000; @@ -61,32 +79,57 @@ protected ByteString byteString() { } /** - * Builder for Message. + * Builder for {@code Message} objects. */ public abstract static class Builder { abstract Builder id(String id); + /** + * Sets the message payload to the provided string. The string is enconded {@code UTF-8}. + */ public abstract Builder payload(String payload); + /** + * Sets the message payload to the provided {@link ByteArray}. + */ public abstract Builder payload(ByteArray payload); + /** + * Sets the message attributes to the provided map. Message attributes are key-value pairs that + * a publisher can define for a message. For example, a key {@code iana.org/language_tag} and + * value {@code en} could be added to messages to mark them as readable by an English-speaking + * subscriber. + */ public abstract Builder attributes(Map attributes); + /** + * Adds a new attribute to the message attributes. If an attribute with name {@code name} was + * already set, its value is updated. + */ public abstract Builder addAttribute(String name, String value); + /** + * Removes an attribute give its name from the message attributes. + */ public abstract Builder removeAttribute(String name); + /** + * Clears all message attributes. + */ public abstract Builder clearAttributes(); abstract Builder publishTime(long publishTime); + /** + * Creates a topic object. + */ public abstract Message build(); } static final class BuilderImpl extends Builder { - private String id = ""; + private String id; private ByteArray payload; private Map attributes = new HashMap<>(); private Long publishTime; @@ -160,35 +203,53 @@ public Message build() { publishTime = builder.publishTime; } + /** + * Returns the time in milliseconds at which the message was published. This value is set by the + * server when it receives the publish call. If not set, this method returns {@code null}. + */ public Long publishTime() { return publishTime; } + /** + * Returns the message attributes. Message attributes are key-value pairs that a publisher can + * define for a message. For example, a key {@code iana.org/language_tag} and value {@code en} + * could be added to messages to mark them as readable by an English-speaking subscriber. + */ public Map attributes() { return attributes; } + /** + * Returns the id of this message, set by the server when the message is published. The id is + * guaranteed to be unique within the topic. This value may be read by a subscriber that receives + * a Pub/Sub message via a pull call or a push delivery. If not set, this method returns + * {@code null}. + */ public String id() { return id; } + /** + * Returns the message payload as a string, decoded using {@code UTF-8}. + */ public String payloadAsString() { return payload.toStringUtf8(); } + /** + * Returns the message payload. + */ public ByteArray payload() { return payload; } @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - return Objects.equals(toPb(), ((Message) o).toPb()); + public boolean equals(Object obj) { + return obj == this + || obj != null + && obj.getClass().equals(Message.class) + && Objects.equals(toPb(), ((Message) obj).toPb()); } @Override @@ -212,10 +273,12 @@ PubsubMessage toPb() { builder.setMessageId(id); } builder.setData(payload.byteString()); - builder.getAttributes().putAll(attributes); + builder.putAllAttributes(attributes); Timestamp.Builder tsBuilder = Timestamp.newBuilder(); - tsBuilder.setSeconds(publishTime / MILLIS_PER_SECOND); - tsBuilder.setNanos((int) (publishTime % MILLIS_PER_SECOND * NANOS_PER_MILLISECOND)); + if (publishTime != null) { + tsBuilder.setSeconds(publishTime / MILLIS_PER_SECOND); + tsBuilder.setNanos((int) (publishTime % MILLIS_PER_SECOND * NANOS_PER_MILLISECOND)); + } builder.setPublishTime(tsBuilder); return builder.build(); } @@ -224,28 +287,55 @@ static Message fromPb(PubsubMessage messagePb) { Builder builder = builder(new InternalByteArray(messagePb.getData())); if (messagePb.hasPublishTime()) { Timestamp ts = messagePb.getPublishTime(); - builder.publishTime( - ts.getSeconds() * MILLIS_PER_SECOND + ts.getNanos() / NANOS_PER_MILLISECOND); + Long millis = ts.getSeconds() * MILLIS_PER_SECOND + ts.getNanos() / NANOS_PER_MILLISECOND; + if (millis != 0) { + builder.publishTime(millis); + } + } + if (!Objects.equals(messagePb.getMessageId(), "")) { + builder.id(messagePb.getMessageId()); } - builder.id(messagePb.getMessageId()); for (Map.Entry entry : messagePb.getAttributes().entrySet()) { builder.addAttribute(entry.getKey(), entry.getValue()); } return builder.build(); } + /** + * Returns a builder for the message object. + */ public Builder toBuilder() { return new BuilderImpl(this); } + /** + * Creates a {@code Message} object given the payload as a string. The string is enconded using + * {@code UTF-8}. + */ public static Message of(String payload) { return builder(payload).build(); } + /** + * Creates a {@code Message} object given the payload as a {@link ByteArray}. To be published a + * message must have a non-empty payload. + */ + public static Message of(ByteArray payload) { + return builder(payload).build(); + } + + /** + * Creates a builder for {@code Message} objects given the payload as a string. The string is + * enconded using {@code UTF-8}. To be published a message must have a non-empty payload. + */ public static Builder builder(String payload) { return new BuilderImpl().payload(payload); } + /** + * Creates a builder for {@code Message} objects given the payload as a {@link ByteArray}. To be + * published a message must have a non-empty payload, or at least one attribute. + */ public static Builder builder(ByteArray payload) { return new BuilderImpl().payload(payload); } diff --git a/gcloud-java-pubsub/src/test/java/com/google/cloud/pubsub/MessageTest.java b/gcloud-java-pubsub/src/test/java/com/google/cloud/pubsub/MessageTest.java new file mode 100644 index 000000000000..3b94bae0d958 --- /dev/null +++ b/gcloud-java-pubsub/src/test/java/com/google/cloud/pubsub/MessageTest.java @@ -0,0 +1,133 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.pubsub; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import com.google.cloud.ByteArray; +import com.google.common.collect.ImmutableMap; + +import org.junit.Test; + +import java.nio.charset.StandardCharsets; +import java.util.Map; + +public class MessageTest { + + private static final String MESSAGE_ID = "messageId"; + private static final String PAYLOAD_STRING = "payload"; + private static final ByteArray PAYLOAD = + ByteArray.copyFrom("payload".getBytes(StandardCharsets.UTF_8)); + private static final Map ATTRIBUTES = + ImmutableMap.of("key1", "value1", "key2", "value2"); + private static final Long PUBLISH_TIME = 42L; + private static final Message MESSAGE_STRING = Message.builder(PAYLOAD_STRING) + .id(MESSAGE_ID) + .attributes(ATTRIBUTES) + .publishTime(PUBLISH_TIME) + .build(); + private static final Message MESSAGE = Message.builder(PAYLOAD) + .id(MESSAGE_ID) + .attributes(ATTRIBUTES) + .publishTime(PUBLISH_TIME) + .build(); + + @Test + public void testToBuilder() { + compareMessage(MESSAGE, MESSAGE.toBuilder().build()); + Message message = MESSAGE.toBuilder() + .payload("newPayload") + .clearAttributes() + .addAttribute("key1", "value1") + .build(); + assertEquals("newPayload", message.payloadAsString()); + assertEquals(ImmutableMap.of("key1", "value1"), message.attributes()); + message = MESSAGE.toBuilder().payload(PAYLOAD_STRING).attributes(ATTRIBUTES).build(); + compareMessage(MESSAGE, message); + } + + @Test + public void testBuilder() { + assertEquals(MESSAGE_ID, MESSAGE.id()); + assertEquals(PAYLOAD, MESSAGE.payload()); + assertEquals(PAYLOAD_STRING, MESSAGE.payloadAsString()); + assertEquals(ATTRIBUTES, MESSAGE.attributes()); + assertEquals(PUBLISH_TIME, MESSAGE.publishTime()); + assertEquals(MESSAGE_ID, MESSAGE_STRING.id()); + assertEquals(PAYLOAD, MESSAGE_STRING.payload()); + assertEquals(PAYLOAD_STRING, MESSAGE_STRING.payloadAsString()); + assertEquals(ATTRIBUTES, MESSAGE_STRING.attributes()); + assertEquals(PUBLISH_TIME, MESSAGE_STRING.publishTime()); + compareMessage(MESSAGE, MESSAGE_STRING); + Message message = Message.builder(PAYLOAD) + .id(MESSAGE_ID) + .attributes(ATTRIBUTES) + .clearAttributes() + .addAttribute("key1", "value1") + .addAttribute("key2", "value2") + .publishTime(PUBLISH_TIME) + .build(); + assertEquals(MESSAGE_ID, message.id()); + assertEquals(PAYLOAD, message.payload()); + assertEquals(PAYLOAD_STRING, message.payloadAsString()); + assertEquals(ATTRIBUTES, message.attributes()); + assertEquals(PUBLISH_TIME, message.publishTime()); + compareMessage(MESSAGE, message); + } + + @Test + public void testOf() { + Message message1 = Message.of(PAYLOAD_STRING); + assertNull(message1.id()); + assertEquals(PAYLOAD, message1.payload()); + assertEquals(PAYLOAD_STRING, message1.payloadAsString()); + assertEquals(ImmutableMap.of(), message1.attributes()); + assertNull(message1.publishTime()); + Message message2 = Message.of(PAYLOAD); + assertNull(message2.id()); + assertEquals(PAYLOAD, message2.payload()); + assertEquals(PAYLOAD_STRING, message2.payloadAsString()); + assertEquals(ImmutableMap.of(), message2.attributes()); + assertNull(message2.publishTime()); + compareMessage(message1 ,message2); + } + + @Test + public void testToAndFromPb() { + compareMessage(MESSAGE, Message.fromPb(MESSAGE.toPb())); + compareMessage(MESSAGE_STRING, Message.fromPb(MESSAGE_STRING.toPb())); + } + + @Test + public void testToAndFromPbIncomplete() { + Message message = Message.of(PAYLOAD_STRING); + compareMessage(message, Message.fromPb(message.toPb())); + message = Message.of(PAYLOAD); + compareMessage(message, Message.fromPb(message.toPb())); + } + + private void compareMessage(Message expected, Message value) { + assertEquals(expected, value); + assertEquals(expected.id(), value.id()); + assertEquals(expected.payload(), value.payload()); + assertEquals(expected.payloadAsString(), value.payloadAsString()); + assertEquals(expected.attributes(), value.attributes()); + assertEquals(expected.publishTime(), value.publishTime()); + assertEquals(expected.hashCode(), value.hashCode()); + } +} From 53f58ae2f3c31f7f55eee049e1462676908841bb Mon Sep 17 00:00:00 2001 From: Marco Ziccardi Date: Wed, 11 May 2016 18:14:20 +0200 Subject: [PATCH 2/2] Add final on static ByteArray methods back --- .../src/main/java/com/google/cloud/ByteArray.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/gcloud-java-core/src/main/java/com/google/cloud/ByteArray.java b/gcloud-java-core/src/main/java/com/google/cloud/ByteArray.java index 38c2ca5dc96b..6a8e3dddd0ce 100644 --- a/gcloud-java-core/src/main/java/com/google/cloud/ByteArray.java +++ b/gcloud-java-core/src/main/java/com/google/cloud/ByteArray.java @@ -135,7 +135,7 @@ public final void copyTo(byte[] target) { /** * Creates a {@code ByteArray} object given an array of bytes. The bytes are copied. */ - public static ByteArray copyFrom(byte[] bytes) { + public final static ByteArray copyFrom(byte[] bytes) { return new ByteArray(ByteString.copyFrom(bytes)); } @@ -143,14 +143,14 @@ public static ByteArray copyFrom(byte[] bytes) { * Creates a {@code ByteArray} object given a string. The string is encoded in {@code UTF-8}. The * bytes are copied. */ - public static ByteArray copyFrom(String string) { + public final static ByteArray copyFrom(String string) { return new ByteArray(ByteString.copyFrom(string, StandardCharsets.UTF_8)); } /** * Creates a {@code ByteArray} object given a {@link ByteBuffer}. The bytes are copied. */ - public static ByteArray copyFrom(ByteBuffer bytes) { + public final static ByteArray copyFrom(ByteBuffer bytes) { return new ByteArray(ByteString.copyFrom(bytes)); } @@ -158,7 +158,7 @@ public static ByteArray copyFrom(ByteBuffer bytes) { * Creates a {@code ByteArray} object given an {@link InputStream}. The stream is read into the * created object. */ - public static ByteArray copyFrom(InputStream input) throws IOException { + public final static ByteArray copyFrom(InputStream input) throws IOException { return new ByteArray(ByteString.readFrom(input)); } }