All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.eventstore.dbclient.AbstractSubscribePersistentSubscription Maven / Gradle / Ivy

package com.eventstore.dbclient;

import com.eventstore.dbclient.proto.persistentsubscriptions.Persistent;
import com.eventstore.dbclient.proto.persistentsubscriptions.PersistentSubscriptionsGrpc;
import com.eventstore.dbclient.proto.shared.Shared;
import io.grpc.Metadata;
import io.grpc.Status;
import io.grpc.StatusRuntimeException;
import io.grpc.stub.ClientCallStreamObserver;
import io.grpc.stub.ClientResponseObserver;
import io.grpc.stub.MetadataUtils;
import io.grpc.stub.StreamObserver;

import java.util.concurrent.CompletableFuture;

abstract class AbstractSubscribePersistentSubscription {
    protected static final Persistent.ReadReq.Options.Builder defaultReadOptions;
    private final GrpcClient connection;
    private final String group;
    private final PersistentSubscriptionListener listener;
    private final SubscribePersistentSubscriptionOptions options;

    static {
        defaultReadOptions = Persistent.ReadReq.Options.newBuilder()
                .setUuidOption(Persistent.ReadReq.Options.UUIDOption.newBuilder()
                        .setStructured(Shared.Empty.getDefaultInstance()));
    }

    public AbstractSubscribePersistentSubscription(GrpcClient connection, String group,
                                                   SubscribePersistentSubscriptionOptions options,
                                                   PersistentSubscriptionListener listener) {
        this.connection = connection;
        this.group = group;
        this.options = options;
        this.listener = listener;
    }

    protected abstract Persistent.ReadReq.Options.Builder createOptions();

    public CompletableFuture execute() {
        return this.connection.runWithArgs(args -> {
            PersistentSubscriptionsGrpc.PersistentSubscriptionsStub client =
                    GrpcUtils.configureStub(PersistentSubscriptionsGrpc.newStub(args.getChannel()), this.connection.getSettings(), this.options);

            final CompletableFuture result = new CompletableFuture<>();

            int bufferSize = this.options.getBufferSize();

            Persistent.ReadReq req = Persistent.ReadReq.newBuilder()
                    .setOptions(createOptions()
                            .setBufferSize(bufferSize)
                            .setGroupName(group))
                    .build();

            if (req.getOptions().hasAll() && !args.supportFeature(FeatureFlags.PERSISTENT_SUBSCRIPTION_TO_ALL)) {
                result.completeExceptionally(new UnsupportedFeature());
            } else {

                ClientResponseObserver observer = new ClientResponseObserver() {
                    private boolean _confirmed;
                    private PersistentSubscription _subscription;
                    private ClientCallStreamObserver _requestStream;

                    @Override
                    public void beforeStart(ClientCallStreamObserver requestStream) {
                        this._requestStream = requestStream;
                    }

                    @Override
                    public void onNext(Persistent.ReadResp readResp) {
                        if (!_confirmed && readResp.hasSubscriptionConfirmation()) {
                            this._confirmed = true;
                            this._subscription = new PersistentSubscription(this._requestStream,
                                    readResp.getSubscriptionConfirmation().getSubscriptionId());
                            result.complete(this._subscription);
                            return;
                        }

                        if (!_confirmed && readResp.hasEvent()) {
                            onError(new IllegalStateException("Unconfirmed persistent subscription received event"));
                            return;
                        }

                        if (_confirmed && !readResp.hasEvent()) {
                            onError(new IllegalStateException(
                                    String.format("Confirmed persistent subscription %s received non-{event,checkpoint} variant",
                                            _subscription.getSubscriptionId())));
                            return;
                        }

                        listener.onEvent(this._subscription, ResolvedEvent.fromWire(readResp.getEvent()));
                    }

                    @Override
                    public void onError(Throwable throwable) {
                        if (!_confirmed) {
                            result.completeExceptionally(throwable);
                        }

                        Throwable error = throwable;
                        if (error instanceof StatusRuntimeException) {
                            StatusRuntimeException sre = (StatusRuntimeException) error;
                            if (sre.getStatus().getCode() == Status.Code.CANCELLED) {
                                listener.onCancelled(this._subscription);
                                return;
                            }

                            String leaderHost = sre.getTrailers().get(Metadata.Key.of("leader-endpoint-host", Metadata.ASCII_STRING_MARSHALLER));
                            String leaderPort = sre.getTrailers().get(Metadata.Key.of("leader-endpoint-port", Metadata.ASCII_STRING_MARSHALLER));

                            if (leaderHost != null && leaderPort != null) {
                                error = new NotLeaderException(leaderHost, Integer.valueOf(leaderPort));
                            }
                        }

                        listener.onError(this._subscription, error);
                    }

                    @Override
                    public void onCompleted() {
                        // Subscriptions should only complete on error.
                    }
                };

                StreamObserver wireStream = client.read(observer);
                wireStream.onNext(req);
            }

            return result;
        });
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy