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

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

package com.eventstore.dbclient;

import com.eventstore.dbclient.proto.serverfeatures.ServerFeaturesGrpc;
import com.eventstore.dbclient.proto.serverfeatures.Serverfeatures;
import com.eventstore.dbclient.proto.shared.Shared;
import io.grpc.ManagedChannel;
import io.grpc.Status;
import io.grpc.StatusRuntimeException;
import io.grpc.stub.ClientCallStreamObserver;
import io.grpc.stub.ClientResponseObserver;

import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.Function;

class ServerFeatures {
    public static Optional getSupportedFeatures(EventStoreDBClientSettings settings, ManagedChannel channel) {
        final ServerFeaturesGrpc.ServerFeaturesStub stub = ServerFeaturesGrpc.newStub(channel);
        try {
            return Optional.ofNullable(getSupportedFeaturesInternal(stub).get(settings.getGossipTimeout(), TimeUnit.MILLISECONDS));
        } catch (InterruptedException e) {
            throw new RuntimeException("Interrupted when fetching server features", e);
        } catch (TimeoutException e) {
            throw new RetryableException(e);
        } catch (ExecutionException e) {
            if (e.getCause() instanceof StatusRuntimeException) {
                StatusRuntimeException error = (StatusRuntimeException) e.getCause();
                if (error.getStatus().getCode() == Status.Code.NOT_FOUND || error.getStatus().getCode() == Status.Code.UNIMPLEMENTED) {
                    return Optional.empty();
                } else if (error.getStatus().getCode() == Status.Code.UNAVAILABLE) {
                    throw new RetryableException(e);
                }
            }
            throw new RuntimeException("Error when fetching server features", e);
        }
    }

    private static CompletableFuture getSupportedFeaturesInternal(ServerFeaturesGrpc.ServerFeaturesStub stub) {
        CompletableFuture result = new CompletableFuture<>();

        stub.getSupportedMethods(Shared.Empty.getDefaultInstance(), convertSingleResponse(result, resp -> {
            int major = 0, minor = 0, patch = 0;

            String[] splits = resp.getEventStoreServerVersion().split(".");
            for (int idx = 0; idx < splits.length; idx++) {
                if (idx > 2) {
                    break;
                }

                int value = Integer.parseInt(splits[idx]);
                switch (idx) {
                    case 0:
                        major = value;
                        break;
                    case 1:
                        minor = value;
                        break;
                    default:
                        patch = value;
                        break;
                }
            }

            ServerVersion version = new ServerVersion(major, minor, patch);

            int features = FeatureFlags.NOTHING;
            for (Serverfeatures.SupportedMethod method : resp.getMethodsList()) {
                if (method.getMethodName().equals("batchappend") && method.getServiceName().equals("event_store.client.streams.streams")) {
                    features |= FeatureFlags.BATCH_APPEND;
                } else if (method.getServiceName().equals("event_store.client.persistent_subscriptions.persistentsubscriptions")) {
                    switch (method.getMethodName()) {
                        case "create":
                            for (String feat : method.getFeaturesList()) {
                                if (feat.equals("all")) {
                                    features |= FeatureFlags.PERSISTENT_SUBSCRIPTION_TO_ALL;
                                }
                            }
                            break;
                        case "getinfo":
                            features |= FeatureFlags.PERSISTENT_SUBSCRIPTION_GET_INFO;
                            break;
                        case "replayparked":
                            features |= FeatureFlags.PERSISTENT_SUBSCRIPTION_REPLAY;
                            break;
                        case "list":
                            features |= FeatureFlags.PERSISTENT_SUBSCRIPTION_LIST;
                            break;
                        case "restartsubsystem":
                            features |= FeatureFlags.PERSISTENT_SUBSCRIPTION_RESTART_SUBSYSTEM;
                            break;
                        default:
                            break;
                    }
                }
            }

            return new ServerInfo(version, features);
        }));

        return result;
    }

    private static  ClientResponseObserver convertSingleResponse(
            CompletableFuture dest, Function converter) {
        return new ClientResponseObserver() {
            @Override
            public void beforeStart(ClientCallStreamObserver requestStream) {
            }

            @Override
            public void onNext(RespT value) {
                try {
                    TargetT converted = converter.apply(value);
                    dest.complete(converted);
                } catch (Throwable e) {
                    dest.completeExceptionally(e);
                }
            }

            @Override
            public void onError(Throwable t) {
                dest.completeExceptionally(t);
            }

            @Override
            public void onCompleted() {
            }
        };
    }

    static class RetryableException extends RuntimeException {
        public RetryableException(Throwable cause) {
            super(cause);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy