
com.google.cloud.pubsub.PubSubImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gcloud-java-pubsub Show documentation
Show all versions of gcloud-java-pubsub Show documentation
Java idiomatic client for Google Cloud Pub/Sub.
The newest version!
/*
* Copyright 2016 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.cloud.pubsub;
import static com.google.cloud.pubsub.PubSub.ListOption.OptionType.PAGE_SIZE;
import static com.google.cloud.pubsub.PubSub.ListOption.OptionType.PAGE_TOKEN;
import static com.google.cloud.pubsub.PubSub.PullOption.OptionType.EXECUTOR_FACTORY;
import static com.google.cloud.pubsub.PubSub.PullOption.OptionType.MAX_QUEUED_CALLBACKS;
import static com.google.common.base.Preconditions.checkArgument;
import com.google.cloud.AsyncPage;
import com.google.cloud.AsyncPageImpl;
import com.google.cloud.BaseService;
import com.google.cloud.Page;
import com.google.cloud.PageImpl;
import com.google.cloud.pubsub.spi.PubSubRpc;
import com.google.cloud.pubsub.spi.PubSubRpc.PullFuture;
import com.google.cloud.pubsub.spi.v1.PublisherApi;
import com.google.cloud.pubsub.spi.v1.SubscriberApi;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Iterators;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.Uninterruptibles;
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 java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
class PubSubImpl extends BaseService implements PubSub {
private final PubSubRpc rpc;
private final AckDeadlineRenewer ackDeadlineRenewer;
private boolean closed;
private static final Function EMPTY_TO_VOID_FUNCTION = new Function() {
@Override
public Void apply(Empty empty) {
return null;
}
};
private static final Function EMPTY_TO_BOOLEAN_FUNCTION =
new Function() {
@Override
public Boolean apply(Empty input) {
return input != null;
}
};
private static final Function
MESSAGE_TO_ACK_ID_FUNCTION = new Function() {
@Override
public String apply(com.google.pubsub.v1.ReceivedMessage message) {
return message.getAckId();
}
};
PubSubImpl(PubSubOptions options) {
super(options);
rpc = options.rpc();
ackDeadlineRenewer = new AckDeadlineRenewer(this);
}
@VisibleForTesting
PubSubImpl(PubSubOptions options, AckDeadlineRenewer ackDeadlineRenewer) {
super(options);
rpc = options.rpc();
this.ackDeadlineRenewer = ackDeadlineRenewer;
}
private abstract static class BasePageFetcher implements AsyncPageImpl.NextPageFetcher {
private static final long serialVersionUID = -2122989557125999209L;
private final PubSubOptions serviceOptions;
private final Map requestOptions;
private BasePageFetcher(PubSubOptions serviceOptions, String cursor,
Map requestOptions) {
this.serviceOptions = serviceOptions;
this.requestOptions =
PageImpl.nextRequestOptions(PAGE_TOKEN, cursor, requestOptions);
}
PubSubOptions serviceOptions() {
return serviceOptions;
}
Map requestOptions() {
return requestOptions;
}
}
private static class TopicPageFetcher extends BasePageFetcher {
private static final long serialVersionUID = -7153536453427361814L;
TopicPageFetcher(PubSubOptions serviceOptions, String cursor,
Map requestOptions) {
super(serviceOptions, cursor, requestOptions);
}
@Override
public Future> nextPage() {
return listTopicsAsync(serviceOptions(), requestOptions());
}
}
private static class SubscriptionPageFetcher extends BasePageFetcher {
private static final long serialVersionUID = -5634446170301177992L;
SubscriptionPageFetcher(PubSubOptions serviceOptions, String cursor,
Map requestOptions) {
super(serviceOptions, cursor, requestOptions);
}
@Override
public Future> nextPage() {
return listSubscriptionsAsync(serviceOptions(), requestOptions());
}
}
private static class SubscriptionNamePageFetcher extends BasePageFetcher {
private static final long serialVersionUID = 7250525437694464444L;
private final String topic;
SubscriptionNamePageFetcher(String topic, PubSubOptions serviceOptions, String cursor,
Map requestOptions) {
super(serviceOptions, cursor, requestOptions);
this.topic = topic;
}
@Override
public Future> nextPage() {
return listSubscriptionsAsync(topic, serviceOptions(), requestOptions());
}
}
private static V get(Future future) {
try {
return Uninterruptibles.getUninterruptibly(future);
} catch (ExecutionException ex) {
throw Throwables.propagate(ex.getCause());
}
}
private static Future transform(Future future,
Function super I, ? extends O> function) {
if (future instanceof ListenableFuture) {
return Futures.transform((ListenableFuture) future, function);
}
return Futures.lazyTransform(future, function);
}
@Override
public Topic create(TopicInfo topic) {
return get(createAsync(topic));
}
@Override
public Future createAsync(TopicInfo topic) {
return transform(rpc.create(topic.toPb(options().projectId())), Topic.fromPbFunction(this));
}
@Override
public Topic getTopic(String topic) {
return get(getTopicAsync(topic));
}
@Override
public Future getTopicAsync(String topic) {
GetTopicRequest request = GetTopicRequest.newBuilder()
.setTopic(PublisherApi.formatTopicName(options().projectId(), topic))
.build();
return transform(rpc.get(request), Topic.fromPbFunction(this));
}
@Override
public boolean deleteTopic(String topic) {
return get(deleteTopicAsync(topic));
}
@Override
public Future deleteTopicAsync(String topic) {
DeleteTopicRequest request = DeleteTopicRequest.newBuilder()
.setTopic(PublisherApi.formatTopicName(options().projectId(), topic))
.build();
return transform(rpc.delete(request), EMPTY_TO_BOOLEAN_FUNCTION);
}
private static ListTopicsRequest listTopicsRequest(PubSubOptions serviceOptions,
Map options) {
ListTopicsRequest.Builder builder = ListTopicsRequest.newBuilder();
builder.setProject(SubscriberApi.formatProjectName(serviceOptions.projectId()));
Integer pageSize = PAGE_SIZE.get(options);
String pageToken = PAGE_TOKEN.get(options);
if (pageSize != null) {
builder.setPageSize(pageSize);
}
if (pageToken != null) {
builder.setPageToken(pageToken);
}
return builder.build();
}
private static Future> listTopicsAsync(final PubSubOptions serviceOptions,
final Map options) {
final ListTopicsRequest request = listTopicsRequest(serviceOptions, options);
Future list = serviceOptions.rpc().list(request);
return transform(list, new Function>() {
@Override
public AsyncPage apply(ListTopicsResponse listTopicsResponse) {
List topics = listTopicsResponse.getTopicsList() == null ? ImmutableList.of()
: Lists.transform(listTopicsResponse.getTopicsList(),
Topic.fromPbFunction(serviceOptions.service()));
String cursor = listTopicsResponse.getNextPageToken().equals("") ? null
: listTopicsResponse.getNextPageToken();
return new AsyncPageImpl<>(
new TopicPageFetcher(serviceOptions, cursor, options), cursor, topics);
}
});
}
@Override
public Page listTopics(ListOption... options) {
return get(listTopicsAsync(options));
}
@Override
public Future> listTopicsAsync(ListOption... options) {
return listTopicsAsync(options(), optionMap(options));
}
@Override
public String publish(String topic, Message message) {
return get(publishAsync(topic, message));
}
private static PublishRequest publishRequest(PubSubOptions serviceOptions, String topic,
Iterable messages) {
PublishRequest.Builder builder = PublishRequest.newBuilder();
builder.setTopic(PublisherApi.formatTopicName(serviceOptions.projectId(), topic));
builder.addAllMessages(Iterables.transform(messages, Message.TO_PB_FUNCTION));
return builder.build();
}
@Override
public Future publishAsync(String topic, Message message) {
return transform(
rpc.publish(publishRequest(options(), topic, Collections.singletonList(message))),
new Function() {
@Override
public String apply(PublishResponse publishResponse) {
return publishResponse.getMessageIdsList().get(0);
}
});
}
@Override
public List publish(String topic, Message message, Message... messages) {
return publish(topic, Lists.asList(message, messages));
}
@Override
public Future> publishAsync(String topic, Message message, Message... messages) {
return publishAsync(topic, Lists.asList(message, messages));
}
@Override
public List publish(String topic, Iterable messages) {
return get(publishAsync(topic, messages));
}
@Override
public Future> publishAsync(String topic, Iterable messages) {
return transform(rpc.publish(publishRequest(options(), topic, messages)),
new Function>() {
@Override
public List apply(PublishResponse publishResponse) {
return publishResponse.getMessageIdsList();
}
});
}
@Override
public Subscription create(SubscriptionInfo subscription) {
return get(createAsync(subscription));
}
@Override
public Future createAsync(SubscriptionInfo subscription) {
return transform(rpc.create(subscription.toPb(options().projectId())),
Subscription.fromPbFunction(this));
}
@Override
public Subscription getSubscription(String subscription) {
return get(getSubscriptionAsync(subscription));
}
@Override
public Future getSubscriptionAsync(String subscription) {
GetSubscriptionRequest request = GetSubscriptionRequest.newBuilder()
.setSubscription(SubscriberApi.formatSubscriptionName(options().projectId(), subscription))
.build();
return transform(rpc.get(request), Subscription.fromPbFunction(this));
}
@Override
public void replacePushConfig(String subscription, PushConfig pushConfig) {
get(replacePushConfigAsync(subscription, pushConfig));
}
@Override
public Future replacePushConfigAsync(String subscription, PushConfig pushConfig) {
ModifyPushConfigRequest request = ModifyPushConfigRequest.newBuilder()
.setSubscription(SubscriberApi.formatSubscriptionName(options().projectId(), subscription))
.setPushConfig(pushConfig != null ? pushConfig.toPb()
: com.google.pubsub.v1.PushConfig.getDefaultInstance())
.build();
return transform(rpc.modify(request), EMPTY_TO_VOID_FUNCTION);
}
@Override
public boolean deleteSubscription(String subscription) {
return get(deleteSubscriptionAsync(subscription));
}
@Override
public Future deleteSubscriptionAsync(String subscription) {
DeleteSubscriptionRequest request = DeleteSubscriptionRequest.newBuilder()
.setSubscription(SubscriberApi.formatSubscriptionName(options().projectId(), subscription))
.build();
return transform(rpc.delete(request), EMPTY_TO_BOOLEAN_FUNCTION);
}
private static ListSubscriptionsRequest listSubscriptionsRequest(PubSubOptions serviceOptions,
Map options) {
ListSubscriptionsRequest.Builder builder = ListSubscriptionsRequest.newBuilder();
builder.setProject(SubscriberApi.formatProjectName(serviceOptions.projectId()));
Integer pageSize = PAGE_SIZE.getInteger(options);
String pageToken = PAGE_TOKEN.getString(options);
if (pageSize != null) {
builder.setPageSize(pageSize);
}
if (pageToken != null) {
builder.setPageToken(pageToken);
}
return builder.build();
}
private static Future> listSubscriptionsAsync(
final PubSubOptions serviceOptions, final Map options) {
final ListSubscriptionsRequest request = listSubscriptionsRequest(serviceOptions, options);
Future list = serviceOptions.rpc().list(request);
return transform(list, new Function>() {
@Override
public AsyncPage apply(ListSubscriptionsResponse listSubscriptionsResponse) {
List subscriptions = listSubscriptionsResponse.getSubscriptionsList() == null
? ImmutableList.of()
: Lists.transform(listSubscriptionsResponse.getSubscriptionsList(),
Subscription.fromPbFunction(serviceOptions.service()));
String cursor = listSubscriptionsResponse.getNextPageToken().equals("") ? null
: listSubscriptionsResponse.getNextPageToken();
return new AsyncPageImpl<>(new SubscriptionPageFetcher(serviceOptions, cursor, options),
cursor, subscriptions);
}
});
}
@Override
public Page listSubscriptions(ListOption... options) {
return get(listSubscriptionsAsync(options));
}
public Future> listSubscriptionsAsync(ListOption... options) {
return listSubscriptionsAsync(options(), optionMap(options));
}
private static ListTopicSubscriptionsRequest listSubscriptionsRequest(String topic,
PubSubOptions serviceOptions, Map options) {
ListTopicSubscriptionsRequest.Builder builder = ListTopicSubscriptionsRequest.newBuilder();
builder.setTopic(PublisherApi.formatTopicName(serviceOptions.projectId(), topic));
Integer pageSize = PAGE_SIZE.getInteger(options);
String pageToken = PAGE_TOKEN.getString(options);
if (pageSize != null) {
builder.setPageSize(pageSize);
}
if (pageToken != null) {
builder.setPageToken(pageToken);
}
return builder.build();
}
private static Future> listSubscriptionsAsync(final String topic,
final PubSubOptions serviceOptions, final Map options) {
final ListTopicSubscriptionsRequest request =
listSubscriptionsRequest(topic, serviceOptions, options);
Future list = serviceOptions.rpc().list(request);
return transform(list,
new Function>() {
@Override
public AsyncPage apply(
ListTopicSubscriptionsResponse listSubscriptionsResponse) {
List subscriptions =
listSubscriptionsResponse.getSubscriptionsList() == null
? ImmutableList.of()
: Lists.transform(listSubscriptionsResponse.getSubscriptionsList(),
new Function() {
@Override
public SubscriptionId apply(String compositeSubscription) {
return SubscriptionId.fromPb(compositeSubscription);
}
});
String cursor = listSubscriptionsResponse.getNextPageToken().equals("") ? null
: listSubscriptionsResponse.getNextPageToken();
return new AsyncPageImpl<>(
new SubscriptionNamePageFetcher(topic, serviceOptions, cursor, options), cursor,
subscriptions);
}
});
}
@Override
public Page listSubscriptions(String topic, ListOption... options) {
return get(listSubscriptionsAsync(topic, options));
}
@Override
public Future> listSubscriptionsAsync(String topic,
ListOption... options) {
return listSubscriptionsAsync(topic, options(), optionMap(options));
}
@Override
public Iterator pull(String subscription, int maxMessages) {
return get(pullAsync(subscription, maxMessages));
}
@Override
public Future> pullAsync(final String subscription, int maxMessages) {
PullRequest request = PullRequest.newBuilder().setReturnImmediately(true)
.setSubscription(SubscriberApi.formatSubscriptionName(options().projectId(), subscription))
.setMaxMessages(maxMessages)
.setReturnImmediately(true)
.build();
PullFuture future = rpc.pull(request);
future.addCallback(new PubSubRpc.PullCallback() {
@Override
public void success(PullResponse response) {
List ackIds = Lists.transform(response.getReceivedMessagesList(),
MESSAGE_TO_ACK_ID_FUNCTION);
ackDeadlineRenewer.add(subscription, ackIds);
}
@Override
public void failure(Throwable error) {
// ignore
}
});
return transform(future, new Function>() {
@Override
public Iterator apply(PullResponse response) {
return Iterators.transform(response.getReceivedMessagesList().iterator(),
new Function() {
@Override
public ReceivedMessage apply(com.google.pubsub.v1.ReceivedMessage receivedMessage) {
// Remove consumed message from automatic ack deadline renewer
ackDeadlineRenewer.remove(subscription, receivedMessage.getAckId());
return ReceivedMessage.fromPb(PubSubImpl.this, subscription, receivedMessage);
}
});
}
});
}
@Override
public MessageConsumer pullAsync(String subscription, MessageProcessor callback,
PullOption... options) {
Map optionMap = optionMap(options);
return MessageConsumerImpl.builder(options(), subscription, ackDeadlineRenewer, callback)
.maxQueuedCallbacks(MAX_QUEUED_CALLBACKS.getInteger(optionMap))
.executorFactory(EXECUTOR_FACTORY.getExecutorFactory(optionMap))
.build();
}
@Override
public void ack(String subscription, String ackId, String... ackIds) {
ack(subscription, Lists.asList(ackId, ackIds));
}
@Override
public Future ackAsync(String subscription, String ackId, String... ackIds) {
return ackAsync(subscription, Lists.asList(ackId, ackIds));
}
@Override
public void ack(String subscription, Iterable ackIds) {
get(ackAsync(subscription, ackIds));
}
@Override
public Future ackAsync(String subscription, Iterable ackIds) {
AcknowledgeRequest request = AcknowledgeRequest.newBuilder()
.setSubscription(SubscriberApi.formatSubscriptionName(options().projectId(), subscription))
.addAllAckIds(ackIds)
.build();
return transform(rpc.acknowledge(request), EMPTY_TO_VOID_FUNCTION);
}
@Override
public void nack(String subscription, String ackId, String... ackIds) {
nack(subscription, Lists.asList(ackId, ackIds));
}
@Override
public Future nackAsync(String subscription, String ackId, String... ackIds) {
return nackAsync(subscription, Lists.asList(ackId, ackIds));
}
@Override
public void nack(String subscription, Iterable ackIds) {
get(nackAsync(subscription, ackIds));
}
@Override
public Future nackAsync(String subscription, Iterable ackIds) {
return modifyAckDeadlineAsync(subscription, 0, TimeUnit.SECONDS, ackIds);
}
@Override
public void modifyAckDeadline(String subscription, int deadline, TimeUnit unit, String ackId,
String... ackIds) {
get(modifyAckDeadlineAsync(subscription, deadline, unit, Lists.asList(ackId, ackIds)));
}
@Override
public Future modifyAckDeadlineAsync(String subscription, int deadline, TimeUnit unit,
String ackId, String... ackIds) {
return modifyAckDeadlineAsync(subscription, deadline, unit, Lists.asList(ackId, ackIds));
}
@Override
public void modifyAckDeadline(String subscription, int deadline, TimeUnit unit,
Iterable ackIds) {
get(modifyAckDeadlineAsync(subscription, deadline, unit, ackIds));
}
@Override
public Future modifyAckDeadlineAsync(String subscription, int deadline, TimeUnit unit,
Iterable ackIds) {
ModifyAckDeadlineRequest request = ModifyAckDeadlineRequest.newBuilder()
.setSubscription(SubscriberApi.formatSubscriptionName(options().projectId(), subscription))
.setAckDeadlineSeconds((int) TimeUnit.SECONDS.convert(deadline, unit))
.addAllAckIds(ackIds)
.build();
return transform(rpc.modify(request), EMPTY_TO_VOID_FUNCTION);
}
static Map optionMap(Option... options) {
Map optionMap = Maps.newHashMap();
for (Option option : options) {
Object prev = optionMap.put(option.optionType(), option.value());
checkArgument(prev == null, "Duplicate option %s", option);
}
return optionMap;
}
@Override
public void close() throws Exception {
if (closed) {
return;
}
closed = true;
rpc.close();
if (ackDeadlineRenewer != null) {
ackDeadlineRenewer.close();
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy