From 1bd31c21a86d5a3363b22cb6fb19c3fcef01add5 Mon Sep 17 00:00:00 2001 From: aozarov Date: Wed, 10 Feb 2016 16:44:58 -0800 Subject: [PATCH 01/10] adding PubSubOptions, PubSub and its factories --- gcloud-java-pubsub/pom.xml | 5 + .../java/com/google/gcloud/pubsub/PubSub.java | 28 +++++ .../google/gcloud/pubsub/PubSubException.java | 62 ++++++++++ .../google/gcloud/pubsub/PubSubFactory.java | 25 ++++ .../com/google/gcloud/pubsub/PubSubImpl.java | 30 +++++ .../google/gcloud/pubsub/PubSubOptions.java | 117 ++++++++++++++++++ .../google/gcloud/pubsub/package-info.java | 29 +++++ .../gcloud/pubsub/spi/DefaultPubSubRpc.java | 28 +++++ .../google/gcloud/pubsub/spi/PubSubRpc.java | 20 +++ .../gcloud/pubsub/spi/PubSubRpcFactory.java | 27 ++++ 10 files changed, 371 insertions(+) create mode 100644 gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/PubSub.java create mode 100644 gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/PubSubException.java create mode 100644 gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/PubSubFactory.java create mode 100644 gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/PubSubImpl.java create mode 100644 gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/PubSubOptions.java create mode 100644 gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/package-info.java create mode 100644 gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/spi/DefaultPubSubRpc.java create mode 100644 gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/spi/PubSubRpc.java create mode 100644 gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/spi/PubSubRpcFactory.java diff --git a/gcloud-java-pubsub/pom.xml b/gcloud-java-pubsub/pom.xml index 4a7485450433..c99fae934996 100644 --- a/gcloud-java-pubsub/pom.xml +++ b/gcloud-java-pubsub/pom.xml @@ -16,6 +16,11 @@ gcloud-java-pubsub + + ${project.groupId} + gcloud-java-core + ${project.version} + com.google.api gax diff --git a/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/PubSub.java b/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/PubSub.java new file mode 100644 index 000000000000..e84e4d66332b --- /dev/null +++ b/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/PubSub.java @@ -0,0 +1,28 @@ +/* + * 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.gcloud.pubsub; + +import com.google.gcloud.Service; + +/** + * An interface for Google Cloud Pub/Sub. + * + * @see Google Cloud Pub/Sub + */ +public interface PubSub extends Service { + +} diff --git a/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/PubSubException.java b/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/PubSubException.java new file mode 100644 index 000000000000..91f56ba1f35a --- /dev/null +++ b/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/PubSubException.java @@ -0,0 +1,62 @@ +/* + * 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.gcloud.pubsub; + +import com.google.common.collect.ImmutableSet; +import com.google.gcloud.BaseServiceException; +import com.google.gcloud.RetryHelper.RetryHelperException; +import com.google.gcloud.RetryHelper.RetryInterruptedException; + +import java.util.Set; + +/** + * Pub/Sub service exception. + * + * @see Google Cloud Pub/Sub error codes + */ +public class PubSubException extends BaseServiceException { + + private static final long serialVersionUID = 6434989638600001226L; + private static final Set RETRYABLE_ERRORS = ImmutableSet.of( + new Error(499, null), + new Error(503, null), + new Error(429, null), + new Error(500, null), + new Error(504, null)); + + public PubSubException(int code, String message) { + super(code, message, null, true); + } + + @Override + protected Set retryableErrors() { + return RETRYABLE_ERRORS; + } + + /** + * Translate RetryHelperException to the ResourceManagerException that caused the error. This + * method will always throw an exception. + * + * @throws PubSubException when {@code ex} was caused by a {@code + * ResourceManagerException} + * @throws RetryInterruptedException when {@code ex} is a {@code RetryInterruptedException} + */ + static PubSubException translateAndThrow(RetryHelperException ex) { + BaseServiceException.translateAndPropagateIfPossible(ex); + throw new PubSubException(UNKNOWN_CODE, ex.getMessage()); + } +} diff --git a/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/PubSubFactory.java b/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/PubSubFactory.java new file mode 100644 index 000000000000..024d68406982 --- /dev/null +++ b/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/PubSubFactory.java @@ -0,0 +1,25 @@ +/* + * 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.gcloud.pubsub; + +import com.google.gcloud.ServiceFactory; + +/** + * An interface for Pub/Sub factories. + */ +public interface PubSubFactory + extends ServiceFactory {} diff --git a/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/PubSubImpl.java b/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/PubSubImpl.java new file mode 100644 index 000000000000..45b57dc0faba --- /dev/null +++ b/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/PubSubImpl.java @@ -0,0 +1,30 @@ +/* + * 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.gcloud.pubsub; + +import com.google.gcloud.BaseService; +import com.google.gcloud.pubsub.spi.PubSubRpc; + +class PubSubImpl extends BaseService implements PubSub { + + private final PubSubRpc rpc; + + public PubSubImpl(PubSubOptions options) { + super(options); + rpc = options.rpc(); + } +} diff --git a/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/PubSubOptions.java b/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/PubSubOptions.java new file mode 100644 index 000000000000..54f97d7e5938 --- /dev/null +++ b/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/PubSubOptions.java @@ -0,0 +1,117 @@ +/* + * 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.gcloud.pubsub; + +import com.google.common.collect.ImmutableSet; +import com.google.gcloud.ServiceOptions; +import com.google.gcloud.pubsub.spi.DefaultPubSubRpc; +import com.google.gcloud.pubsub.spi.PubSubRpc; +import com.google.gcloud.pubsub.spi.PubSubRpcFactory; + +import java.util.Set; + +public class PubSubOptions extends ServiceOptions { + + private static final long serialVersionUID = 6740347843343421456L; + private static final String PUBSUB_SCOPE = "https://www.googleapis.com/auth/pubsub"; + private static final Set SCOPES = ImmutableSet.of(PUBSUB_SCOPE); + private static final String DEFAULT_HOST = "https://pubsub.googleapis.com"; + + public static class DefaultPubSubFactory implements PubSubFactory { + private static final PubSubFactory INSTANCE = new DefaultPubSubFactory(); + + @Override + public PubSub create(PubSubOptions options) { + return new PubSubImpl(options); + } + } + + /** + * Returns a default {@code PubSubOptions} instance. + */ + public static PubSubOptions defaultInstance() { + return builder().build(); + } + + public static class DefaultPubSubRpcFactory implements PubSubRpcFactory { + private static final PubSubRpcFactory INSTANCE = + new DefaultPubSubRpcFactory(); + + @Override + public PubSubRpc create(PubSubOptions options) { + return new DefaultPubSubRpc(options); + } + } + + @Override + protected String defaultHost() { + return DEFAULT_HOST; + } + + public static class Builder extends + ServiceOptions.Builder { + + private Builder() {} + + private Builder(PubSubOptions options) { + super(options); + } + + @Override + public PubSubOptions build() { + return new PubSubOptions(this); + } + } + + private PubSubOptions(Builder builder) { + super(PubSubFactory.class, PubSubRpcFactory.class, builder); + } + + @Override + protected PubSubFactory defaultServiceFactory() { + return DefaultPubSubFactory.INSTANCE; + } + + @Override + protected PubSubRpcFactory defaultRpcFactory() { + return DefaultPubSubRpcFactory.INSTANCE; + } + + @Override + protected Set scopes() { + return SCOPES; + } + + @Override + public boolean equals(Object obj) { + return obj instanceof PubSubOptions && baseEquals((PubSubOptions) obj); + } + + @Override + public int hashCode() { + return baseHashCode(); + } + + @Override + public Builder toBuilder() { + return new Builder(this); + } + + public static Builder builder() { + return new Builder(); + } +} diff --git a/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/package-info.java b/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/package-info.java new file mode 100644 index 000000000000..4782f6927567 --- /dev/null +++ b/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/package-info.java @@ -0,0 +1,29 @@ +/* + * 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. + */ + +/** + * A client to Google Cloud Pub/Sub. + * + *

Here's a simple usage example for using gcloud-pubsub: + * todo: add example + *

 {@code
+ * PubSub pubsub = PubSubOptions.defaultInstance().service();
+ * }
+ * + * @see Google Cloud Pub/Sub + */ + +package com.google.gcloud.pubsub; diff --git a/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/spi/DefaultPubSubRpc.java b/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/spi/DefaultPubSubRpc.java new file mode 100644 index 000000000000..b1d705c87e54 --- /dev/null +++ b/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/spi/DefaultPubSubRpc.java @@ -0,0 +1,28 @@ +/* + * 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.gcloud.pubsub.spi; + +import com.google.gcloud.pubsub.PubSubOptions; + +public class DefaultPubSubRpc implements PubSubRpc { + + private final PubSubOptions options; + + public DefaultPubSubRpc(PubSubOptions options) { + this.options = options; + } +} diff --git a/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/spi/PubSubRpc.java b/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/spi/PubSubRpc.java new file mode 100644 index 000000000000..4f0d26a96119 --- /dev/null +++ b/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/spi/PubSubRpc.java @@ -0,0 +1,20 @@ +/* + * 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.gcloud.pubsub.spi; + +public interface PubSubRpc { +} diff --git a/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/spi/PubSubRpcFactory.java b/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/spi/PubSubRpcFactory.java new file mode 100644 index 000000000000..308efe8c9995 --- /dev/null +++ b/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/spi/PubSubRpcFactory.java @@ -0,0 +1,27 @@ +/* + * 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.gcloud.pubsub.spi; + +import com.google.gcloud.pubsub.PubSubOptions; +import com.google.gcloud.spi.ServiceRpcFactory; + +/** + * An interface for Pub/Sub RPC factory. + * Implementation will be loaded via {@link java.util.ServiceLoader}. + */ +public interface PubSubRpcFactory extends ServiceRpcFactory { +} From ecacdec0cc454409b88f0621c4e74d2dc18a1bb6 Mon Sep 17 00:00:00 2001 From: Arie Ozarov Date: Sun, 20 Mar 2016 22:19:29 -0700 Subject: [PATCH 02/10] Adding data types and define API --- .../com/google/gcloud/pubsub/Message.java | 192 ++++++++++++++++++ .../java/com/google/gcloud/pubsub/PubSub.java | 91 +++++++++ .../com/google/gcloud/pubsub/PubSubImpl.java | 124 +++++++++++ .../com/google/gcloud/pubsub/PushConfig.java | 142 +++++++++++++ .../google/gcloud/pubsub/Subscription.java | 118 +++++++++++ .../gcloud/pubsub/SubscriptionInfo.java | 180 ++++++++++++++++ .../java/com/google/gcloud/pubsub/Topic.java | 100 +++++++++ .../com/google/gcloud/pubsub/TopicInfo.java | 112 ++++++++++ 8 files changed, 1059 insertions(+) create mode 100644 gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/Message.java create mode 100644 gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/PushConfig.java create mode 100644 gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/Subscription.java create mode 100644 gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/SubscriptionInfo.java create mode 100644 gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/Topic.java create mode 100644 gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/TopicInfo.java diff --git a/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/Message.java b/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/Message.java new file mode 100644 index 000000000000..ff2cacb84e09 --- /dev/null +++ b/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/Message.java @@ -0,0 +1,192 @@ +/* + * 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.gcloud.pubsub; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.api.client.util.Strings; +import com.google.common.base.MoreObjects; +import com.google.common.collect.ImmutableMap; +import com.google.protobuf.ByteString; +import com.google.protobuf.Timestamp; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +/** + * Pub/Sub message. + */ +public final class Message implements Serializable { + + private static final long serialVersionUID = -1436515787233340634L; + private static final long NANOS_PER_MILLISECOND = 1000000; + private static final long MILLIS_PER_SECOND = 1000; + + private final String id; + private final ByteString payload; + private final ImmutableMap attributes; + private final Long publishTime; + + /** + * Builder for Message. + */ + public static final class Builder { + + private String id; + private byte[] payload; + private Map attributes = new HashMap<>(); + private Long publishTime; + + private Builder() {} + + private Builder(Message message) { + id = message.id; + payload = message.payload.toByteArray(); + attributes = new HashMap<>(message.attributes); + publishTime = message.publishTime; + } + + Builder id(String id) { + this.id = id; + return this; + } + + public Builder payload(byte[] payload) { + this.payload = checkNotNull(payload); + return this; + } + + public Builder addAttribute(String name, String value) { + attributes.put(name, value); + return this; + } + + public Builder removeAttribute(String name) { + attributes.remove(name); + return this; + } + + public Builder clearAttributes() { + attributes.clear(); + return this; + } + + Builder publishTime(Long publishTime) { + this.publishTime = publishTime; + return this; + } + + public Message build() { + return new Message(this); + } + } + + private Message(Builder builder) { + id = builder.id; + payload = ByteString.copyFrom(checkNotNull(builder.payload)); + attributes = ImmutableMap.copyOf(builder.attributes); + publishTime = builder.publishTime; + } + + public Long publishTime() { + return publishTime; + } + + public Map attributes() { + return attributes; + } + + public String id() { + return id; + } + + public byte[] payload() { + return payload.toByteArray(); + } + + @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()); + } + + @Override + public int hashCode() { + return Objects.hash(serialVersionUID, id, payload, attributes, publishTime); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("id", id) + .add("payload", payload) + .add("attributes", attributes) + .add("publishTime", publishTime) + .toString(); + } + + com.google.pubsub.v1.PubsubMessage toPb() { + com.google.pubsub.v1.PubsubMessage.Builder builder = + com.google.pubsub.v1.PubsubMessage.newBuilder(); + if (id != null) { + builder.setMessageId(id); + } + builder.setData(payload); + builder.getAttributes().putAll(attributes); + if (publishTime != null) { + Timestamp.Builder tsBuilder = Timestamp.newBuilder(); + tsBuilder.setSeconds(publishTime / MILLIS_PER_SECOND); + tsBuilder.setNanos((int) (publishTime % MILLIS_PER_SECOND * NANOS_PER_MILLISECOND)); + builder.setPublishTime(tsBuilder); + } + return builder.build(); + } + + static Message fromPb(com.google.pubsub.v1.PubsubMessage messagePb) { + Builder builder = builder(); + if (messagePb.hasPublishTime()) { + Timestamp ts = messagePb.getPublishTime(); + builder.publishTime( + ts.getSeconds() * MILLIS_PER_SECOND + ts.getNanos() / NANOS_PER_MILLISECOND); + } + if (Strings.isNullOrEmpty(messagePb.getMessageId())) { + builder.id(messagePb.getMessageId()); + } + for (Map.Entry entry : messagePb.getAttributes().entrySet()) { + builder.addAttribute(entry.getKey(), entry.getValue()); + } + builder.payload(messagePb.getData().toByteArray()); + return builder.build(); + } + + public Builder toBuilder() { + return new Builder(this); + } + + public static Message of(byte[] payload) { + return builder().payload(payload).build(); + } + + public static Builder builder() { + return new Builder(); + } +} diff --git a/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/PubSub.java b/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/PubSub.java index e84e4d66332b..36e78cd78b89 100644 --- a/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/PubSub.java +++ b/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/PubSub.java @@ -16,8 +16,12 @@ package com.google.gcloud.pubsub; +import com.google.gcloud.Page; import com.google.gcloud.Service; +import java.util.List; +import java.util.concurrent.Future; + /** * An interface for Google Cloud Pub/Sub. * @@ -25,4 +29,91 @@ */ public interface PubSub extends Service { + class ListOption { + // page size + // page token + } + + class PullOption { + // bool return_immediately = 2; + // int32 max_messages = 3; + } + + // topics + ////////////////////// + Topic createTopic(TopicInfo topic); + + Future createTopicAsync(String topic); + + // null if not found + Topic getTopic(String topic); + + Future getTopicAsync(String topic); + + // false if not found + boolean deleteTopic(String topic); + + Future deleteTopicAsync(String topic); + + Page listTopics(ListOption... options); + + // todo: consider AsyncPage that has nextPageAsync + Future> listTopicsAsync(ListOption... options); + + String publish(String topic, Message message); + + Future publishAsync(String topic, Message message); + + List publish(String topic, Message first, Message... other); + + Future> publishAsync(String topic, Message first, Message... other); + + // subscriptions + //////////////////////////// + Subscription createSubscription(SubscriptionInfo subscription); + + Future createSubscriptionAsync(SubscriptionInfo subscription); + + // null if not found + Subscription getSubscription(String subscription); + + Future getSubscriptionAsync(String topic); + + // false if not found + boolean deleteSubscription(String subscription); + + Future deleteSubscriptionAsync(String subscription); + + Page listSubscription(ListOption... options); + + // todo: consider AsyncPage that has nextPageAsync + Future> listSubscriptionAsync(ListOption... options); + + Page listSubscription(String topic, ListOption... options); + + // todo: consider AsyncPage that has nextPageAsync + Future> listSubscriptionAsync(String topic, ListOption... options); + + // Ack options: + // 1) replace return value to ReceivedMessage (Message + functional ack) + // 2) like (1) but with no auto-renew, so provide a function for renew + // 3) rename Message to MessageInfo and make Message functional with ack + // 4) return a "special" list (extends List) but has a way to "ackSoFar" + // 5) instead of List use callback and auto-acknowledge per callback (and auto-renew) + // ** Auto renew means, using a separate thread. + List pull(String subscription, PullOption... options); + + Future> pullAsync(String subscription, PullOption... options); + + + // Note regarding Date types: + // 1) No field selection + // --- This is why there are no options for getters + // 2) Never null for primitive values or collections + // --- should we replace default "" with null (e.g. id when not populated)? + + // IAM Policy operations: getPolicy, replacePolicy, testPermissions + // Not sure if ready (docs is not up-to-date), is it one per topic and + // one per subscription? + } diff --git a/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/PubSubImpl.java b/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/PubSubImpl.java index 45b57dc0faba..4173677d0528 100644 --- a/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/PubSubImpl.java +++ b/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/PubSubImpl.java @@ -17,8 +17,12 @@ package com.google.gcloud.pubsub; import com.google.gcloud.BaseService; +import com.google.gcloud.Page; import com.google.gcloud.pubsub.spi.PubSubRpc; +import java.util.List; +import java.util.concurrent.Future; + class PubSubImpl extends BaseService implements PubSub { private final PubSubRpc rpc; @@ -27,4 +31,124 @@ public PubSubImpl(PubSubOptions options) { super(options); rpc = options.rpc(); } + + @Override + public Topic createTopic(TopicInfo topic) { + return null; + } + + @Override + public Future createTopicAsync(String topic) { + return null; + } + + @Override + public Topic getTopic(String topic) { + return null; + } + + @Override + public Future getTopicAsync(String topic) { + return null; + } + + @Override + public boolean deleteTopic(String topic) { + return false; + } + + @Override + public Future deleteTopicAsync(String topic) { + return null; + } + + @Override + public Page listTopics(ListOption... options) { + return null; + } + + @Override + public Future> listTopicsAsync(ListOption... options) { + return null; + } + + @Override + public String publish(String topic, Message message) { + return null; + } + + @Override + public Future publishAsync(String topic, Message message) { + return null; + } + + @Override + public List publish(String topic, Message first, Message... other) { + return null; + } + + @Override + public Future> publishAsync(String topic, Message first, Message... other) { + return null; + } + + @Override + public Subscription createSubscription(SubscriptionInfo subscription) { + return null; + } + + @Override + public Future createSubscriptionAsync(SubscriptionInfo subscription) { + return null; + } + + @Override + public Subscription getSubscription(String subscription) { + return null; + } + + @Override + public Future getSubscriptionAsync(String topic) { + return null; + } + + @Override + public boolean deleteSubscription(String subscription) { + return false; + } + + @Override + public Future deleteSubscriptionAsync(String subscription) { + return null; + } + + @Override + public Page listSubscription(ListOption... options) { + return null; + } + + @Override + public Future> listSubscriptionAsync(ListOption... options) { + return null; + } + + @Override + public Page listSubscription(String topic, ListOption... options) { + return null; + } + + @Override + public Future> listSubscriptionAsync(String topic, ListOption... options) { + return null; + } + + @Override + public List pull(String subscription, PullOption... options) { + return null; + } + + @Override + public Future> pullAsync(String subscription, PullOption... options) { + return null; + } } diff --git a/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/PushConfig.java b/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/PushConfig.java new file mode 100644 index 000000000000..4f903c534cfe --- /dev/null +++ b/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/PushConfig.java @@ -0,0 +1,142 @@ +/* + * 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.gcloud.pubsub; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.base.MoreObjects; +import com.google.common.collect.ImmutableMap; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +/** + * PubSub subscription push configuration. + */ +public final class PushConfig implements Serializable { + + private static final long serialVersionUID = 4408885787064092231L; + + private final String endpoint; + private final ImmutableMap attributes; + + public static final class Builder { + + private String endpoint; + private final Map attributes = new HashMap<>(); + + private Builder() { + } + + public Builder endPoint(String endpoint) { + this.endpoint = checkNotNull(endpoint); + return this; + } + + public Builder addAttribute(String name, String value) { + attributes.put(name, value); + return this; + } + + public Builder removeAttribute(String name) { + attributes.remove(name); + return this; + } + + public Builder clearAttributes() { + attributes.clear(); + return this; + } + + public PushConfig build() { + return new PushConfig(this); + } + } + + private PushConfig(Builder builder) { + endpoint = builder.endpoint; + attributes = ImmutableMap.copyOf(builder.attributes); + } + + public String endpoint() { + return endpoint; + } + + public Map attributes() { + return attributes; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + PushConfig that = (PushConfig) o; + return Objects.equals(endpoint, that.endpoint) && Objects.equals(attributes, that.attributes); + } + + @Override + public int hashCode() { + return Objects.hash(endpoint, attributes); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("attributes", attributes) + .add("endpoint", endpoint) + .toString(); + } + + public Builder toBuilder() { + return builder(endpoint, attributes); + } + + public static PushConfig of(String endpoint) { + return builder(endpoint).build(); + } + + public static PushConfig of(String endpoint, Map attributes) { + return builder(endpoint, attributes).build(); + } + + public static Builder builder(String endPoint) { + return new Builder().endPoint(endPoint); + } + + public static Builder builder(String endpoint, Map attributes) { + Builder builder = builder(endpoint); + for (Map.Entry entry : attributes.entrySet()) { + builder.addAttribute(entry.getKey(), entry.getValue()); + } + return builder; + } + + com.google.pubsub.v1.PushConfig toPb() { + return com.google.pubsub.v1.PushConfig.newBuilder().setPushEndpoint(endpoint) + .putAllAttributes(attributes).build(); + } + + static PushConfig fromPb(com.google.pubsub.v1.PushConfig pushConfigPb) { + return builder(pushConfigPb.getPushEndpoint(), pushConfigPb.getAttributes()).build(); + } +} diff --git a/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/Subscription.java b/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/Subscription.java new file mode 100644 index 000000000000..c14ae815b0e1 --- /dev/null +++ b/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/Subscription.java @@ -0,0 +1,118 @@ +/* + * 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.gcloud.pubsub; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.util.Objects; + +/** + * PubSub subscription. + */ +public class Subscription extends SubscriptionInfo { + + private final PubSubOptions options; + private transient PubSub pubsub; + + public static final class Builder extends SubscriptionInfo.Builder { + + private final PubSub pubsub; + private final BuilderImpl delegate; + + private Builder(Subscription subscription) { + pubsub = subscription.pubsub; + delegate = new BuilderImpl(subscription); + } + + @Override + public Builder topic(String name) { + delegate.topic(name); + return this; + } + + @Override + public Builder name(String name) { + delegate.name(name); + return this; + } + + @Override + public Builder pushConfig(PushConfig pushConfig) { + delegate.pushConfig(pushConfig); + return this; + } + + @Override + public Builder ackDeadLineSeconds(int ackDeadLineSeconds) { + delegate.ackDeadLineSeconds(ackDeadLineSeconds); + return this; + } + + @Override + public Subscription build() { + return new Subscription(this.pubsub, this.delegate); + } + } + + Subscription(PubSub pubsub, BuilderImpl builder) { + super(builder); + this.pubsub = checkNotNull(pubsub); + options = pubsub.options(); + } + + @Override + public Builder toBuilder() { + return new Builder(this); + } + + @Override + public int hashCode() { + return Objects.hash(options, super.hashCode()); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + Subscription other = (Subscription) obj; + return Objects.equals(toPb(), other.toPb()) && Objects.equals(options, other.options); + } + + public PubSub pubSub() { + return pubsub; + } + + // Operations to add: + // delete, reload, modifyPushConfig? (instead of replace), pull + + + private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { + in.defaultReadObject(); + this.pubsub = options.service(); + } + + static Subscription fromPb(PubSub storage, com.google.pubsub.v1.Subscription subscriptionPb) { + SubscriptionInfo subscriptionInfo = SubscriptionInfo.fromPb(subscriptionPb); + return new Subscription(storage, new BuilderImpl(subscriptionInfo)); + } +} diff --git a/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/SubscriptionInfo.java b/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/SubscriptionInfo.java new file mode 100644 index 000000000000..2b42b4b8e2ea --- /dev/null +++ b/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/SubscriptionInfo.java @@ -0,0 +1,180 @@ +/* + * 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.gcloud.pubsub; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.base.MoreObjects; + +import java.io.Serializable; +import java.util.Objects; + +/** + * Pub/Sub subscription information. + */ +public class SubscriptionInfo implements Serializable { + + private static final long serialVersionUID = 1860057426574127128L; + + private final String name; + private final String topic; + private final PushConfig pushConfig; + private final int ackDeadlineSeconds; + + + /** + * Builder for Subscription. + */ + public abstract static class Builder { + + public abstract Builder name(String name); + + public abstract Builder topic(String name); + + public abstract Builder pushConfig(PushConfig pushConfig); + + public abstract Builder ackDeadLineSeconds(int ackDeadLineSeconds); + + public abstract SubscriptionInfo build(); + } + + static final class BuilderImpl extends Builder { + + private String name; + private String topic; + private PushConfig pushConfig; + private int ackDeadlineSeconds; + + private BuilderImpl(String topic, String name) { + this.topic = topic; + this.name = name; + } + + BuilderImpl(SubscriptionInfo subscription) { + name = subscription.name; + topic = subscription.topic; + pushConfig = subscription.pushConfig; + ackDeadlineSeconds = subscription.ackDeadlineSeconds; + } + + public Builder name(String name) { + this.name = checkNotNull(name); + return this; + } + + public Builder topic(String topic) { + this.topic = checkNotNull(topic); + return this; + } + + public Builder pushConfig(PushConfig pushConfig) { + this.pushConfig = pushConfig; + return this; + } + + public Builder ackDeadLineSeconds(int ackDeadlineSeconds) { + this.ackDeadlineSeconds = ackDeadlineSeconds; + return this; + } + + public SubscriptionInfo build() { + return new SubscriptionInfo(this); + } + } + + SubscriptionInfo(BuilderImpl builder) { + topic = builder.topic; + name = builder.name; + pushConfig = builder.pushConfig; + ackDeadlineSeconds = builder.ackDeadlineSeconds; + } + + public String topic() { + return topic; + } + + public String name() { + return name; + } + + public PushConfig pushConfig() { + return pushConfig; + } + + public long ackDeadlineSeconds() { + return ackDeadlineSeconds; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + return Objects.equals(toPb(), ((SubscriptionInfo)o).toPb()); + } + + @Override + public int hashCode() { + return Objects.hash(topic, name, pushConfig, ackDeadlineSeconds); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("topic", topic) + .add("name", name) + .add("pushConfig", pushConfig) + .add("ackDeadlineSeconds", ackDeadlineSeconds) + .toString(); + } + + com.google.pubsub.v1.Subscription toPb() { + com.google.pubsub.v1.Subscription.Builder builder = + com.google.pubsub.v1.Subscription.newBuilder(); + builder.setTopic(topic); + builder.setName(name); + builder.setAckDeadlineSeconds(ackDeadlineSeconds); + if (pushConfig != null) { + builder.setPushConfig(pushConfig.toPb()); + } + return builder.build(); + } + + static SubscriptionInfo fromPb(com.google.pubsub.v1.Subscription subscription) { + Builder builder = builder(subscription.getTopic(), subscription.getName()); + builder.ackDeadLineSeconds(subscription.getAckDeadlineSeconds()); + if (subscription.hasPushConfig()) { + builder.pushConfig(PushConfig.fromPb(subscription.getPushConfig())); + } + return builder.build(); + } + + public Builder toBuilder() { + return new BuilderImpl(this); + } + + public static SubscriptionInfo of(String topic, String name) { + return builder(topic, name).build(); + } + + public static Builder builder(String topic, String name) { + return new BuilderImpl(topic, name); + } +} diff --git a/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/Topic.java b/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/Topic.java new file mode 100644 index 000000000000..1520bf453dd5 --- /dev/null +++ b/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/Topic.java @@ -0,0 +1,100 @@ +/* + * 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.gcloud.pubsub; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.util.Objects; + +/** + * PubSub Topic. + */ +public class Topic extends TopicInfo { + + private final PubSubOptions options; + private transient PubSub pubsub; + + public static final class Builder extends TopicInfo.Builder { + + private final PubSub pubsub; + private final BuilderImpl delegate; + + private Builder(Topic topic) { + pubsub = topic.pubsub; + delegate = new BuilderImpl(topic); + } + + @Override + public TopicInfo.Builder name(String name) { + delegate.name(name); + return this; + } + + @Override + public Topic build() { + return new Topic(this.pubsub, this.delegate); + } + } + + Topic(PubSub pubsub, BuilderImpl builder) { + super(builder); + this.pubsub = checkNotNull(pubsub); + options = pubsub.options(); + } + + @Override + public Builder toBuilder() { + return new Builder(this); + } + + @Override + public int hashCode() { + return Objects.hash(options, super.hashCode()); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + Topic other = (Topic) obj; + return Objects.equals(toPb(), other.toPb()) && Objects.equals(options, other.options); + } + + public PubSub pubSub() { + return pubsub; + } + + // Operations to add: + // delete, reload, publish, listSubscriptions for topic, + + + private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { + in.defaultReadObject(); + this.pubsub = options.service(); + } + + static Topic fromPb(PubSub storage, com.google.pubsub.v1.Topic topicPb) { + TopicInfo topicInfo = TopicInfo.fromPb(topicPb); + return new Topic(storage, new BuilderImpl(topicInfo)); + } +} diff --git a/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/TopicInfo.java b/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/TopicInfo.java new file mode 100644 index 000000000000..cb1071e8aa15 --- /dev/null +++ b/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/TopicInfo.java @@ -0,0 +1,112 @@ +/* + * 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.gcloud.pubsub; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.base.MoreObjects; + +import java.io.Serializable; +import java.util.Objects; + +/** + * Pub/Sub Topic information. + */ +public class TopicInfo implements Serializable { + + private static final long serialVersionUID = -5907624842808725353L; + + private final String name; + + /** + * Builder for TopicInfo. + */ + public abstract static class Builder { + + public abstract Builder name(String name); + + public abstract TopicInfo build(); + } + + static final class BuilderImpl extends Builder { + + private String name; + + BuilderImpl() { + } + + BuilderImpl(TopicInfo topicInfo) { + this.name = topicInfo.name; + } + + public Builder name(String name) { + this.name = checkNotNull(name); + return this; + } + + public TopicInfo build() { + return new TopicInfo(this); + } + } + + TopicInfo(BuilderImpl builder) { + name = builder.name; + } + + public String name() { + return name; + } + + @Override + public int hashCode() { + return Objects.hash(name); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + return Objects.equals(toPb(), ((TopicInfo) obj).toPb()); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("name", name) + .toString(); + } + + com.google.pubsub.v1.Topic toPb() { + return com.google.pubsub.v1.Topic.newBuilder().setName(name).build(); + } + + static TopicInfo fromPb(com.google.pubsub.v1.Topic topicPb) { + return builder().name(topicPb.getName()).build(); + } + + public Builder toBuilder() { + return new BuilderImpl(this); + } + + public static Builder builder() { + return new BuilderImpl(); + } +} From 5eef7a980438b011c4b52e41ca93796b33d5873b Mon Sep 17 00:00:00 2001 From: aozarov Date: Tue, 22 Mar 2016 17:38:59 -0700 Subject: [PATCH 03/10] Add ReceivedMessage and fix review comments --- .../java/com/google/gcloud/AsyncPage.java | 23 +++ .../com/google/gcloud/pubsub/Message.java | 40 ++++- .../java/com/google/gcloud/pubsub/PubSub.java | 149 ++++++++++++---- .../com/google/gcloud/pubsub/PubSubImpl.java | 91 ++++++++-- .../google/gcloud/pubsub/ReceivedMessage.java | 160 ++++++++++++++++++ .../google/gcloud/pubsub/Subscription.java | 36 +++- .../gcloud/pubsub/SubscriptionInfo.java | 8 +- .../java/com/google/gcloud/pubsub/Topic.java | 56 +++++- .../com/google/gcloud/pubsub/TopicInfo.java | 13 +- 9 files changed, 507 insertions(+), 69 deletions(-) create mode 100644 gcloud-java-core/src/main/java/com/google/gcloud/AsyncPage.java create mode 100644 gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/ReceivedMessage.java diff --git a/gcloud-java-core/src/main/java/com/google/gcloud/AsyncPage.java b/gcloud-java-core/src/main/java/com/google/gcloud/AsyncPage.java new file mode 100644 index 000000000000..fea806fd7089 --- /dev/null +++ b/gcloud-java-core/src/main/java/com/google/gcloud/AsyncPage.java @@ -0,0 +1,23 @@ +/* + * 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.gcloud; + +import java.util.concurrent.Future; + +public interface AsyncPage extends Page { + Future> nextPageAsync(); +} diff --git a/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/Message.java b/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/Message.java index ff2cacb84e09..0f3794abcb75 100644 --- a/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/Message.java +++ b/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/Message.java @@ -30,7 +30,7 @@ /** * Pub/Sub message. */ -public final class Message implements Serializable { +public class Message implements Serializable { private static final long serialVersionUID = -1436515787233340634L; private static final long NANOS_PER_MILLISECOND = 1000000; @@ -41,61 +41,85 @@ public final class Message implements Serializable { private final ImmutableMap attributes; private final Long publishTime; + abstract static class Builder { + + abstract Builder id(String id); + + public abstract Builder payload(byte[] payload); + + public abstract Builder addAttribute(String name, String value); + + public abstract Builder removeAttribute(String name); + + public abstract Builder clearAttributes(); + + abstract Builder publishTime(Long publishTime); + + public abstract Message build(); + } + /** * Builder for Message. */ - public static final class Builder { + public static final class BuilderImpl extends Builder { private String id; private byte[] payload; private Map attributes = new HashMap<>(); private Long publishTime; - private Builder() {} + private BuilderImpl() {} - private Builder(Message message) { + BuilderImpl(Message message) { id = message.id; payload = message.payload.toByteArray(); attributes = new HashMap<>(message.attributes); publishTime = message.publishTime; } - Builder id(String id) { + @Override + BuilderImpl id(String id) { this.id = id; return this; } + @Override public Builder payload(byte[] payload) { this.payload = checkNotNull(payload); return this; } + @Override public Builder addAttribute(String name, String value) { attributes.put(name, value); return this; } + @Override public Builder removeAttribute(String name) { attributes.remove(name); return this; } + @Override public Builder clearAttributes() { attributes.clear(); return this; } + @Override Builder publishTime(Long publishTime) { this.publishTime = publishTime; return this; } + @Override public Message build() { return new Message(this); } } - private Message(Builder builder) { + Message(BuilderImpl builder) { id = builder.id; payload = ByteString.copyFrom(checkNotNull(builder.payload)); attributes = ImmutableMap.copyOf(builder.attributes); @@ -179,7 +203,7 @@ static Message fromPb(com.google.pubsub.v1.PubsubMessage messagePb) { } public Builder toBuilder() { - return new Builder(this); + return new BuilderImpl(this); } public static Message of(byte[] payload) { @@ -187,6 +211,6 @@ public static Message of(byte[] payload) { } public static Builder builder() { - return new Builder(); + return new BuilderImpl(); } } diff --git a/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/PubSub.java b/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/PubSub.java index 36e78cd78b89..69aea74e4613 100644 --- a/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/PubSub.java +++ b/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/PubSub.java @@ -16,11 +16,14 @@ package com.google.gcloud.pubsub; +import com.google.gcloud.AsyncPage; import com.google.gcloud.Page; import com.google.gcloud.Service; +import java.io.Serializable; import java.util.List; import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; /** * An interface for Google Cloud Pub/Sub. @@ -29,21 +32,75 @@ */ public interface PubSub extends Service { - class ListOption { - // page size - // page token + final class ListOption implements Serializable { + + private static final long serialVersionUID = 6517442127283383124L; + + private final Option option; + private final Object value; + + enum Option { + PAGE_SIZE, PAGE_TOKEN + } + + private ListOption(Option option, Object value) { + this.option = option; + this.value = value; + } + + Option option() { + return option; + } + + Object value() { + return value; + } + + public static ListOption pageSize(int pageSize) { + return new ListOption(Option.PAGE_SIZE, pageSize); + } + + public static ListOption pageToken(String pageToken) { + return new ListOption(Option.PAGE_TOKEN, pageToken); + } } - class PullOption { - // bool return_immediately = 2; - // int32 max_messages = 3; + final class PullOption implements Serializable { + + private static final long serialVersionUID = -5220474819637439937L; + + private final Option option; + private final Object value; + + enum Option { + RETURN_IMMEDIATELY, MAX_MESSAGES + } + + private PullOption(Option option, Object value) { + this.option = option; + this.value = value; + } + + Option option() { + return option; + } + + Object value() { + return value; + } + + public static PullOption returnImmediatly() { + return new PullOption(Option.RETURN_IMMEDIATELY, true); + } + + public static PullOption maxMessages(int maxMessages) { + return new PullOption(Option.MAX_MESSAGES, maxMessages); + } } - // topics - ////////////////////// - Topic createTopic(TopicInfo topic); + Topic create(TopicInfo topic); - Future createTopicAsync(String topic); + Future createAsync(TopicInfo topic); // null if not found Topic getTopic(String topic); @@ -57,63 +114,81 @@ class PullOption { Page listTopics(ListOption... options); - // todo: consider AsyncPage that has nextPageAsync - Future> listTopicsAsync(ListOption... options); + Future> listTopicsAsync(ListOption... options); String publish(String topic, Message message); Future publishAsync(String topic, Message message); - List publish(String topic, Message first, Message... other); + List publish(String topic, Message message, Message... messages); + + Future> publishAsync(String topic, Message message, Message... messages); + + List publish(String topic, Iterable messages); - Future> publishAsync(String topic, Message first, Message... other); + Future> publishAsync(String topic, Iterable messages); - // subscriptions - //////////////////////////// - Subscription createSubscription(SubscriptionInfo subscription); + Subscription create(SubscriptionInfo subscription); - Future createSubscriptionAsync(SubscriptionInfo subscription); + Future createAsync(SubscriptionInfo subscription); // null if not found Subscription getSubscription(String subscription); - Future getSubscriptionAsync(String topic); + Future getSubscriptionAsync(String subscription); + + void replacePushConfig(String subscription, PushConfig pushConfig); + + Future replacePushConfigAsync(String subscription, PushConfig pushConfig); // false if not found boolean deleteSubscription(String subscription); Future deleteSubscriptionAsync(String subscription); - Page listSubscription(ListOption... options); + Page listSubscriptions(ListOption... options); - // todo: consider AsyncPage that has nextPageAsync - Future> listSubscriptionAsync(ListOption... options); + Future> listSubscriptionsAsync(ListOption... options); - Page listSubscription(String topic, ListOption... options); + Page listSubscriptions(String topic, ListOption... options); - // todo: consider AsyncPage that has nextPageAsync - Future> listSubscriptionAsync(String topic, ListOption... options); + Future> listSubscriptionsAsync(String topic, ListOption... options); - // Ack options: - // 1) replace return value to ReceivedMessage (Message + functional ack) - // 2) like (1) but with no auto-renew, so provide a function for renew - // 3) rename Message to MessageInfo and make Message functional with ack - // 4) return a "special" list (extends List) but has a way to "ackSoFar" - // 5) instead of List use callback and auto-acknowledge per callback (and auto-renew) - // ** Auto renew means, using a separate thread. - List pull(String subscription, PullOption... options); + // Possible Ack options: + // 1) return a "special" iterator with "ack-iterated-messages" + // 2) provide a way to pull with callback(Message) - ask messages that were called successfully + // + // Also, consider auto-renewable for all messages that were pulled and not acked. + List pull(String subscription, PullOption... options); Future> pullAsync(String subscription, PullOption... options); + void acknowledge(String subscription, String ackId, String... ackIds); + + Future acknowledgeAsync(String subscription, String ackId, String... ackIds); - // Note regarding Date types: + void acknowledge(String subscription, Iterable ackIds); + + Future acknowledgeAsync(String subscription, Iterable ackIds); + + void modifyAckDeadline(String subscription, int deadline, TimeUnit unit, String ackId, + String... ackIds); + + Future modifyAckDeadlineAsync(String subscription, int deadline, TimeUnit unit, + String ackId, String... ackIds); + + void modifyAckDeadline(String subscription, int deadline, TimeUnit unit, Iterable ackIds); + + Future modifyAckDeadlineAsync(String subscription, int deadline, TimeUnit unit, + Iterable ackIds); + + // Note regarding Data types: // 1) No field selection // --- This is why there are no options for getters // 2) Never null for primitive values or collections // --- should we replace default "" with null (e.g. id when not populated)? // IAM Policy operations: getPolicy, replacePolicy, testPermissions - // Not sure if ready (docs is not up-to-date), is it one per topic and - // one per subscription? - + // Not sure if ready (docs is not up-to-date) + // Looks like policy is per resource (topic or subscription) but not per service? } diff --git a/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/PubSubImpl.java b/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/PubSubImpl.java index 4173677d0528..5b20f921e76f 100644 --- a/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/PubSubImpl.java +++ b/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/PubSubImpl.java @@ -16,12 +16,14 @@ package com.google.gcloud.pubsub; +import com.google.gcloud.AsyncPage; import com.google.gcloud.BaseService; import com.google.gcloud.Page; import com.google.gcloud.pubsub.spi.PubSubRpc; import java.util.List; import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; class PubSubImpl extends BaseService implements PubSub { @@ -33,12 +35,12 @@ public PubSubImpl(PubSubOptions options) { } @Override - public Topic createTopic(TopicInfo topic) { + public Topic create(TopicInfo topic) { return null; } @Override - public Future createTopicAsync(String topic) { + public Future createAsync(TopicInfo topic) { return null; } @@ -68,7 +70,7 @@ public Page listTopics(ListOption... options) { } @Override - public Future> listTopicsAsync(ListOption... options) { + public Future> listTopicsAsync(ListOption... options) { return null; } @@ -83,22 +85,32 @@ public Future publishAsync(String topic, Message message) { } @Override - public List publish(String topic, Message first, Message... other) { + public List publish(String topic, Message message, Message... messages) { return null; } @Override - public Future> publishAsync(String topic, Message first, Message... other) { + public Future> publishAsync(String topic, Message message, Message... messages) { return null; } @Override - public Subscription createSubscription(SubscriptionInfo subscription) { + public List publish(String topic, Iterable messages) { return null; } @Override - public Future createSubscriptionAsync(SubscriptionInfo subscription) { + public Future> publishAsync(String topic, Iterable messages) { + return null; + } + + @Override + public Subscription create(SubscriptionInfo subscription) { + return null; + } + + @Override + public Future createAsync(SubscriptionInfo subscription) { return null; } @@ -108,7 +120,17 @@ public Subscription getSubscription(String subscription) { } @Override - public Future getSubscriptionAsync(String topic) { + public Future getSubscriptionAsync(String subscription) { + return null; + } + + @Override + public void replacePushConfig(String subscription, PushConfig pushConfig) { + + } + + @Override + public Future replacePushConfigAsync(String subscription, PushConfig pushConfig) { return null; } @@ -123,27 +145,27 @@ public Future deleteSubscriptionAsync(String subscription) { } @Override - public Page listSubscription(ListOption... options) { + public Page listSubscriptions(ListOption... options) { return null; } @Override - public Future> listSubscriptionAsync(ListOption... options) { + public Future> listSubscriptionsAsync(ListOption... options) { return null; } @Override - public Page listSubscription(String topic, ListOption... options) { + public Page listSubscriptions(String topic, ListOption... options) { return null; } @Override - public Future> listSubscriptionAsync(String topic, ListOption... options) { + public Future> listSubscriptionsAsync(String topic, ListOption... options) { return null; } @Override - public List pull(String subscription, PullOption... options) { + public List pull(String subscription, PullOption... options) { return null; } @@ -151,4 +173,47 @@ public List pull(String subscription, PullOption... options) { public Future> pullAsync(String subscription, PullOption... options) { return null; } + + @Override + public void acknowledge(String subscription, String ackId, String... ackIds) { + + } + + @Override + public Future acknowledgeAsync(String subscription, String ackId, String... ackIds) { + return null; + } + + @Override + public void acknowledge(String subscription, Iterable ackIds) { + + } + + @Override + public Future acknowledgeAsync(String subscription, Iterable ackIds) { + return null; + } + + @Override + public void modifyAckDeadline(String subscription, int deadline, TimeUnit unit, String ackId, + String... ackIds) { + + } + + @Override + public Future modifyAckDeadlineAsync(String subscription, int deadline, TimeUnit unit, + String ackId, String... ackIds) { + return null; + } + + @Override + public void modifyAckDeadline(String subscription, int deadline, TimeUnit unit, + Iterable ackIds) { + + } + + @Override + public Future modifyAckDeadlineAsync(String subscription, int deadline, TimeUnit unit, Iterable ackIds) { + return null; + } } diff --git a/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/ReceivedMessage.java b/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/ReceivedMessage.java new file mode 100644 index 000000000000..92de5059f7cb --- /dev/null +++ b/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/ReceivedMessage.java @@ -0,0 +1,160 @@ +/* + * 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.gcloud.pubsub; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.util.Objects; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +public class ReceivedMessage extends Message { + + private static final long serialVersionUID = -4178477763916251733L; + + private final String subscription; + private final String ackId; + private transient PubSub pubsub; + private final PubSubOptions options; + + public static final class Builder extends Message.Builder { + + private final String subscription; + private final String ackId; + private final PubSub pubsub; + private final BuilderImpl delegate; + + private Builder(String subscription, String ackId, PubSub pubsub, BuilderImpl delegate) { + this.subscription = subscription; + this.ackId = ackId; + this.pubsub = pubsub; + this.delegate = delegate; + } + + @Override + Builder id(String id) { + delegate.id(id); + return this; + } + + @Override + public Builder payload(byte[] payload) { + delegate.payload(payload); + return this; + } + + @Override + public Builder addAttribute(String name, String value) { + delegate.addAttribute(name, value); + return this; + } + + @Override + public Builder removeAttribute(String name) { + removeAttribute(name); + return this; + } + + @Override + public Builder clearAttributes() { + delegate.clearAttributes(); + return this; + } + + @Override + Builder publishTime(Long publishTime) { + delegate.publishTime(publishTime); + return this; + } + + @Override + public ReceivedMessage build() { + return new ReceivedMessage(this); + } + } + + ReceivedMessage(Builder builder) { + super(builder.delegate); + subscription = checkNotNull(builder.subscription); + ackId = checkNotNull(builder.ackId); + pubsub = checkNotNull(builder.pubsub); + options = pubsub.options(); + } + + public Builder toBuilder() { + return new Builder(subscription, ackId, pubsub, new BuilderImpl(this)); + } + + @Override + public int hashCode() { + return Objects.hash(options, super.hashCode()); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + ReceivedMessage other = (ReceivedMessage) obj; + return Objects.equals(toPb(), other.toPb()) && Objects.equals(options, other.options); + } + + public PubSub pubSub() { + return pubsub; + } + + public String subscription() { + return subscription; + } + + public String ackId() { + return ackId; + } + + public void acknowledge() { + pubsub.acknowledge(subscription, ackId); + } + + public Future acknowledgeAsync() { + return pubsub.acknowledgeAsync(subscription, ackId); + } + + public void modifyAckDeadline(int deadline, TimeUnit unit) { + pubsub.modifyAckDeadline(subscription, deadline, unit, ackId); + } + + public Future modifyAckDeadlineAsync(int deadline, TimeUnit unit) { + return pubsub.modifyAckDeadlineAsync(subscription, deadline, unit, ackId); + } + + private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { + in.defaultReadObject(); + this.pubsub = options.service(); + } + + static ReceivedMessage fromPb(PubSub storage, String subscription, + com.google.pubsub.v1.ReceivedMessage msgPb) { + Message message = Message.fromPb(msgPb.getMessage()); + String ackId = msgPb.getAckId(); + return new Builder(subscription, ackId, storage, new BuilderImpl(message)).build(); + } +} diff --git a/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/Subscription.java b/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/Subscription.java index c14ae815b0e1..b7d02fc213c2 100644 --- a/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/Subscription.java +++ b/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/Subscription.java @@ -18,9 +18,13 @@ import static com.google.common.base.Preconditions.checkNotNull; +import com.google.gcloud.pubsub.PubSub.PullOption; + import java.io.IOException; import java.io.ObjectInputStream; +import java.util.List; import java.util.Objects; +import java.util.concurrent.Future; /** * PubSub subscription. @@ -102,9 +106,37 @@ public PubSub pubSub() { return pubsub; } - // Operations to add: - // delete, reload, modifyPushConfig? (instead of replace), pull + public boolean delete() { + return pubsub.deleteSubscription(name()); + } + + public Future deleteAsync() { + return pubsub.deleteSubscriptionAsync(name()); + } + + public Subscription reload() { + return pubsub.getSubscription(name()); + } + + public Future reloadAsync() { + return pubsub.getSubscriptionAsync(name()); + } + public void replacePushConfig(PushConfig pushConfig) { + pubsub.replacePushConfig(name(), pushConfig); + } + + public Future replacePushConfigAsync(PushConfig pushConfig) { + return pubsub.replacePushConfigAsync(name(), pushConfig); + } + + public List pull(PullOption... options) { + return pubsub.pull(name(), options); + } + + public Future> pullAsync(PullOption... options) { + return pubsub.pullAsync(name(), options); + } private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); diff --git a/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/SubscriptionInfo.java b/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/SubscriptionInfo.java index 2b42b4b8e2ea..6ffc3f026c5b 100644 --- a/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/SubscriptionInfo.java +++ b/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/SubscriptionInfo.java @@ -60,8 +60,8 @@ static final class BuilderImpl extends Builder { private int ackDeadlineSeconds; private BuilderImpl(String topic, String name) { - this.topic = topic; - this.name = name; + this.topic = checkNotNull(topic); + this.name = checkNotNull(name); } BuilderImpl(SubscriptionInfo subscription) { @@ -174,6 +174,10 @@ public static SubscriptionInfo of(String topic, String name) { return builder(topic, name).build(); } + public static SubscriptionInfo of(String topic, String name, String endpoint) { + return builder(topic, name).pushConfig(PushConfig.of(endpoint)).build(); + } + public static Builder builder(String topic, String name) { return new BuilderImpl(topic, name); } diff --git a/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/Topic.java b/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/Topic.java index 1520bf453dd5..585131e14ad1 100644 --- a/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/Topic.java +++ b/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/Topic.java @@ -18,9 +18,15 @@ import static com.google.common.base.Preconditions.checkNotNull; +import com.google.gcloud.AsyncPage; +import com.google.gcloud.Page; +import com.google.gcloud.pubsub.PubSub.ListOption; + import java.io.IOException; import java.io.ObjectInputStream; +import java.util.List; import java.util.Objects; +import java.util.concurrent.Future; /** * PubSub Topic. @@ -41,7 +47,7 @@ private Builder(Topic topic) { } @Override - public TopicInfo.Builder name(String name) { + public Builder name(String name) { delegate.name(name); return this; } @@ -84,9 +90,53 @@ public PubSub pubSub() { return pubsub; } - // Operations to add: - // delete, reload, publish, listSubscriptions for topic, + public boolean delete() { + return pubsub.deleteTopic(name()); + } + + public Future deleteAsync() { + return pubsub.deleteTopicAsync(name()); + } + + public Topic reload() { + return pubsub.getTopic(name()); + } + + public Future reloadAsync() { + return pubsub.getTopicAsync(name()); + } + + public String publish(Message message) { + return pubsub.publish(name(), message); + } + + public Future publishAsync(Message message) { + return pubsub.publishAsync(name(), message); + } + public List publish(Message message, Message... messages) { + return pubsub.publish(name(), message, messages); + } + + public Future> publishAsync(Message message, Message... messages) { + return pubsub.publishAsync(name(), message, messages); + } + + public List publish(Iterable messages) { + return pubsub.publish(name(), messages); + } + + public Future> publishAsync(Iterable messages) { + return pubsub.publishAsync(name(), messages); + } + + public Page listSubscriptions(ListOption... options) { + return pubsub.listSubscriptions(name(), options); + } + + public Future> listSubscriptionsAsync(ListOption... options) { + return pubsub.listSubscriptionsAsync(name(), options); + } private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); diff --git a/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/TopicInfo.java b/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/TopicInfo.java index cb1071e8aa15..e3381081c137 100644 --- a/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/TopicInfo.java +++ b/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/TopicInfo.java @@ -46,7 +46,8 @@ static final class BuilderImpl extends Builder { private String name; - BuilderImpl() { + BuilderImpl(String name) { + this.name = checkNotNull(name); } BuilderImpl(TopicInfo topicInfo) { @@ -99,14 +100,18 @@ com.google.pubsub.v1.Topic toPb() { } static TopicInfo fromPb(com.google.pubsub.v1.Topic topicPb) { - return builder().name(topicPb.getName()).build(); + return builder(topicPb.getName()).build(); } public Builder toBuilder() { return new BuilderImpl(this); } - public static Builder builder() { - return new BuilderImpl(); + public static TopicInfo of(String name) { + return builder(name).build(); + } + + public static Builder builder(String name) { + return new BuilderImpl(name); } } From 01ec8a3a735edebb7ce068ca6d96849a25072743 Mon Sep 17 00:00:00 2001 From: Arie Ozarov Date: Sun, 27 Mar 2016 21:05:22 -0700 Subject: [PATCH 04/10] Fix code review comments and add DefaultPubSubRpc --- .../google/{gcloud => cloud}/AsyncPage.java | 2 +- gcloud-java-pubsub/pom.xml | 1 + .../{gcloud => cloud}/pubsub/Message.java | 31 +-- .../{gcloud => cloud}/pubsub/PubSub.java | 8 +- .../pubsub/PubSubException.java | 8 +- .../pubsub/PubSubFactory.java | 4 +- .../{gcloud => cloud}/pubsub/PubSubImpl.java | 55 ++++-- .../pubsub/PubSubOptions.java | 21 ++- .../{gcloud => cloud}/pubsub/PushConfig.java | 2 +- .../pubsub/ReceivedMessage.java | 14 +- .../pubsub/Subscription.java | 10 +- .../pubsub/SubscriptionInfo.java | 9 +- .../{gcloud => cloud}/pubsub/Topic.java | 20 +- .../{gcloud => cloud}/pubsub/TopicInfo.java | 4 +- .../pubsub/package-info.java | 2 +- .../cloud/pubsub/spi/DefaultPubSubRpc.java | 177 ++++++++++++++++++ .../google/cloud/pubsub/spi/PubSubRpc.java | 71 +++++++ .../pubsub/spi/PubSubRpcFactory.java | 6 +- .../gcloud/pubsub/spi/DefaultPubSubRpc.java | 28 --- .../google/gcloud/pubsub/spi/PubSubRpc.java | 20 -- 20 files changed, 383 insertions(+), 110 deletions(-) rename gcloud-java-core/src/main/java/com/google/{gcloud => cloud}/AsyncPage.java (96%) rename gcloud-java-pubsub/src/main/java/com/google/{gcloud => cloud}/pubsub/Message.java (89%) rename gcloud-java-pubsub/src/main/java/com/google/{gcloud => cloud}/pubsub/PubSub.java (97%) rename gcloud-java-pubsub/src/main/java/com/google/{gcloud => cloud}/pubsub/PubSubException.java (90%) rename gcloud-java-pubsub/src/main/java/com/google/{gcloud => cloud}/pubsub/PubSubFactory.java (90%) rename gcloud-java-pubsub/src/main/java/com/google/{gcloud => cloud}/pubsub/PubSubImpl.java (74%) rename gcloud-java-pubsub/src/main/java/com/google/{gcloud => cloud}/pubsub/PubSubOptions.java (83%) rename gcloud-java-pubsub/src/main/java/com/google/{gcloud => cloud}/pubsub/PushConfig.java (99%) rename gcloud-java-pubsub/src/main/java/com/google/{gcloud => cloud}/pubsub/ReceivedMessage.java (93%) rename gcloud-java-pubsub/src/main/java/com/google/{gcloud => cloud}/pubsub/Subscription.java (92%) rename gcloud-java-pubsub/src/main/java/com/google/{gcloud => cloud}/pubsub/SubscriptionInfo.java (96%) rename gcloud-java-pubsub/src/main/java/com/google/{gcloud => cloud}/pubsub/Topic.java (87%) rename gcloud-java-pubsub/src/main/java/com/google/{gcloud => cloud}/pubsub/TopicInfo.java (97%) rename gcloud-java-pubsub/src/main/java/com/google/{gcloud => cloud}/pubsub/package-info.java (96%) create mode 100644 gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/spi/DefaultPubSubRpc.java create mode 100644 gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/spi/PubSubRpc.java rename gcloud-java-pubsub/src/main/java/com/google/{gcloud => cloud}/pubsub/spi/PubSubRpcFactory.java (86%) delete mode 100644 gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/spi/DefaultPubSubRpc.java delete mode 100644 gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/spi/PubSubRpc.java diff --git a/gcloud-java-core/src/main/java/com/google/gcloud/AsyncPage.java b/gcloud-java-core/src/main/java/com/google/cloud/AsyncPage.java similarity index 96% rename from gcloud-java-core/src/main/java/com/google/gcloud/AsyncPage.java rename to gcloud-java-core/src/main/java/com/google/cloud/AsyncPage.java index fea806fd7089..ea2905795e62 100644 --- a/gcloud-java-core/src/main/java/com/google/gcloud/AsyncPage.java +++ b/gcloud-java-core/src/main/java/com/google/cloud/AsyncPage.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud; +package com.google.cloud; import java.util.concurrent.Future; diff --git a/gcloud-java-pubsub/pom.xml b/gcloud-java-pubsub/pom.xml index c99fae934996..8724ed627e1c 100644 --- a/gcloud-java-pubsub/pom.xml +++ b/gcloud-java-pubsub/pom.xml @@ -4,6 +4,7 @@ gcloud-java-pubsub jar GCloud Java Pub/Sub + https://github.com/GoogleCloudPlatform/gcloud-java/tree/master/gcloud-java-pubsub Java idiomatic client for Google Cloud Pub/Sub. diff --git a/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/Message.java b/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/Message.java similarity index 89% rename from gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/Message.java rename to gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/Message.java index 0f3794abcb75..738b1bd0a4b4 100644 --- a/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/Message.java +++ b/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/Message.java @@ -12,7 +12,7 @@ * the License. */ -package com.google.gcloud.pubsub; +package com.google.cloud.pubsub; import static com.google.common.base.Preconditions.checkNotNull; @@ -41,12 +41,17 @@ public class Message implements Serializable { private final ImmutableMap attributes; private final Long publishTime; - abstract static class Builder { + /** + * Builder for Message. + */ + public abstract static class Builder { abstract Builder id(String id); public abstract Builder payload(byte[] payload); + public abstract Builder attributes(Map attributes); + public abstract Builder addAttribute(String name, String value); public abstract Builder removeAttribute(String name); @@ -58,10 +63,7 @@ abstract static class Builder { public abstract Message build(); } - /** - * Builder for Message. - */ - public static final class BuilderImpl extends Builder { + static final class BuilderImpl extends Builder { private String id; private byte[] payload; @@ -95,6 +97,12 @@ public Builder addAttribute(String name, String value) { return this; } + @Override + public Builder attributes(Map attributes) { + this.attributes = new HashMap<>(attributes); + return this; + } + @Override public Builder removeAttribute(String name) { attributes.remove(name); @@ -150,7 +158,7 @@ public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) { return false; } - return Objects.equals(toPb(), ((Message)o).toPb()); + return Objects.equals(toPb(), ((Message) o).toPb()); } @Override @@ -186,7 +194,7 @@ com.google.pubsub.v1.PubsubMessage toPb() { } static Message fromPb(com.google.pubsub.v1.PubsubMessage messagePb) { - Builder builder = builder(); + Builder builder = builder(messagePb.getData().toByteArray()); if (messagePb.hasPublishTime()) { Timestamp ts = messagePb.getPublishTime(); builder.publishTime( @@ -198,7 +206,6 @@ static Message fromPb(com.google.pubsub.v1.PubsubMessage messagePb) { for (Map.Entry entry : messagePb.getAttributes().entrySet()) { builder.addAttribute(entry.getKey(), entry.getValue()); } - builder.payload(messagePb.getData().toByteArray()); return builder.build(); } @@ -207,10 +214,10 @@ public Builder toBuilder() { } public static Message of(byte[] payload) { - return builder().payload(payload).build(); + return builder(payload).build(); } - public static Builder builder() { - return new BuilderImpl(); + public static Builder builder(byte[] payload) { + return new BuilderImpl().payload(payload); } } diff --git a/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/PubSub.java b/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/PubSub.java similarity index 97% rename from gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/PubSub.java rename to gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/PubSub.java index 69aea74e4613..21607cf8fff6 100644 --- a/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/PubSub.java +++ b/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/PubSub.java @@ -14,11 +14,11 @@ * limitations under the License. */ -package com.google.gcloud.pubsub; +package com.google.cloud.pubsub; -import com.google.gcloud.AsyncPage; -import com.google.gcloud.Page; -import com.google.gcloud.Service; +import com.google.cloud.AsyncPage; +import com.google.cloud.Page; +import com.google.cloud.Service; import java.io.Serializable; import java.util.List; diff --git a/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/PubSubException.java b/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/PubSubException.java similarity index 90% rename from gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/PubSubException.java rename to gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/PubSubException.java index 91f56ba1f35a..5ac8629a4afe 100644 --- a/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/PubSubException.java +++ b/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/PubSubException.java @@ -14,12 +14,12 @@ * limitations under the License. */ -package com.google.gcloud.pubsub; +package com.google.cloud.pubsub; +import com.google.cloud.BaseServiceException; +import com.google.cloud.RetryHelper.RetryHelperException; +import com.google.cloud.RetryHelper.RetryInterruptedException; import com.google.common.collect.ImmutableSet; -import com.google.gcloud.BaseServiceException; -import com.google.gcloud.RetryHelper.RetryHelperException; -import com.google.gcloud.RetryHelper.RetryInterruptedException; import java.util.Set; diff --git a/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/PubSubFactory.java b/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/PubSubFactory.java similarity index 90% rename from gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/PubSubFactory.java rename to gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/PubSubFactory.java index 024d68406982..8aa073f3a10a 100644 --- a/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/PubSubFactory.java +++ b/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/PubSubFactory.java @@ -14,9 +14,9 @@ * limitations under the License. */ -package com.google.gcloud.pubsub; +package com.google.cloud.pubsub; -import com.google.gcloud.ServiceFactory; +import com.google.cloud.ServiceFactory; /** * An interface for Pub/Sub factories. diff --git a/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/PubSubImpl.java b/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/PubSubImpl.java similarity index 74% rename from gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/PubSubImpl.java rename to gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/PubSubImpl.java index 5b20f921e76f..2fac193f5af8 100644 --- a/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/PubSubImpl.java +++ b/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/PubSubImpl.java @@ -14,14 +14,23 @@ * limitations under the License. */ -package com.google.gcloud.pubsub; +package com.google.cloud.pubsub; -import com.google.gcloud.AsyncPage; -import com.google.gcloud.BaseService; -import com.google.gcloud.Page; -import com.google.gcloud.pubsub.spi.PubSubRpc; +import static com.google.common.util.concurrent.Futures.lazyTransform; + +import com.google.cloud.AsyncPage; +import com.google.cloud.BaseService; +import com.google.cloud.Page; +import com.google.cloud.pubsub.spi.PubSubRpc; +import com.google.common.base.Function; +import com.google.common.base.Throwables; +import com.google.common.util.concurrent.Uninterruptibles; +import com.google.protobuf.Empty; +import com.google.pubsub.v1.DeleteTopicRequest; +import com.google.pubsub.v1.GetTopicRequest; import java.util.List; +import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; @@ -29,39 +38,55 @@ class PubSubImpl extends BaseService implements PubSub { private final PubSubRpc rpc; - public PubSubImpl(PubSubOptions options) { + PubSubImpl(PubSubOptions options) { super(options); rpc = options.rpc(); } + private static V get(Future future) { + try { + return Uninterruptibles.getUninterruptibly(future); + } catch (ExecutionException ex) { + // TODO: we should propagate PubSubException + throw Throwables.propagate(ex.getCause()); + } + } + @Override public Topic create(TopicInfo topic) { - return null; + return get(createAsync(topic)); } @Override public Future createAsync(TopicInfo topic) { - return null; + return lazyTransform(rpc.create(topic.toPb()), Topic.fromPbFunction(this)); } @Override public Topic getTopic(String topic) { - return null; + return get(getTopicAsync(topic)); } @Override public Future getTopicAsync(String topic) { - return null; + GetTopicRequest request = GetTopicRequest.newBuilder().setTopic(topic).build(); + return lazyTransform(rpc.get(request), Topic.fromPbFunction(this)); } @Override public boolean deleteTopic(String topic) { - return false; + return get(deleteTopicAsync(topic)); } @Override public Future deleteTopicAsync(String topic) { - return null; + DeleteTopicRequest request = DeleteTopicRequest.newBuilder().setTopic(topic).build(); + return lazyTransform(rpc.delete(request), new Function() { + @Override + public Boolean apply(Empty input) { + return true; + } + }); } @Override @@ -160,7 +185,8 @@ public Page listSubscriptions(String topic, ListOption... options) } @Override - public Future> listSubscriptionsAsync(String topic, ListOption... options) { + public Future> listSubscriptionsAsync(String topic, + ListOption... options) { return null; } @@ -213,7 +239,8 @@ public void modifyAckDeadline(String subscription, int deadline, TimeUnit unit, } @Override - public Future modifyAckDeadlineAsync(String subscription, int deadline, TimeUnit unit, Iterable ackIds) { + public Future modifyAckDeadlineAsync(String subscription, int deadline, TimeUnit unit, + Iterable ackIds) { return null; } } diff --git a/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/PubSubOptions.java b/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/PubSubOptions.java similarity index 83% rename from gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/PubSubOptions.java rename to gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/PubSubOptions.java index 54f97d7e5938..b71b431c8fa5 100644 --- a/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/PubSubOptions.java +++ b/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/PubSubOptions.java @@ -14,14 +14,17 @@ * limitations under the License. */ -package com.google.gcloud.pubsub; +package com.google.cloud.pubsub; +import static com.google.cloud.BaseServiceException.UNKNOWN_CODE; + +import com.google.cloud.ServiceOptions; +import com.google.cloud.pubsub.spi.DefaultPubSubRpc; +import com.google.cloud.pubsub.spi.PubSubRpc; +import com.google.cloud.pubsub.spi.PubSubRpcFactory; import com.google.common.collect.ImmutableSet; -import com.google.gcloud.ServiceOptions; -import com.google.gcloud.pubsub.spi.DefaultPubSubRpc; -import com.google.gcloud.pubsub.spi.PubSubRpc; -import com.google.gcloud.pubsub.spi.PubSubRpcFactory; +import java.io.IOException; import java.util.Set; public class PubSubOptions extends ServiceOptions { @@ -53,7 +56,13 @@ public static class DefaultPubSubRpcFactory implements PubSubRpcFactory { @Override public PubSubRpc create(PubSubOptions options) { - return new DefaultPubSubRpc(options); + try { + return new DefaultPubSubRpc(options); + } catch (IOException e) { + PubSubException exception = new PubSubException(UNKNOWN_CODE, e.getMessage()); + exception.initCause(e); + throw exception; + } } } diff --git a/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/PushConfig.java b/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/PushConfig.java similarity index 99% rename from gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/PushConfig.java rename to gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/PushConfig.java index 4f903c534cfe..61b64a07b36b 100644 --- a/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/PushConfig.java +++ b/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/PushConfig.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.pubsub; +package com.google.cloud.pubsub; import static com.google.common.base.Preconditions.checkNotNull; diff --git a/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/ReceivedMessage.java b/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/ReceivedMessage.java similarity index 93% rename from gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/ReceivedMessage.java rename to gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/ReceivedMessage.java index 92de5059f7cb..ac30920b062c 100644 --- a/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/ReceivedMessage.java +++ b/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/ReceivedMessage.java @@ -14,12 +14,13 @@ * limitations under the License. */ -package com.google.gcloud.pubsub; +package com.google.cloud.pubsub; import static com.google.common.base.Preconditions.checkNotNull; import java.io.IOException; import java.io.ObjectInputStream; +import java.util.Map; import java.util.Objects; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; @@ -59,6 +60,12 @@ public Builder payload(byte[] payload) { return this; } + @Override + public Builder attributes(Map attributes) { + delegate.attributes(attributes); + return this; + } + @Override public Builder addAttribute(String name, String value) { delegate.addAttribute(name, value); @@ -67,7 +74,7 @@ public Builder addAttribute(String name, String value) { @Override public Builder removeAttribute(String name) { - removeAttribute(name); + delegate.removeAttribute(name); return this; } @@ -97,6 +104,7 @@ public ReceivedMessage build() { options = pubsub.options(); } + @Override public Builder toBuilder() { return new Builder(subscription, ackId, pubsub, new BuilderImpl(this)); } @@ -153,7 +161,7 @@ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundE static ReceivedMessage fromPb(PubSub storage, String subscription, com.google.pubsub.v1.ReceivedMessage msgPb) { - Message message = Message.fromPb(msgPb.getMessage()); + Message message = fromPb(msgPb.getMessage()); String ackId = msgPb.getAckId(); return new Builder(subscription, ackId, storage, new BuilderImpl(message)).build(); } diff --git a/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/Subscription.java b/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/Subscription.java similarity index 92% rename from gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/Subscription.java rename to gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/Subscription.java index b7d02fc213c2..7f17ba22ed9f 100644 --- a/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/Subscription.java +++ b/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/Subscription.java @@ -14,11 +14,11 @@ * limitations under the License. */ -package com.google.gcloud.pubsub; +package com.google.cloud.pubsub; import static com.google.common.base.Preconditions.checkNotNull; -import com.google.gcloud.pubsub.PubSub.PullOption; +import com.google.cloud.pubsub.PubSub.PullOption; import java.io.IOException; import java.io.ObjectInputStream; @@ -31,6 +31,8 @@ */ public class Subscription extends SubscriptionInfo { + private static final long serialVersionUID = -4153366055659552230L; + private final PubSubOptions options; private transient PubSub pubsub; @@ -138,8 +140,8 @@ public Future> pullAsync(PullOption... options) { return pubsub.pullAsync(name(), options); } - private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { - in.defaultReadObject(); + private void readObject(ObjectInputStream input) throws IOException, ClassNotFoundException { + input.defaultReadObject(); this.pubsub = options.service(); } diff --git a/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/SubscriptionInfo.java b/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/SubscriptionInfo.java similarity index 96% rename from gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/SubscriptionInfo.java rename to gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/SubscriptionInfo.java index 6ffc3f026c5b..97e7b35becd9 100644 --- a/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/SubscriptionInfo.java +++ b/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/SubscriptionInfo.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.pubsub; +package com.google.cloud.pubsub; import static com.google.common.base.Preconditions.checkNotNull; @@ -71,26 +71,31 @@ private BuilderImpl(String topic, String name) { ackDeadlineSeconds = subscription.ackDeadlineSeconds; } + @Override public Builder name(String name) { this.name = checkNotNull(name); return this; } + @Override public Builder topic(String topic) { this.topic = checkNotNull(topic); return this; } + @Override public Builder pushConfig(PushConfig pushConfig) { this.pushConfig = pushConfig; return this; } + @Override public Builder ackDeadLineSeconds(int ackDeadlineSeconds) { this.ackDeadlineSeconds = ackDeadlineSeconds; return this; } + @Override public SubscriptionInfo build() { return new SubscriptionInfo(this); } @@ -127,7 +132,7 @@ public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) { return false; } - return Objects.equals(toPb(), ((SubscriptionInfo)o).toPb()); + return Objects.equals(toPb(), ((SubscriptionInfo) o).toPb()); } @Override diff --git a/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/Topic.java b/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/Topic.java similarity index 87% rename from gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/Topic.java rename to gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/Topic.java index 585131e14ad1..d79ba825323e 100644 --- a/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/Topic.java +++ b/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/Topic.java @@ -14,13 +14,14 @@ * limitations under the License. */ -package com.google.gcloud.pubsub; +package com.google.cloud.pubsub; import static com.google.common.base.Preconditions.checkNotNull; -import com.google.gcloud.AsyncPage; -import com.google.gcloud.Page; -import com.google.gcloud.pubsub.PubSub.ListOption; +import com.google.cloud.AsyncPage; +import com.google.cloud.Page; +import com.google.cloud.pubsub.PubSub.ListOption; +import com.google.common.base.Function; import java.io.IOException; import java.io.ObjectInputStream; @@ -33,6 +34,8 @@ */ public class Topic extends TopicInfo { + private static final long serialVersionUID = -2686692223763315944L; + private final PubSubOptions options; private transient PubSub pubsub; @@ -147,4 +150,13 @@ static Topic fromPb(PubSub storage, com.google.pubsub.v1.Topic topicPb) { TopicInfo topicInfo = TopicInfo.fromPb(topicPb); return new Topic(storage, new BuilderImpl(topicInfo)); } + + static Function fromPbFunction(final PubSub pubsub) { + return new Function() { + @Override + public Topic apply(com.google.pubsub.v1.Topic topicPb) { + return fromPb(pubsub, topicPb); + } + }; + } } diff --git a/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/TopicInfo.java b/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/TopicInfo.java similarity index 97% rename from gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/TopicInfo.java rename to gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/TopicInfo.java index e3381081c137..37f4ae289b6f 100644 --- a/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/TopicInfo.java +++ b/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/TopicInfo.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.pubsub; +package com.google.cloud.pubsub; import static com.google.common.base.Preconditions.checkNotNull; @@ -54,11 +54,13 @@ static final class BuilderImpl extends Builder { this.name = topicInfo.name; } + @Override public Builder name(String name) { this.name = checkNotNull(name); return this; } + @Override public TopicInfo build() { return new TopicInfo(this); } diff --git a/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/package-info.java b/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/package-info.java similarity index 96% rename from gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/package-info.java rename to gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/package-info.java index 4782f6927567..05dc8dcbf036 100644 --- a/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/package-info.java +++ b/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/package-info.java @@ -26,4 +26,4 @@ * @see Google Cloud Pub/Sub */ -package com.google.gcloud.pubsub; +package com.google.cloud.pubsub; diff --git a/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/spi/DefaultPubSubRpc.java b/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/spi/DefaultPubSubRpc.java new file mode 100644 index 000000000000..6856aafcd25c --- /dev/null +++ b/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/spi/DefaultPubSubRpc.java @@ -0,0 +1,177 @@ +/* + * 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.spi; + +import com.google.api.gax.core.ConnectionSettings; +import com.google.api.gax.core.RetrySettings; +import com.google.api.gax.grpc.ApiCallSettings; +import com.google.cloud.RetryParams; +import com.google.cloud.pubsub.PubSubOptions; +import com.google.cloud.pubsub.spi.v1.PublisherApi; +import com.google.cloud.pubsub.spi.v1.PublisherSettings; +import com.google.cloud.pubsub.spi.v1.SubscriberApi; +import com.google.cloud.pubsub.spi.v1.SubscriberSettings; +import com.google.protobuf.Empty; +import com.google.pubsub.v1.AcknowledgeRequest; +import com.google.pubsub.v1.DeleteSubscriptionRequest; +import com.google.pubsub.v1.DeleteTopicRequest; +import com.google.pubsub.v1.GetSubscriptionRequest; +import com.google.pubsub.v1.GetTopicRequest; +import com.google.pubsub.v1.ListSubscriptionsRequest; +import com.google.pubsub.v1.ListSubscriptionsResponse; +import com.google.pubsub.v1.ListTopicSubscriptionsRequest; +import com.google.pubsub.v1.ListTopicSubscriptionsResponse; +import com.google.pubsub.v1.ListTopicsRequest; +import com.google.pubsub.v1.ListTopicsResponse; +import com.google.pubsub.v1.ModifyAckDeadlineRequest; +import com.google.pubsub.v1.ModifyPushConfigRequest; +import com.google.pubsub.v1.PublishRequest; +import com.google.pubsub.v1.PublishResponse; +import com.google.pubsub.v1.PullRequest; +import com.google.pubsub.v1.PullResponse; +import com.google.pubsub.v1.Subscription; +import com.google.pubsub.v1.Topic; + +import org.joda.time.Duration; + +import java.io.IOException; +import java.util.concurrent.Future; + +public class DefaultPubSubRpc implements PubSubRpc { + + private final PubSubOptions options; + private final PublisherApi publisherApi; + private final SubscriberApi subscriberApi; + + public DefaultPubSubRpc(PubSubOptions options) throws IOException { + this.options = options; + try { + PublisherSettings.Builder pbuilder = PublisherSettings.defaultInstance().toBuilder(); + pbuilder.provideChannelWith(ConnectionSettings.builder() + .provideCredentialsWith(options.authCredentials().credentials()).build()); + pbuilder.applyToAllApiMethods(apiCallSettings(options)); + publisherApi = PublisherApi.create(pbuilder.build()); + SubscriberSettings.Builder sBuilder = SubscriberSettings.defaultInstance().toBuilder(); + sBuilder.provideChannelWith(ConnectionSettings.builder() + .provideCredentialsWith(options.authCredentials().credentials()).build()); + sBuilder.applyToAllApiMethods(apiCallSettings(options)); + subscriberApi = SubscriberApi.create(sBuilder.build()); + } catch (Exception ex) { + throw new IOException(ex); + } + } + + private static ApiCallSettings.Builder apiCallSettings(PubSubOptions options) { + // TODO: figure out how to specify timeout these settings + // retryParams.retryMaxAttempts(), retryParams.retryMinAttempts() + RetryParams retryParams = options.retryParams(); + final RetrySettings.Builder builder = RetrySettings.newBuilder() + .setTotalTimeout(Duration.millis(retryParams.totalRetryPeriodMillis())) + .setInitialRpcTimeout(Duration.millis(options.connectTimeout())) + .setRpcTimeoutMultiplier(1.5) + .setMaxRpcTimeout((Duration.millis(options.connectTimeout() + options.readTimeout()))) + .setInitialRetryDelay(Duration.millis(retryParams.initialRetryDelayMillis())) + .setRetryDelayMultiplier(retryParams.retryDelayBackoffFactor()) + .setMaxRetryDelay(Duration.millis(retryParams.maxRetryDelayMillis())); + // TODO: this needs to be replaced with something like ApiCallSettings.of(null, retrySettings) + // once the gax supports it + return new ApiCallSettings.Builder() { + + @Override + public RetrySettings.Builder getRetrySettingsBuilder() { + return builder; + } + + @Override + public ApiCallSettings build() { + return null; + } + }; + } + + @Override + public Future create(Topic topic) { + // TODO: understand what the exception that could be thrown + // and how to get either retriable and/or the service error codes + return publisherApi.createTopicCallable().futureCall(topic); + } + + @Override + public Future publish(PublishRequest request) { + return publisherApi.publishCallable().futureCall(request); + } + + @Override + public Future get(GetTopicRequest request) { + return publisherApi.getTopicCallable().futureCall(request); + } + + @Override + public Future list(ListTopicsRequest request) { + return publisherApi.listTopicsCallable().futureCall(request); + } + + @Override + public Future list(ListTopicSubscriptionsRequest request) { + return publisherApi.listTopicSubscriptionsCallable().futureCall(request); + } + + @Override + public Future delete(DeleteTopicRequest request) { + return publisherApi.deleteTopicCallable().futureCall(request); + } + + @Override + public Future create(Subscription subscription) { + return subscriberApi.createSubscriptionCallable().futureCall(subscription); + } + + @Override + public Future get(GetSubscriptionRequest request) { + return subscriberApi.getSubscriptionCallable().futureCall(request); + } + + @Override + public Future list(ListSubscriptionsRequest request) { + return subscriberApi.listSubscriptionsCallable().futureCall(request); + } + + @Override + public Future delete(DeleteSubscriptionRequest request) { + return subscriberApi.deleteSubscriptionCallable().futureCall(request); + } + + @Override + public Future modify(ModifyAckDeadlineRequest request) { + return subscriberApi.modifyAckDeadlineCallable().futureCall(request); + } + + @Override + public Future acknowledge(AcknowledgeRequest request) { + return subscriberApi.acknowledgeCallable().futureCall(request); + } + + @Override + public Future pull(PullRequest request) { + return subscriberApi.pullCallable().futureCall(request); + } + + @Override + public Future modify(ModifyPushConfigRequest request) { + return subscriberApi.modifyPushConfigCallable().futureCall(request); + } +} diff --git a/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/spi/PubSubRpc.java b/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/spi/PubSubRpc.java new file mode 100644 index 000000000000..e286c1bfebd9 --- /dev/null +++ b/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/spi/PubSubRpc.java @@ -0,0 +1,71 @@ +/* + * 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.spi; + +import com.google.protobuf.Empty; +import com.google.pubsub.v1.AcknowledgeRequest; +import com.google.pubsub.v1.DeleteSubscriptionRequest; +import com.google.pubsub.v1.DeleteTopicRequest; +import com.google.pubsub.v1.GetSubscriptionRequest; +import com.google.pubsub.v1.GetTopicRequest; +import com.google.pubsub.v1.ListSubscriptionsRequest; +import com.google.pubsub.v1.ListSubscriptionsResponse; +import com.google.pubsub.v1.ListTopicSubscriptionsRequest; +import com.google.pubsub.v1.ListTopicSubscriptionsResponse; +import com.google.pubsub.v1.ListTopicsRequest; +import com.google.pubsub.v1.ListTopicsResponse; +import com.google.pubsub.v1.ModifyAckDeadlineRequest; +import com.google.pubsub.v1.ModifyPushConfigRequest; +import com.google.pubsub.v1.PublishRequest; +import com.google.pubsub.v1.PublishResponse; +import com.google.pubsub.v1.PullRequest; +import com.google.pubsub.v1.PullResponse; +import com.google.pubsub.v1.Subscription; +import com.google.pubsub.v1.Topic; + +import java.util.concurrent.Future; + +public interface PubSubRpc { + + Future create(Topic topic); + + Future publish(PublishRequest request); + + Future get(GetTopicRequest request); + + Future list(ListTopicsRequest request); + + Future list(ListTopicSubscriptionsRequest request); + + Future delete(DeleteTopicRequest request); + + Future create(Subscription subscription); + + Future get(GetSubscriptionRequest request); + + Future list(ListSubscriptionsRequest request); + + Future delete(DeleteSubscriptionRequest request); + + Future modify(ModifyAckDeadlineRequest request); + + Future acknowledge(AcknowledgeRequest request); + + Future pull(PullRequest request); + + Future modify(ModifyPushConfigRequest request); +} diff --git a/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/spi/PubSubRpcFactory.java b/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/spi/PubSubRpcFactory.java similarity index 86% rename from gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/spi/PubSubRpcFactory.java rename to gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/spi/PubSubRpcFactory.java index 308efe8c9995..d3648a68399f 100644 --- a/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/spi/PubSubRpcFactory.java +++ b/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/spi/PubSubRpcFactory.java @@ -14,10 +14,10 @@ * limitations under the License. */ -package com.google.gcloud.pubsub.spi; +package com.google.cloud.pubsub.spi; -import com.google.gcloud.pubsub.PubSubOptions; -import com.google.gcloud.spi.ServiceRpcFactory; +import com.google.cloud.pubsub.PubSubOptions; +import com.google.cloud.spi.ServiceRpcFactory; /** * An interface for Pub/Sub RPC factory. diff --git a/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/spi/DefaultPubSubRpc.java b/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/spi/DefaultPubSubRpc.java deleted file mode 100644 index b1d705c87e54..000000000000 --- a/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/spi/DefaultPubSubRpc.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * 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.gcloud.pubsub.spi; - -import com.google.gcloud.pubsub.PubSubOptions; - -public class DefaultPubSubRpc implements PubSubRpc { - - private final PubSubOptions options; - - public DefaultPubSubRpc(PubSubOptions options) { - this.options = options; - } -} diff --git a/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/spi/PubSubRpc.java b/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/spi/PubSubRpc.java deleted file mode 100644 index 4f0d26a96119..000000000000 --- a/gcloud-java-pubsub/src/main/java/com/google/gcloud/pubsub/spi/PubSubRpc.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * 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.gcloud.pubsub.spi; - -public interface PubSubRpc { -} From 46af358887c3d034fc272841fb5f9a2eae1a0b05 Mon Sep 17 00:00:00 2001 From: Arie Ozarov Date: Tue, 12 Apr 2016 20:13:54 -0700 Subject: [PATCH 05/10] - Fix some codacy issues - Make messages consistent with proto defaults - Rename acknowledge to ack - Add nack - Add PullCallback --- .../java/com/google/cloud/pubsub/Message.java | 31 ++++++-------- .../java/com/google/cloud/pubsub/PubSub.java | 41 +++++++++++-------- .../com/google/cloud/pubsub/PubSubImpl.java | 38 ++++++++++++++--- .../google/cloud/pubsub/ReceivedMessage.java | 22 ++++++---- .../com/google/cloud/pubsub/Subscription.java | 11 ++++- .../java/com/google/cloud/pubsub/Topic.java | 4 +- .../cloud/pubsub/spi/DefaultPubSubRpc.java | 2 +- 7 files changed, 99 insertions(+), 50 deletions(-) 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 738b1bd0a4b4..4e28a7f6ae87 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 @@ -16,11 +16,11 @@ import static com.google.common.base.Preconditions.checkNotNull; -import com.google.api.client.util.Strings; import com.google.common.base.MoreObjects; import com.google.common.collect.ImmutableMap; import com.google.protobuf.ByteString; import com.google.protobuf.Timestamp; +import com.google.pubsub.v1.PubsubMessage; import java.io.Serializable; import java.util.HashMap; @@ -58,14 +58,14 @@ public abstract static class Builder { public abstract Builder clearAttributes(); - abstract Builder publishTime(Long publishTime); + abstract Builder publishTime(long publishTime); public abstract Message build(); } static final class BuilderImpl extends Builder { - private String id; + private String id = ""; private byte[] payload; private Map attributes = new HashMap<>(); private Long publishTime; @@ -81,7 +81,7 @@ private BuilderImpl() {} @Override BuilderImpl id(String id) { - this.id = id; + this.id = checkNotNull(id); return this; } @@ -116,7 +116,7 @@ public Builder clearAttributes() { } @Override - Builder publishTime(Long publishTime) { + Builder publishTime(long publishTime) { this.publishTime = publishTime; return this; } @@ -176,33 +176,28 @@ public String toString() { .toString(); } - com.google.pubsub.v1.PubsubMessage toPb() { - com.google.pubsub.v1.PubsubMessage.Builder builder = - com.google.pubsub.v1.PubsubMessage.newBuilder(); + PubsubMessage toPb() { + PubsubMessage.Builder builder = PubsubMessage.newBuilder(); if (id != null) { builder.setMessageId(id); } builder.setData(payload); builder.getAttributes().putAll(attributes); - if (publishTime != null) { - Timestamp.Builder tsBuilder = Timestamp.newBuilder(); - tsBuilder.setSeconds(publishTime / MILLIS_PER_SECOND); - tsBuilder.setNanos((int) (publishTime % MILLIS_PER_SECOND * NANOS_PER_MILLISECOND)); - builder.setPublishTime(tsBuilder); - } + Timestamp.Builder tsBuilder = Timestamp.newBuilder(); + tsBuilder.setSeconds(publishTime / MILLIS_PER_SECOND); + tsBuilder.setNanos((int) (publishTime % MILLIS_PER_SECOND * NANOS_PER_MILLISECOND)); + builder.setPublishTime(tsBuilder); return builder.build(); } - static Message fromPb(com.google.pubsub.v1.PubsubMessage messagePb) { + static Message fromPb(PubsubMessage messagePb) { Builder builder = builder(messagePb.getData().toByteArray()); if (messagePb.hasPublishTime()) { Timestamp ts = messagePb.getPublishTime(); builder.publishTime( ts.getSeconds() * MILLIS_PER_SECOND + ts.getNanos() / NANOS_PER_MILLISECOND); } - if (Strings.isNullOrEmpty(messagePb.getMessageId())) { - builder.id(messagePb.getMessageId()); - } + builder.id(messagePb.getMessageId()); for (Map.Entry entry : messagePb.getAttributes().entrySet()) { builder.addAttribute(entry.getKey(), entry.getValue()); } diff --git a/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/PubSub.java b/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/PubSub.java index 21607cf8fff6..f53541bc9182 100644 --- a/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/PubSub.java +++ b/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/PubSub.java @@ -98,6 +98,14 @@ public static PullOption maxMessages(int maxMessages) { } } + /** + * A callback to process pulled messages. + * The message will be ack'ed upon successful return or nack'ed if exception is thrown. + */ + interface PullCallback { + void process(Message message) throws Exception; + } + Topic create(TopicInfo topic); Future createAsync(TopicInfo topic); @@ -154,22 +162,29 @@ public static PullOption maxMessages(int maxMessages) { Future> listSubscriptionsAsync(String topic, ListOption... options); - // Possible Ack options: - // 1) return a "special" iterator with "ack-iterated-messages" - // 2) provide a way to pull with callback(Message) - ask messages that were called successfully - // - // Also, consider auto-renewable for all messages that were pulled and not acked. List pull(String subscription, PullOption... options); - Future> pullAsync(String subscription, PullOption... options); + Future> pullAsync(String subscription, PullOption... options); + + void pull(String subscription, PullCallback callback, PullOption... options); + + void pullAsync(String subscription, PullCallback callback, PullOption... options); + + void ack(String subscription, String ackId, String... ackIds); - void acknowledge(String subscription, String ackId, String... ackIds); + Future ackAsync(String subscription, String ackId, String... ackIds); - Future acknowledgeAsync(String subscription, String ackId, String... ackIds); + void ack(String subscription, Iterable ackIds); - void acknowledge(String subscription, Iterable ackIds); + Future ackAsync(String subscription, Iterable ackIds); - Future acknowledgeAsync(String subscription, Iterable ackIds); + void nack(String subscription, String ackId, String... ackIds); + + Future nackAsync(String subscription, String ackId, String... ackIds); + + void nack(String subscription, Iterable ackIds); + + Future nackAsync(String subscription, Iterable ackIds); void modifyAckDeadline(String subscription, int deadline, TimeUnit unit, String ackId, String... ackIds); @@ -182,12 +197,6 @@ Future modifyAckDeadlineAsync(String subscription, int deadline, TimeUnit Future modifyAckDeadlineAsync(String subscription, int deadline, TimeUnit unit, Iterable ackIds); - // Note regarding Data types: - // 1) No field selection - // --- This is why there are no options for getters - // 2) Never null for primitive values or collections - // --- should we replace default "" with null (e.g. id when not populated)? - // IAM Policy operations: getPolicy, replacePolicy, testPermissions // Not sure if ready (docs is not up-to-date) // Looks like policy is per resource (topic or subscription) but not per service? diff --git a/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/PubSubImpl.java b/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/PubSubImpl.java index 2fac193f5af8..18b1b9f98576 100644 --- a/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/PubSubImpl.java +++ b/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/PubSubImpl.java @@ -196,27 +196,55 @@ public List pull(String subscription, PullOption... options) { } @Override - public Future> pullAsync(String subscription, PullOption... options) { + public Future> pullAsync(String subscription, PullOption... options) { return null; } @Override - public void acknowledge(String subscription, String ackId, String... ackIds) { + public void pull(String subscription, PullCallback callback, PullOption... options) { } @Override - public Future acknowledgeAsync(String subscription, String ackId, String... ackIds) { + public void pullAsync(String subscription, PullCallback callback, PullOption... options) { + + } + + @Override + public void ack(String subscription, String ackId, String... ackIds) { + } + + @Override + public Future ackAsync(String subscription, String ackId, String... ackIds) { + return null; + } + + @Override + public void ack(String subscription, Iterable ackIds) { + + } + + @Override + public Future ackAsync(String subscription, Iterable ackIds) { + return null; + } + + @Override + public void nack(String subscription, String ackId, String... ackIds) { + } + + @Override + public Future nackAsync(String subscription, String ackId, String... ackIds) { return null; } @Override - public void acknowledge(String subscription, Iterable ackIds) { + public void nack(String subscription, Iterable ackIds) { } @Override - public Future acknowledgeAsync(String subscription, Iterable ackIds) { + public Future nackAsync(String subscription, Iterable ackIds) { return null; } diff --git a/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/ReceivedMessage.java b/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/ReceivedMessage.java index ac30920b062c..c72ff209a097 100644 --- a/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/ReceivedMessage.java +++ b/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/ReceivedMessage.java @@ -85,7 +85,7 @@ public Builder clearAttributes() { } @Override - Builder publishTime(Long publishTime) { + Builder publishTime(long publishTime) { delegate.publishTime(publishTime); return this; } @@ -138,12 +138,20 @@ public String ackId() { return ackId; } - public void acknowledge() { - pubsub.acknowledge(subscription, ackId); + public void ack() { + pubsub.ack(subscription, ackId); } - public Future acknowledgeAsync() { - return pubsub.acknowledgeAsync(subscription, ackId); + public Future ackAsync() { + return pubsub.ackAsync(subscription, ackId); + } + + public void nack() { + pubsub.nack(subscription, ackId); + } + + public Future nackAsync() { + return pubsub.nackAsync(subscription, ackId); } public void modifyAckDeadline(int deadline, TimeUnit unit) { @@ -154,8 +162,8 @@ public Future modifyAckDeadlineAsync(int deadline, TimeUnit unit) { return pubsub.modifyAckDeadlineAsync(subscription, deadline, unit, ackId); } - private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { - in.defaultReadObject(); + private void readObject(ObjectInputStream input) throws IOException, ClassNotFoundException { + input.defaultReadObject(); this.pubsub = options.service(); } diff --git a/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/Subscription.java b/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/Subscription.java index 7f17ba22ed9f..a1b256171721 100644 --- a/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/Subscription.java +++ b/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/Subscription.java @@ -18,6 +18,7 @@ import static com.google.common.base.Preconditions.checkNotNull; +import com.google.cloud.pubsub.PubSub.PullCallback; import com.google.cloud.pubsub.PubSub.PullOption; import java.io.IOException; @@ -136,10 +137,18 @@ public List pull(PullOption... options) { return pubsub.pull(name(), options); } - public Future> pullAsync(PullOption... options) { + public Future> pullAsync(PullOption... options) { return pubsub.pullAsync(name(), options); } + public void pull(PullCallback callback, PullOption... options) { + pubsub.pull(name(), callback, options); + } + + public void pullAsync(PullCallback callback, PullOption... options) { + pubsub.pull(name(), callback, options); + } + private void readObject(ObjectInputStream input) throws IOException, ClassNotFoundException { input.defaultReadObject(); this.pubsub = options.service(); diff --git a/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/Topic.java b/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/Topic.java index d79ba825323e..77dd31fcd876 100644 --- a/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/Topic.java +++ b/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/Topic.java @@ -141,8 +141,8 @@ public Future> listSubscriptionsAsync(ListOption... opti return pubsub.listSubscriptionsAsync(name(), options); } - private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { - in.defaultReadObject(); + private void readObject(ObjectInputStream input) throws IOException, ClassNotFoundException { + input.defaultReadObject(); this.pubsub = options.service(); } diff --git a/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/spi/DefaultPubSubRpc.java b/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/spi/DefaultPubSubRpc.java index 6856aafcd25c..a8b6391b4f11 100644 --- a/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/spi/DefaultPubSubRpc.java +++ b/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/spi/DefaultPubSubRpc.java @@ -83,7 +83,7 @@ private static ApiCallSettings.Builder apiCallSettings(PubSubOptions options) { .setTotalTimeout(Duration.millis(retryParams.totalRetryPeriodMillis())) .setInitialRpcTimeout(Duration.millis(options.connectTimeout())) .setRpcTimeoutMultiplier(1.5) - .setMaxRpcTimeout((Duration.millis(options.connectTimeout() + options.readTimeout()))) + .setMaxRpcTimeout(Duration.millis(options.connectTimeout() + options.readTimeout())) .setInitialRetryDelay(Duration.millis(retryParams.initialRetryDelayMillis())) .setRetryDelayMultiplier(retryParams.retryDelayBackoffFactor()) .setMaxRetryDelay(Duration.millis(retryParams.maxRetryDelayMillis())); From 0b918ba9a66cdf89c9ebf6a7238a068789677ae2 Mon Sep 17 00:00:00 2001 From: Arie Ozarov Date: Tue, 19 Apr 2016 17:40:20 -0700 Subject: [PATCH 06/10] applying code review comments --- .../java/com/google/cloud/pubsub/PubSub.java | 23 +++++++++++++------ .../com/google/cloud/pubsub/PubSubImpl.java | 14 ++++------- .../com/google/cloud/pubsub/Subscription.java | 17 ++++++-------- 3 files changed, 28 insertions(+), 26 deletions(-) diff --git a/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/PubSub.java b/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/PubSub.java index f53541bc9182..ecf7bfd0cdce 100644 --- a/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/PubSub.java +++ b/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/PubSub.java @@ -21,6 +21,7 @@ import com.google.cloud.Service; import java.io.Serializable; +import java.util.Iterator; import java.util.List; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; @@ -89,7 +90,7 @@ Object value() { return value; } - public static PullOption returnImmediatly() { + public static PullOption returnImmediately() { return new PullOption(Option.RETURN_IMMEDIATELY, true); } @@ -102,10 +103,20 @@ public static PullOption maxMessages(int maxMessages) { * A callback to process pulled messages. * The message will be ack'ed upon successful return or nack'ed if exception is thrown. */ - interface PullCallback { + interface MessageProcessor { void process(Message message) throws Exception; } + /** + * An interface to control asynchronous pulling. + */ + interface MessageConsumer extends AutoCloseable { + + void start(); + + void stop(); + } + Topic create(TopicInfo topic); Future createAsync(TopicInfo topic); @@ -162,13 +173,11 @@ interface PullCallback { Future> listSubscriptionsAsync(String topic, ListOption... options); - List pull(String subscription, PullOption... options); - - Future> pullAsync(String subscription, PullOption... options); + Iterator pull(String subscription, PullOption... options); - void pull(String subscription, PullCallback callback, PullOption... options); + Future> pullAsync(String subscription, PullOption... options); - void pullAsync(String subscription, PullCallback callback, PullOption... options); + MessageConsumer pullAsync(String subscription, MessageProcessor callback); void ack(String subscription, String ackId, String... ackIds); diff --git a/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/PubSubImpl.java b/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/PubSubImpl.java index 18b1b9f98576..cc19a1d53672 100644 --- a/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/PubSubImpl.java +++ b/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/PubSubImpl.java @@ -29,6 +29,7 @@ import com.google.pubsub.v1.DeleteTopicRequest; import com.google.pubsub.v1.GetTopicRequest; +import java.util.Iterator; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; @@ -191,23 +192,18 @@ public Future> listSubscriptionsAsync(String topic, } @Override - public List pull(String subscription, PullOption... options) { + public Iterator pull(String subscription, PullOption... options) { return null; } @Override - public Future> pullAsync(String subscription, PullOption... options) { + public Future> pullAsync(String subscription, PullOption... options) { return null; } @Override - public void pull(String subscription, PullCallback callback, PullOption... options) { - - } - - @Override - public void pullAsync(String subscription, PullCallback callback, PullOption... options) { - + public MessageConsumer pullAsync(String subscription, MessageProcessor callback) { + return null; } @Override diff --git a/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/Subscription.java b/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/Subscription.java index a1b256171721..d49328ad7486 100644 --- a/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/Subscription.java +++ b/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/Subscription.java @@ -18,12 +18,13 @@ import static com.google.common.base.Preconditions.checkNotNull; -import com.google.cloud.pubsub.PubSub.PullCallback; +import com.google.cloud.pubsub.PubSub.MessageConsumer; +import com.google.cloud.pubsub.PubSub.MessageProcessor; import com.google.cloud.pubsub.PubSub.PullOption; import java.io.IOException; import java.io.ObjectInputStream; -import java.util.List; +import java.util.Iterator; import java.util.Objects; import java.util.concurrent.Future; @@ -133,20 +134,16 @@ public Future replacePushConfigAsync(PushConfig pushConfig) { return pubsub.replacePushConfigAsync(name(), pushConfig); } - public List pull(PullOption... options) { + public Iterator pull(PullOption... options) { return pubsub.pull(name(), options); } - public Future> pullAsync(PullOption... options) { + public Future> pullAsync(PullOption... options) { return pubsub.pullAsync(name(), options); } - public void pull(PullCallback callback, PullOption... options) { - pubsub.pull(name(), callback, options); - } - - public void pullAsync(PullCallback callback, PullOption... options) { - pubsub.pull(name(), callback, options); + public MessageConsumer pullAsync(MessageProcessor callback) { + return pubsub.pullAsync(name(), callback); } private void readObject(ObjectInputStream input) throws IOException, ClassNotFoundException { From fb6562eab334d7c5a1c87cbc244edb822fc6e590 Mon Sep 17 00:00:00 2001 From: aozarov Date: Thu, 21 Apr 2016 17:38:20 -0700 Subject: [PATCH 07/10] add ByteArray --- gcloud-java-core/pom.xml | 5 + .../main/java/com/google/cloud/ByteArray.java | 165 ++++++++++++++++++ .../java/com/google/cloud/pubsub/Message.java | 59 +++++-- .../google/cloud/pubsub/ReceivedMessage.java | 10 +- 4 files changed, 225 insertions(+), 14 deletions(-) create mode 100644 gcloud-java-core/src/main/java/com/google/cloud/ByteArray.java diff --git a/gcloud-java-core/pom.xml b/gcloud-java-core/pom.xml index 22648d2e6ffa..611caf9fbfd2 100644 --- a/gcloud-java-core/pom.xml +++ b/gcloud-java-core/pom.xml @@ -98,5 +98,10 @@ 3.4 test
+ + com.google.protobuf + protobuf-java + 3.0.0-beta-1 +
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 new file mode 100644 index 000000000000..f23fb0876b41 --- /dev/null +++ b/gcloud-java-core/src/main/java/com/google/cloud/ByteArray.java @@ -0,0 +1,165 @@ +/* + * Copyright 2015 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 com.google.common.base.MoreObjects; +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; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.Iterator; + +/** + * An immutable byte array holder. + */ +public class ByteArray implements Iterable, Serializable { + + private static final long serialVersionUID = -1908809133893782840L; + private final ByteString byteString; + + protected ByteArray(ByteString byteString) { + this.byteString = byteString; + } + + protected ByteArray(ByteArray byteArray) { + this.byteString = byteArray.byteString(); + } + + @Override + public final Iterator iterator() { + return byteString.iterator(); + } + + @Override + public String toString() { + ToStringHelper toStringHelper = MoreObjects.toStringHelper(this); + StringBuilder stBuilder = new StringBuilder(); + for (int i = 0; i < Math.min(256, byteString.size()); i++) { + stBuilder.append(String.format("%02x", byteString.byteAt(i))); + } + if (byteString.size() > 256) { + stBuilder.append("..."); + } + return toStringHelper.add("bytes", stBuilder.toString()).toString(); + } + + @Override + public final int hashCode() { + return byteString.hashCode(); + } + + @Override + public final boolean equals(Object obj) { + return obj == this + || obj instanceof ByteArray && byteString.equals(((ByteArray) obj).byteString); + } + + /** + * Returns the size of this blob. + */ + public final int length() { + return byteString.size(); + } + + /** + * Returns a copy as byte array. + */ + public final byte[] toByteArray() { + return byteString.toByteArray(); + } + + /** + * Returns the content as {@code UTF-8} string. + */ + public final String toStringUtf8() { + return byteString.toStringUtf8(); + } + + /** + * Returns a read-only {@link ByteBuffer} for this blob content. + */ + public final ByteBuffer asReadOnlyByteBuffer() { + return byteString.asReadOnlyByteBuffer(); + } + + /** + * Returns an {@link InputStream} for this blob content. + */ + public final InputStream asInputStream() { + final ByteBuffer byteBuffer = asReadOnlyByteBuffer(); + return new InputStream() { + @Override public int read() { + return !byteBuffer.hasRemaining() ? -1 : byteBuffer.get() & 0xFF; + } + }; + } + + protected ByteString byteString() { + return byteString; + } + + /** + * Copies bytes into a 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 + */ + public final void copyTo(ByteBuffer target) { + byteString.copyTo(target); + } + + /** + * Copies bytes into a buffer. + * + * @throws IndexOutOfBoundsException if an offset or size is negative or too large + */ + public final void copyTo(byte[] target) { + byteString.copyTo(target, 0, 0, length()); + } + + public static final ByteArray copyFrom(byte[] bytes) { + return new ByteArray(ByteString.copyFrom(bytes)); + } + + /** + * Copy the bytes using {@code UTF-8} decoding. + */ + public static final ByteArray copyFrom(String string) { + return new ByteArray(ByteString.copyFrom(string, StandardCharsets.UTF_8)); + } + + public static final 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()); + } +} 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 4e28a7f6ae87..a5e1da75bc1c 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 @@ -16,6 +16,7 @@ import static com.google.common.base.Preconditions.checkNotNull; +import com.google.cloud.ByteArray; import com.google.common.base.MoreObjects; import com.google.common.collect.ImmutableMap; import com.google.protobuf.ByteString; @@ -37,10 +38,27 @@ public class Message implements Serializable { private static final long MILLIS_PER_SECOND = 1000; private final String id; - private final ByteString payload; + private final InternalByteArray payload; private final ImmutableMap attributes; private final Long publishTime; + private static final class InternalByteArray extends ByteArray { + + private static final long serialVersionUID = -3330181485911805428L; + + protected InternalByteArray(ByteString byteString) { + super(byteString); + } + + protected InternalByteArray(ByteArray byteArray) { + super(byteArray); + } + + protected ByteString byteString() { + return super.byteString(); + } + } + /** * Builder for Message. */ @@ -48,7 +66,9 @@ public abstract static class Builder { abstract Builder id(String id); - public abstract Builder payload(byte[] payload); + public abstract Builder payload(String payload); + + public abstract Builder payload(ByteArray payload); public abstract Builder attributes(Map attributes); @@ -66,7 +86,7 @@ public abstract static class Builder { static final class BuilderImpl extends Builder { private String id = ""; - private byte[] payload; + private ByteArray payload; private Map attributes = new HashMap<>(); private Long publishTime; @@ -74,7 +94,7 @@ private BuilderImpl() {} BuilderImpl(Message message) { id = message.id; - payload = message.payload.toByteArray(); + payload = message.payload; attributes = new HashMap<>(message.attributes); publishTime = message.publishTime; } @@ -86,8 +106,13 @@ BuilderImpl id(String id) { } @Override - public Builder payload(byte[] payload) { - this.payload = checkNotNull(payload); + public Builder payload(String payload) { + return payload(ByteArray.copyFrom(payload)); + } + + @Override + public Builder payload(ByteArray payload) { + this.payload = payload; return this; } @@ -129,7 +154,7 @@ public Message build() { Message(BuilderImpl builder) { id = builder.id; - payload = ByteString.copyFrom(checkNotNull(builder.payload)); + payload = new InternalByteArray(checkNotNull(builder.payload)); attributes = ImmutableMap.copyOf(builder.attributes); publishTime = builder.publishTime; } @@ -146,8 +171,12 @@ public String id() { return id; } - public byte[] payload() { - return payload.toByteArray(); + public String payloadAsString() { + return payload.toStringUtf8(); + } + + public ByteArray payload() { + return payload; } @Override @@ -181,7 +210,7 @@ PubsubMessage toPb() { if (id != null) { builder.setMessageId(id); } - builder.setData(payload); + builder.setData(payload.byteString()); builder.getAttributes().putAll(attributes); Timestamp.Builder tsBuilder = Timestamp.newBuilder(); tsBuilder.setSeconds(publishTime / MILLIS_PER_SECOND); @@ -191,7 +220,7 @@ PubsubMessage toPb() { } static Message fromPb(PubsubMessage messagePb) { - Builder builder = builder(messagePb.getData().toByteArray()); + Builder builder = builder(new InternalByteArray(messagePb.getData())); if (messagePb.hasPublishTime()) { Timestamp ts = messagePb.getPublishTime(); builder.publishTime( @@ -208,11 +237,15 @@ public Builder toBuilder() { return new BuilderImpl(this); } - public static Message of(byte[] payload) { + public static Message of(String payload) { return builder(payload).build(); } - public static Builder builder(byte[] payload) { + public static Builder builder(String payload) { + return new BuilderImpl().payload(payload); + } + + public static Builder builder(ByteArray payload) { return new BuilderImpl().payload(payload); } } diff --git a/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/ReceivedMessage.java b/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/ReceivedMessage.java index c72ff209a097..c3cb705cb6d7 100644 --- a/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/ReceivedMessage.java +++ b/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/ReceivedMessage.java @@ -18,6 +18,8 @@ import static com.google.common.base.Preconditions.checkNotNull; +import com.google.cloud.ByteArray; + import java.io.IOException; import java.io.ObjectInputStream; import java.util.Map; @@ -55,7 +57,13 @@ Builder id(String id) { } @Override - public Builder payload(byte[] payload) { + public Builder payload(String payload) { + delegate.payload(payload); + return this; + } + + @Override + public Builder payload(ByteArray payload) { delegate.payload(payload); return this; } From 72b853383f812778f3b4e1b03b5af0b6bb36a7ca Mon Sep 17 00:00:00 2001 From: aozarov Date: Fri, 22 Apr 2016 16:04:01 -0700 Subject: [PATCH 08/10] translate exceptions and add more info regarding pull --- gcloud-java-core/pom.xml | 5 ++ .../google/cloud/BaseServiceException.java | 11 +++ .../java/com/google/cloud/pubsub/PubSub.java | 37 +++++++-- .../google/cloud/pubsub/PubSubException.java | 38 +++------ .../com/google/cloud/pubsub/PubSubImpl.java | 7 +- .../google/cloud/pubsub/PubSubOptions.java | 9 +-- .../cloud/pubsub/spi/DefaultPubSubRpc.java | 77 +++++++++++-------- .../google/cloud/pubsub/spi/PubSubRpc.java | 1 + 8 files changed, 111 insertions(+), 74 deletions(-) diff --git a/gcloud-java-core/pom.xml b/gcloud-java-core/pom.xml index 611caf9fbfd2..a2f9b40ad71e 100644 --- a/gcloud-java-core/pom.xml +++ b/gcloud-java-core/pom.xml @@ -103,5 +103,10 @@ protobuf-java 3.0.0-beta-1 + + com.google.api + gax + 0.0.11 + diff --git a/gcloud-java-core/src/main/java/com/google/cloud/BaseServiceException.java b/gcloud-java-core/src/main/java/com/google/cloud/BaseServiceException.java index 6dc87f4abb3e..a2a1486f8dce 100644 --- a/gcloud-java-core/src/main/java/com/google/cloud/BaseServiceException.java +++ b/gcloud-java-core/src/main/java/com/google/cloud/BaseServiceException.java @@ -18,6 +18,7 @@ import com.google.api.client.googleapis.json.GoogleJsonError; import com.google.api.client.googleapis.json.GoogleJsonResponseException; +import com.google.api.gax.grpc.ApiException; import com.google.common.base.MoreObjects; import java.io.IOException; @@ -143,6 +144,16 @@ public BaseServiceException(int code, String message, String reason, boolean ide this.debugInfo = null; } + public BaseServiceException(ApiException apiException, boolean idempotent) { + super(apiException.getMessage(), apiException); + this.code = apiException.getStatusCode().value(); + this.reason = apiException.getStatusCode().name(); + this.idempotent = idempotent; + this.retryable = apiException.isRetryable(); + this.location = null; + this.debugInfo = null; + } + protected Set retryableErrors() { return Collections.emptySet(); } diff --git a/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/PubSub.java b/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/PubSub.java index ecf7bfd0cdce..f79c7f570e16 100644 --- a/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/PubSub.java +++ b/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/PubSub.java @@ -74,7 +74,7 @@ final class PullOption implements Serializable { private final Object value; enum Option { - RETURN_IMMEDIATELY, MAX_MESSAGES + MAX_MESSAGES } private PullOption(Option option, Object value) { @@ -90,10 +90,6 @@ Object value() { return value; } - public static PullOption returnImmediately() { - return new PullOption(Option.RETURN_IMMEDIATELY, true); - } - public static PullOption maxMessages(int maxMessages) { return new PullOption(Option.MAX_MESSAGES, maxMessages); } @@ -108,11 +104,38 @@ interface MessageProcessor { } /** - * An interface to control asynchronous pulling. + * An interface to control message consumer settings. */ interface MessageConsumer extends AutoCloseable { - void start(); + final class PullOption implements Serializable { + + private final Option option; + private final Object value; + + enum Option { + MAX_CONCURRENT_CALLBACKS + } + + private PullOption(Option option, Object value) { + this.option = option; + this.value = value; + } + + Option option() { + return option; + } + + Object value() { + return value; + } + + public static PullOption maxConcurrentCallbacks(int maxConcurrency) { + return new PullOption(Option.MAX_CONCURRENT_CALLBACKS, maxConcurrency); + } + } + + void start(MessageConsumer.PullOption... options); void stop(); } diff --git a/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/PubSubException.java b/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/PubSubException.java index 5ac8629a4afe..0ff6fa7e56c3 100644 --- a/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/PubSubException.java +++ b/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/PubSubException.java @@ -16,11 +16,10 @@ package com.google.cloud.pubsub; +import com.google.api.gax.grpc.ApiException; import com.google.cloud.BaseServiceException; -import com.google.cloud.RetryHelper.RetryHelperException; -import com.google.cloud.RetryHelper.RetryInterruptedException; -import com.google.common.collect.ImmutableSet; +import java.io.IOException; import java.util.Set; /** @@ -28,35 +27,20 @@ * * @see Google Cloud Pub/Sub error codes */ -public class PubSubException extends BaseServiceException { +public final class PubSubException extends BaseServiceException { private static final long serialVersionUID = 6434989638600001226L; - private static final Set RETRYABLE_ERRORS = ImmutableSet.of( - new Error(499, null), - new Error(503, null), - new Error(429, null), - new Error(500, null), - new Error(504, null)); - - public PubSubException(int code, String message) { - super(code, message, null, true); + + public PubSubException(IOException ex, boolean idempotent) { + super(ex, idempotent); } - @Override - protected Set retryableErrors() { - return RETRYABLE_ERRORS; + public PubSubException(ApiException apiException, boolean idempotent) { + super(apiException, idempotent); } - /** - * Translate RetryHelperException to the ResourceManagerException that caused the error. This - * method will always throw an exception. - * - * @throws PubSubException when {@code ex} was caused by a {@code - * ResourceManagerException} - * @throws RetryInterruptedException when {@code ex} is a {@code RetryInterruptedException} - */ - static PubSubException translateAndThrow(RetryHelperException ex) { - BaseServiceException.translateAndPropagateIfPossible(ex); - throw new PubSubException(UNKNOWN_CODE, ex.getMessage()); + @Override + protected Set retryableErrors() { + return null; } } diff --git a/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/PubSubImpl.java b/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/PubSubImpl.java index cc19a1d53672..bc9e428b6ab1 100644 --- a/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/PubSubImpl.java +++ b/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/PubSubImpl.java @@ -48,7 +48,6 @@ private static V get(Future future) { try { return Uninterruptibles.getUninterruptibly(future); } catch (ExecutionException ex) { - // TODO: we should propagate PubSubException throw Throwables.propagate(ex.getCause()); } } @@ -193,16 +192,22 @@ public Future> listSubscriptionsAsync(String topic, @Override public Iterator pull(String subscription, PullOption... options) { + // this should set return_immediately to true return null; } @Override public Future> pullAsync(String subscription, PullOption... options) { + // though this method can set return_immediately to false (as future can be canceled) I + // suggest to keep it false so sync could delegate to asyc and use the same options + // this method also should use the VTKIT thread-pool to renew ack deadline for non consumed + // messages return null; } @Override public MessageConsumer pullAsync(String subscription, MessageProcessor callback) { + // this method should use the VTKIT thread-pool (maybe getting it should be part of the spi) return null; } diff --git a/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/PubSubOptions.java b/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/PubSubOptions.java index b71b431c8fa5..73482ccef25f 100644 --- a/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/PubSubOptions.java +++ b/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/PubSubOptions.java @@ -16,8 +16,6 @@ package com.google.cloud.pubsub; -import static com.google.cloud.BaseServiceException.UNKNOWN_CODE; - import com.google.cloud.ServiceOptions; import com.google.cloud.pubsub.spi.DefaultPubSubRpc; import com.google.cloud.pubsub.spi.PubSubRpc; @@ -51,17 +49,14 @@ public static PubSubOptions defaultInstance() { } public static class DefaultPubSubRpcFactory implements PubSubRpcFactory { - private static final PubSubRpcFactory INSTANCE = - new DefaultPubSubRpcFactory(); + private static final PubSubRpcFactory INSTANCE = new DefaultPubSubRpcFactory(); @Override public PubSubRpc create(PubSubOptions options) { try { return new DefaultPubSubRpc(options); } catch (IOException e) { - PubSubException exception = new PubSubException(UNKNOWN_CODE, e.getMessage()); - exception.initCause(e); - throw exception; + throw new PubSubException(e, true); } } } diff --git a/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/spi/DefaultPubSubRpc.java b/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/spi/DefaultPubSubRpc.java index a8b6391b4f11..93aa44549d33 100644 --- a/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/spi/DefaultPubSubRpc.java +++ b/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/spi/DefaultPubSubRpc.java @@ -19,12 +19,17 @@ import com.google.api.gax.core.ConnectionSettings; import com.google.api.gax.core.RetrySettings; import com.google.api.gax.grpc.ApiCallSettings; +import com.google.api.gax.grpc.ApiException; import com.google.cloud.RetryParams; +import com.google.cloud.pubsub.PubSubException; import com.google.cloud.pubsub.PubSubOptions; import com.google.cloud.pubsub.spi.v1.PublisherApi; import com.google.cloud.pubsub.spi.v1.PublisherSettings; import com.google.cloud.pubsub.spi.v1.SubscriberApi; import com.google.cloud.pubsub.spi.v1.SubscriberSettings; +import com.google.common.base.Function; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; import com.google.protobuf.Empty; import com.google.pubsub.v1.AcknowledgeRequest; import com.google.pubsub.v1.DeleteSubscriptionRequest; @@ -51,22 +56,24 @@ import java.io.IOException; import java.util.concurrent.Future; +import io.grpc.Status.Code; + public class DefaultPubSubRpc implements PubSubRpc { - private final PubSubOptions options; private final PublisherApi publisherApi; private final SubscriberApi subscriberApi; public DefaultPubSubRpc(PubSubOptions options) throws IOException { - this.options = options; try { + // Provide (and use a common thread-pool). + // This depends on https://github.com/googleapis/gax-java/issues/73 PublisherSettings.Builder pbuilder = PublisherSettings.defaultInstance().toBuilder(); - pbuilder.provideChannelWith(ConnectionSettings.builder() + pbuilder.provideChannelWith(ConnectionSettings.newBuilder() .provideCredentialsWith(options.authCredentials().credentials()).build()); pbuilder.applyToAllApiMethods(apiCallSettings(options)); publisherApi = PublisherApi.create(pbuilder.build()); SubscriberSettings.Builder sBuilder = SubscriberSettings.defaultInstance().toBuilder(); - sBuilder.provideChannelWith(ConnectionSettings.builder() + sBuilder.provideChannelWith(ConnectionSettings.newBuilder() .provideCredentialsWith(options.authCredentials().credentials()).build()); sBuilder.applyToAllApiMethods(apiCallSettings(options)); subscriberApi = SubscriberApi.create(sBuilder.build()); @@ -76,7 +83,7 @@ public DefaultPubSubRpc(PubSubOptions options) throws IOException { } private static ApiCallSettings.Builder apiCallSettings(PubSubOptions options) { - // TODO: figure out how to specify timeout these settings + // TODO: specify timeout these settings: // retryParams.retryMaxAttempts(), retryParams.retryMinAttempts() RetryParams retryParams = options.retryParams(); final RetrySettings.Builder builder = RetrySettings.newBuilder() @@ -87,91 +94,97 @@ private static ApiCallSettings.Builder apiCallSettings(PubSubOptions options) { .setInitialRetryDelay(Duration.millis(retryParams.initialRetryDelayMillis())) .setRetryDelayMultiplier(retryParams.retryDelayBackoffFactor()) .setMaxRetryDelay(Duration.millis(retryParams.maxRetryDelayMillis())); - // TODO: this needs to be replaced with something like ApiCallSettings.of(null, retrySettings) - // once the gax supports it - return new ApiCallSettings.Builder() { - - @Override - public RetrySettings.Builder getRetrySettingsBuilder() { - return builder; - } + return ApiCallSettings.newBuilder().setRetrySettingsBuilder(builder); + } + private static Future translate(ListenableFuture from, final boolean idempotent, + final int... returnNullOn) { + return Futures.catching(from, ApiException.class, new Function() { @Override - public ApiCallSettings build() { - return null; + public V apply(ApiException exception) { + throw new PubSubException(exception, idempotent); } - }; + }); } @Override public Future create(Topic topic) { - // TODO: understand what the exception that could be thrown - // and how to get either retriable and/or the service error codes - return publisherApi.createTopicCallable().futureCall(topic); + // TODO: it would be nice if we can get the idempotent inforamtion from the ApiCallSettings + // or from the exception + return translate(publisherApi.createTopicCallable().futureCall(topic), true); } @Override public Future publish(PublishRequest request) { - return publisherApi.publishCallable().futureCall(request); + return translate(publisherApi.publishCallable().futureCall(request), false); } @Override public Future get(GetTopicRequest request) { - return publisherApi.getTopicCallable().futureCall(request); + return translate(publisherApi.getTopicCallable().futureCall(request), true, + Code.NOT_FOUND.value()); } @Override public Future list(ListTopicsRequest request) { - return publisherApi.listTopicsCallable().futureCall(request); + // we should consider using gax PageAccessor once + // https://github.com/googleapis/gax-java/issues/74 is fixed + // Though it is a cleaner SPI without it, but PageAccessor is an interface + // and if it saves code we should not easily dismiss it. + return translate(publisherApi.listTopicsCallable().futureCall(request), true); } @Override public Future list(ListTopicSubscriptionsRequest request) { - return publisherApi.listTopicSubscriptionsCallable().futureCall(request); + return translate(publisherApi.listTopicSubscriptionsCallable().futureCall(request), true); } @Override public Future delete(DeleteTopicRequest request) { - return publisherApi.deleteTopicCallable().futureCall(request); + // TODO: check if null is not going to work for Empty + return translate(publisherApi.deleteTopicCallable().futureCall(request), true, + Code.NOT_FOUND.value()); } @Override public Future create(Subscription subscription) { - return subscriberApi.createSubscriptionCallable().futureCall(subscription); + return translate(subscriberApi.createSubscriptionCallable().futureCall(subscription), false); } @Override public Future get(GetSubscriptionRequest request) { - return subscriberApi.getSubscriptionCallable().futureCall(request); + return translate(subscriberApi.getSubscriptionCallable().futureCall(request), true, + Code.NOT_FOUND.value()); } @Override public Future list(ListSubscriptionsRequest request) { - return subscriberApi.listSubscriptionsCallable().futureCall(request); + return translate(subscriberApi.listSubscriptionsCallable().futureCall(request), true); } @Override public Future delete(DeleteSubscriptionRequest request) { - return subscriberApi.deleteSubscriptionCallable().futureCall(request); + return translate(subscriberApi.deleteSubscriptionCallable().futureCall(request), true, + Code.NOT_FOUND.value()); } @Override public Future modify(ModifyAckDeadlineRequest request) { - return subscriberApi.modifyAckDeadlineCallable().futureCall(request); + return translate(subscriberApi.modifyAckDeadlineCallable().futureCall(request), false); } @Override public Future acknowledge(AcknowledgeRequest request) { - return subscriberApi.acknowledgeCallable().futureCall(request); + return translate(subscriberApi.acknowledgeCallable().futureCall(request), false); } @Override public Future pull(PullRequest request) { - return subscriberApi.pullCallable().futureCall(request); + return translate(subscriberApi.pullCallable().futureCall(request), false); } @Override public Future modify(ModifyPushConfigRequest request) { - return subscriberApi.modifyPushConfigCallable().futureCall(request); + return translate(subscriberApi.modifyPushConfigCallable().futureCall(request), false); } } diff --git a/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/spi/PubSubRpc.java b/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/spi/PubSubRpc.java index e286c1bfebd9..8474ba042234 100644 --- a/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/spi/PubSubRpc.java +++ b/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/spi/PubSubRpc.java @@ -41,6 +41,7 @@ public interface PubSubRpc { + // in all cases root cause of ExecutionException is PubSubException Future create(Topic topic); Future publish(PublishRequest request); From 55df976448b165fe154682053561a09346b64314 Mon Sep 17 00:00:00 2001 From: aozarov Date: Mon, 25 Apr 2016 09:49:02 -0700 Subject: [PATCH 09/10] consider returnNullOn before throwing --- .../com/google/cloud/pubsub/spi/DefaultPubSubRpc.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/spi/DefaultPubSubRpc.java b/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/spi/DefaultPubSubRpc.java index 93aa44549d33..ebe83841a4ac 100644 --- a/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/spi/DefaultPubSubRpc.java +++ b/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/spi/DefaultPubSubRpc.java @@ -54,8 +54,10 @@ import org.joda.time.Duration; import java.io.IOException; +import java.util.Set; import java.util.concurrent.Future; +import autovalue.shaded.com.google.common.common.collect.Sets; import io.grpc.Status.Code; public class DefaultPubSubRpc implements PubSubRpc { @@ -98,10 +100,17 @@ private static ApiCallSettings.Builder apiCallSettings(PubSubOptions options) { } private static Future translate(ListenableFuture from, final boolean idempotent, - final int... returnNullOn) { + int... returnNullOn) { + final Set returnNullOnSet = Sets.newHashSetWithExpectedSize(returnNullOn.length); + for (int value : returnNullOn) { + returnNullOnSet.add(value); + } return Futures.catching(from, ApiException.class, new Function() { @Override public V apply(ApiException exception) { + if (returnNullOnSet.contains(exception.getStatusCode().value())) { + return null; + } throw new PubSubException(exception, idempotent); } }); From 22455ae3e21df62c0ddfbc8b3bc5aae20f40a887 Mon Sep 17 00:00:00 2001 From: Marco Ziccardi Date: Thu, 28 Apr 2016 15:14:03 +0200 Subject: [PATCH 10/10] Fix pubsub-related codacy issues --- .../src/main/java/com/google/cloud/pubsub/Message.java | 1 + .../src/main/java/com/google/cloud/pubsub/PubSub.java | 2 ++ 2 files changed, 3 insertions(+) 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 a5e1da75bc1c..830f9115a41d 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 @@ -54,6 +54,7 @@ protected InternalByteArray(ByteArray byteArray) { super(byteArray); } + @Override protected ByteString byteString() { return super.byteString(); } diff --git a/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/PubSub.java b/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/PubSub.java index f79c7f570e16..2695cd0064e8 100644 --- a/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/PubSub.java +++ b/gcloud-java-pubsub/src/main/java/com/google/cloud/pubsub/PubSub.java @@ -110,6 +110,8 @@ interface MessageConsumer extends AutoCloseable { final class PullOption implements Serializable { + private static final long serialVersionUID = 4792164134340316582L; + private final Option option; private final Object value;