Skip to content

Commit e78f782

Browse files
committed
Add javadoc and tests for functional ReceivedMessage class
1 parent 6df07ba commit e78f782

File tree

3 files changed

+276
-12
lines changed

3 files changed

+276
-12
lines changed

gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/Message.java

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
* a key {@code iana.org/language_tag} and value {@code en} could be added to messages to mark them
3838
* as readable by an English-speaking subscriber.
3939
*
40-
* <p>To be published a message must have a non-empty payload, or at least one attribute.
40+
* <p>To be published, a message must have a non-empty payload, or at least one attribute.
4141
*
4242
* @see <a href="https://cloud.google.com/pubsub/overview#data_model">Pub/Sub Data Model</a>
4343
*/
@@ -64,11 +64,11 @@ private static final class InternalByteArray extends ByteArray {
6464

6565
private static final long serialVersionUID = -3330181485911805428L;
6666

67-
protected InternalByteArray(ByteString byteString) {
67+
InternalByteArray(ByteString byteString) {
6868
super(byteString);
6969
}
7070

71-
protected InternalByteArray(ByteArray byteArray) {
71+
InternalByteArray(ByteArray byteArray) {
7272
super(byteArray);
7373
}
7474

@@ -244,17 +244,24 @@ public ByteArray payload() {
244244
return payload;
245245
}
246246

247+
final boolean baseEquals(Message message) {
248+
return Objects.equals(id, message.id)
249+
&& Objects.equals(payload, message.payload)
250+
&& Objects.equals(attributes, message.attributes)
251+
&& Objects.equals(publishTime, message.publishTime);
252+
}
253+
247254
@Override
248255
public boolean equals(Object obj) {
249256
return obj == this
250257
|| obj != null
251258
&& obj.getClass().equals(Message.class)
252-
&& Objects.equals(toPb(), ((Message) obj).toPb());
259+
&& baseEquals((Message) obj);
253260
}
254261

255262
@Override
256263
public int hashCode() {
257-
return Objects.hash(serialVersionUID, id, payload, attributes, publishTime);
264+
return Objects.hash(id, payload, attributes, publishTime);
258265
}
259266

260267
@Override

gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/ReceivedMessage.java

Lines changed: 77 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,16 @@
2727
import java.util.concurrent.Future;
2828
import java.util.concurrent.TimeUnit;
2929

30-
public class ReceivedMessage extends Message {
30+
/**
31+
* A Google Cloud Pub/Sub received message. A received message has all the information in
32+
* {@link Message} as well as the acknowledge id. The ack id can be used to acknowledge the received
33+
* message.
34+
*
35+
* <p>{@code ReceivedMessage} also adds a layer of service-related functionality over
36+
* {@link Message} that help manage received messages (see {@link #ack()}, {@link #nack()} and
37+
* {@link #modifyAckDeadline(int, TimeUnit)}).
38+
*/
39+
public final class ReceivedMessage extends Message {
3140

3241
private static final long serialVersionUID = -4178477763916251733L;
3342

@@ -124,48 +133,109 @@ public int hashCode() {
124133

125134
@Override
126135
public boolean equals(Object obj) {
127-
if (this == obj) {
136+
if (obj == this) {
128137
return true;
129138
}
130-
if (obj == null || getClass() != obj.getClass()) {
139+
if (obj == null || !obj.getClass().equals(ReceivedMessage.class)) {
131140
return false;
132141
}
133142
ReceivedMessage other = (ReceivedMessage) obj;
134-
return Objects.equals(toPb(), other.toPb()) && Objects.equals(options, other.options);
143+
return baseEquals(other) && Objects.equals(options, other.options);
135144
}
136145

137-
public PubSub pubSub() {
146+
/**
147+
* Returns the received message's {@code PubSub} object used to issue requests.
148+
*/
149+
public PubSub pubsub() {
138150
return pubsub;
139151
}
140152

153+
/**
154+
* Returns the name of the subscription this message was received from.
155+
*/
141156
public String subscription() {
142157
return subscription;
143158
}
144159

160+
/**
161+
* Returns the acknowledge id of the message. The ack id can be used to acknowledge the received
162+
* message.
163+
*/
145164
public String ackId() {
146165
return ackId;
147166
}
148167

168+
/**
169+
* Acknowledges the current message.
170+
*
171+
* @throws PubSubException upon failure, or if the subscription was not found
172+
*/
149173
public void ack() {
150174
pubsub.ack(subscription, ackId);
151175
}
152176

177+
/**
178+
* Sends a request to acknowledge the current message. The method returns a {@code Future} object
179+
* that can be used to wait for the acknowledge operation to be completed.
180+
*
181+
* @throws PubSubException upon failure, or if the subscription was not found
182+
*/
153183
public Future<Void> ackAsync() {
154184
return pubsub.ackAsync(subscription, ackId);
155185
}
156186

187+
/**
188+
* "Nacks" the current message. This method corresponds to calling
189+
* {@link #modifyAckDeadline(int, TimeUnit)} with a deadline of 0.
190+
*
191+
* @throws PubSubException upon failure, or if the subscription was not found
192+
*/
157193
public void nack() {
158194
pubsub.nack(subscription, ackId);
159195
}
160196

197+
/**
198+
* Sends a request to "nack" the current message. This method corresponds to calling
199+
* {@link #modifyAckDeadlineAsync(int, TimeUnit)} with a deadline of 0. The method returns a
200+
* {@code Future} object that can be used to wait for the "nack" operation to be completed.
201+
*
202+
* @throws PubSubException upon failure, or if the subscription was not found
203+
*/
161204
public Future<Void> nackAsync() {
162205
return pubsub.nackAsync(subscription, ackId);
163206
}
164207

208+
/**
209+
* Modifies the acknowledge deadline of the current message. {@code deadline} must be &gt;= 0 and
210+
* is the new deadline with respect to the time the modify request was received by the Pub/Sub
211+
* service. For example, if {@code deadline} is 10 and {@code unit} is {@link TimeUnit#SECONDS},
212+
* the new ack deadline will expire 10 seconds after the modify request was received by the
213+
* service. Specifying 0 may be used to make the message available for another pull request
214+
* (corresponds to calling {@link #nack()}.
215+
*
216+
* @param deadline the new deadline, relative to the time the modify request is received by the
217+
* Pub/Sub service
218+
* @param unit {@code deadline} time unit
219+
* @throws PubSubException upon failure, or if the subscription was not found
220+
*/
165221
public void modifyAckDeadline(int deadline, TimeUnit unit) {
166222
pubsub.modifyAckDeadline(subscription, deadline, unit, ackId);
167223
}
168224

225+
/**
226+
* Sends a request to modify the acknowledge deadline of the given messages. {@code deadline}
227+
* must be &gt;= 0 and is the new deadline with respect to the time the modify request was
228+
* received by the Pub/Sub service. For example, if {@code deadline} is 10 and {@code unit} is
229+
* {@link TimeUnit#SECONDS}, the new ack deadline will expire 10 seconds after the modify request
230+
* was received by the service. Specifying 0 may be used to make the message available for another
231+
* pull request (corresponds to calling {@link #nackAsync()}. The method returns a {@code Future}
232+
* object that can be used to wait for the modify operation to be completed.
233+
*
234+
* @param deadline the new deadline, relative to the time the modify request is received by the
235+
* Pub/Sub service
236+
* @param unit {@code deadline} time unit
237+
* @throws PubSubException upon failure, or if the subscription was not found
238+
*/
169239
public Future<Void> modifyAckDeadlineAsync(int deadline, TimeUnit unit) {
170240
return pubsub.modifyAckDeadlineAsync(subscription, deadline, unit, ackId);
171241
}
@@ -175,10 +245,10 @@ private void readObject(ObjectInputStream input) throws IOException, ClassNotFou
175245
this.pubsub = options.service();
176246
}
177247

178-
static ReceivedMessage fromPb(PubSub storage, String subscription,
248+
static ReceivedMessage fromPb(PubSub pubsub, String subscription,
179249
com.google.pubsub.v1.ReceivedMessage msgPb) {
180250
Message message = fromPb(msgPb.getMessage());
181251
String ackId = msgPb.getAckId();
182-
return new Builder(subscription, ackId, storage, new BuilderImpl(message)).build();
252+
return new Builder(subscription, ackId, pubsub, new BuilderImpl(message)).build();
183253
}
184254
}
Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
/*
2+
* Copyright 2016 Google Inc. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.cloud.pubsub;
18+
19+
import static org.easymock.EasyMock.createMock;
20+
import static org.easymock.EasyMock.createStrictMock;
21+
import static org.easymock.EasyMock.expect;
22+
import static org.easymock.EasyMock.replay;
23+
import static org.easymock.EasyMock.verify;
24+
import static org.junit.Assert.assertArrayEquals;
25+
import static org.junit.Assert.assertEquals;
26+
import static org.junit.Assert.assertNull;
27+
import static org.junit.Assert.assertSame;
28+
29+
import com.google.api.client.util.Charsets;
30+
import com.google.cloud.ByteArray;
31+
import com.google.common.collect.ImmutableMap;
32+
import com.google.common.util.concurrent.Futures;
33+
34+
import org.easymock.EasyMock;
35+
import org.junit.After;
36+
import org.junit.Test;
37+
38+
import java.nio.charset.StandardCharsets;
39+
import java.util.Map;
40+
import java.util.concurrent.ExecutionException;
41+
import java.util.concurrent.TimeUnit;
42+
43+
public class ReceivedMessageTest {
44+
45+
private static final String SUBSCRIPTION = "subscription";
46+
private static final String ACK_ID = "ackId";
47+
private static final String MESSAGE_ID = "messageId";
48+
private static final String PAYLOAD_STRING = "payload";
49+
private static final ByteArray PAYLOAD =
50+
ByteArray.copyFrom("payload".getBytes(StandardCharsets.UTF_8));
51+
private static final Map<String, String> ATTRIBUTES =
52+
ImmutableMap.of("key1", "value1", "key2", "value2");
53+
private static final Long PUBLISH_TIME = 42L;
54+
private static final Message MESSAGE = Message.builder(PAYLOAD)
55+
.id(MESSAGE_ID)
56+
.attributes(ATTRIBUTES)
57+
.publishTime(PUBLISH_TIME)
58+
.build();
59+
private static final com.google.pubsub.v1.ReceivedMessage RECEIVED_MESSAGE_PB =
60+
com.google.pubsub.v1.ReceivedMessage.newBuilder()
61+
.setMessage(MESSAGE.toPb())
62+
.setAckId(ACK_ID)
63+
.build();
64+
65+
private final PubSub serviceMockReturnsOptions = createStrictMock(PubSub.class);
66+
private final PubSubOptions mockOptions = createMock(PubSubOptions.class);
67+
private PubSub pubsub;
68+
private ReceivedMessage expectedMessage;
69+
private ReceivedMessage message;
70+
71+
private void initializeExpectedMessage(int optionsCalls) {
72+
expect(serviceMockReturnsOptions.options()).andReturn(mockOptions).times(optionsCalls);
73+
replay(serviceMockReturnsOptions);
74+
pubsub = createStrictMock(PubSub.class);
75+
expectedMessage =
76+
ReceivedMessage.fromPb(serviceMockReturnsOptions, SUBSCRIPTION, RECEIVED_MESSAGE_PB);
77+
}
78+
79+
private void initializeMessage() {
80+
message = ReceivedMessage.fromPb(pubsub, SUBSCRIPTION, RECEIVED_MESSAGE_PB);
81+
}
82+
83+
@After
84+
public void tearDown() throws Exception {
85+
verify(pubsub, serviceMockReturnsOptions);
86+
}
87+
88+
@Test
89+
public void testBuilder() {
90+
initializeExpectedMessage(3);
91+
replay(pubsub);
92+
Map<String, String> attributes = ImmutableMap.of("newKey1", "newVal1");
93+
ReceivedMessage builtMessage = expectedMessage.toBuilder()
94+
.payload("newPayload")
95+
.id("newMessageId")
96+
.attributes(attributes)
97+
.publishTime(PUBLISH_TIME + 1)
98+
.build();
99+
assertSame(serviceMockReturnsOptions, builtMessage.pubsub());
100+
assertEquals(SUBSCRIPTION, builtMessage.subscription());
101+
assertEquals(ACK_ID, builtMessage.ackId());
102+
assertEquals("newMessageId", builtMessage.id());
103+
assertArrayEquals("newPayload".getBytes(Charsets.UTF_8), builtMessage.payload().toByteArray());
104+
assertEquals("newPayload", builtMessage.payloadAsString());
105+
assertEquals(attributes, builtMessage.attributes());
106+
assertEquals(PUBLISH_TIME + 1, (long) builtMessage.publishTime());
107+
builtMessage = builtMessage.toBuilder()
108+
.payload(PAYLOAD)
109+
.id(MESSAGE_ID)
110+
.clearAttributes()
111+
.addAttribute("key1", "value1")
112+
.addAttribute("key2", "value2")
113+
.publishTime(PUBLISH_TIME)
114+
.build();
115+
assertSame(serviceMockReturnsOptions, builtMessage.pubsub());
116+
assertEquals(MESSAGE_ID, builtMessage.id());
117+
assertEquals(PAYLOAD, builtMessage.payload());
118+
assertEquals(PAYLOAD_STRING, builtMessage.payloadAsString());
119+
assertEquals(ATTRIBUTES, builtMessage.attributes());
120+
assertEquals(PUBLISH_TIME, builtMessage.publishTime());
121+
compareReceivedMessage(expectedMessage, builtMessage);
122+
}
123+
124+
@Test
125+
public void testToBuilder() {
126+
initializeExpectedMessage(2);
127+
replay(pubsub);
128+
compareReceivedMessage(expectedMessage, expectedMessage.toBuilder().build());
129+
}
130+
131+
@Test
132+
public void testAck() {
133+
initializeExpectedMessage(1);
134+
expect(pubsub.options()).andReturn(mockOptions);
135+
pubsub.ack(SUBSCRIPTION, ACK_ID);
136+
EasyMock.expectLastCall();
137+
replay(pubsub);
138+
initializeMessage();
139+
message.ack();
140+
}
141+
142+
@Test
143+
public void testAckAsync() throws ExecutionException, InterruptedException {
144+
initializeExpectedMessage(1);
145+
expect(pubsub.options()).andReturn(mockOptions);
146+
expect(pubsub.ackAsync(SUBSCRIPTION, ACK_ID)).andReturn(Futures.<Void>immediateFuture(null));
147+
EasyMock.expectLastCall();
148+
replay(pubsub);
149+
initializeMessage();
150+
assertNull(message.ackAsync().get());
151+
}
152+
153+
@Test
154+
public void testModifyAckDeadline() {
155+
initializeExpectedMessage(1);
156+
expect(pubsub.options()).andReturn(mockOptions);
157+
pubsub.modifyAckDeadline(SUBSCRIPTION, 10, TimeUnit.SECONDS, ACK_ID);
158+
EasyMock.expectLastCall();
159+
replay(pubsub);
160+
initializeMessage();
161+
message.modifyAckDeadline(10, TimeUnit.SECONDS);
162+
}
163+
164+
@Test
165+
public void testModifyAckDeadlineAsync() throws ExecutionException, InterruptedException {
166+
initializeExpectedMessage(1);
167+
expect(pubsub.options()).andReturn(mockOptions);
168+
expect(pubsub.modifyAckDeadlineAsync(SUBSCRIPTION, 10, TimeUnit.SECONDS, ACK_ID))
169+
.andReturn(Futures.<Void>immediateFuture(null));
170+
EasyMock.expectLastCall();
171+
replay(pubsub);
172+
initializeMessage();
173+
assertNull(message.modifyAckDeadlineAsync(10, TimeUnit.SECONDS).get());
174+
}
175+
176+
private void compareReceivedMessage(ReceivedMessage expected, ReceivedMessage value) {
177+
assertEquals(expected, value);
178+
assertEquals(expected.id(), value.id());
179+
assertEquals(expected.payload(), value.payload());
180+
assertEquals(expected.payloadAsString(), value.payloadAsString());
181+
assertEquals(expected.attributes(), value.attributes());
182+
assertEquals(expected.publishTime(), value.publishTime());
183+
assertEquals(expected.ackId(), value.ackId());
184+
assertEquals(expected.subscription(), value.subscription());
185+
assertEquals(expected.hashCode(), value.hashCode());
186+
}
187+
}

0 commit comments

Comments
 (0)