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

io.etcd.jetcd.ElectionImpl Maven / Gradle / Ivy

There is a newer version: 0.8.4
Show newest version
/*
 * Copyright 2016-2021 The jetcd authors
 *
 * 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 io.etcd.jetcd;

import java.util.concurrent.CompletableFuture;
import java.util.function.Function;

import io.etcd.jetcd.api.CampaignRequest;
import io.etcd.jetcd.api.ElectionGrpc;
import io.etcd.jetcd.api.LeaderRequest;
import io.etcd.jetcd.api.ProclaimRequest;
import io.etcd.jetcd.api.ResignRequest;
import io.etcd.jetcd.common.exception.EtcdExceptionFactory;
import io.etcd.jetcd.election.CampaignResponse;
import io.etcd.jetcd.election.LeaderKey;
import io.etcd.jetcd.election.LeaderResponse;
import io.etcd.jetcd.election.NoLeaderException;
import io.etcd.jetcd.election.NotLeaderException;
import io.etcd.jetcd.election.ProclaimResponse;
import io.etcd.jetcd.election.ResignResponse;
import io.grpc.StatusRuntimeException;
import io.grpc.stub.StreamObserver;

import static com.google.common.base.Preconditions.checkNotNull;

final class ElectionImpl implements Election {
    private final ElectionGrpc.ElectionStub stub;
    private final ByteSequence namespace;

    ElectionImpl(ClientConnectionManager connectionManager) {
        this.stub = connectionManager.newStub(ElectionGrpc::newStub);
        this.namespace = connectionManager.getNamespace();
    }

    @Override
    public CompletableFuture campaign(ByteSequence electionName, long leaseId, ByteSequence proposal) {
        checkNotNull(electionName, "election name should not be null");
        checkNotNull(proposal, "proposal should not be null");

        CampaignRequest request = CampaignRequest.newBuilder()
            .setName(Util.prefixNamespace(electionName.getByteString(), namespace))
            .setValue(proposal.getByteString())
            .setLease(leaseId)
            .build();

        final StreamObserverDelegate observer = new StreamObserverDelegate<>(
            CampaignResponse::new);
        stub.campaign(request, observer);

        return observer.getFuture();
    }

    @Override
    public CompletableFuture proclaim(LeaderKey leaderKey, ByteSequence proposal) {
        checkNotNull(leaderKey, "leader key should not be null");
        checkNotNull(proposal, "proposal should not be null");

        io.etcd.jetcd.api.LeaderKey leader = io.etcd.jetcd.api.LeaderKey.newBuilder()
            .setKey(leaderKey.getKey()).setName(leaderKey.getName())
            .setLease(leaderKey.getLease()).setRev(leaderKey.getRevision())
            .build();
        ProclaimRequest request = ProclaimRequest.newBuilder()
            .setLeader(leader).setValue(proposal.getByteString())
            .build();

        final StreamObserverDelegate observer = new StreamObserverDelegate<>(
            ProclaimResponse::new);
        stub.proclaim(request, observer);

        return observer.getFuture();
    }

    @Override
    public CompletableFuture leader(ByteSequence electionName) {
        checkNotNull(electionName, "election name should not be null");

        LeaderRequest request = LeaderRequest.newBuilder()
            .setName(Util.prefixNamespace(electionName.getByteString(), namespace))
            .build();

        final StreamObserverDelegate observer = new StreamObserverDelegate<>(
            input -> new LeaderResponse(input, namespace));
        stub.leader(request, observer);

        return observer.getFuture();
    }

    @Override
    public void observe(ByteSequence electionName, Listener listener) {
        checkNotNull(electionName, "election name should not be null");
        checkNotNull(listener, "listener should not be null");

        LeaderRequest request = LeaderRequest.newBuilder().setName(electionName.getByteString()).build();

        stub.observe(request, new StreamObserver() {
            @Override
            public void onNext(io.etcd.jetcd.api.LeaderResponse value) {
                listener.onNext(new LeaderResponse(value, namespace));
            }

            @Override
            public void onError(Throwable error) {
                listener.onError(EtcdExceptionFactory.toEtcdException(error));
            }

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

    @Override
    public CompletableFuture resign(LeaderKey leaderKey) {
        checkNotNull(leaderKey, "leader key should not be null");

        io.etcd.jetcd.api.LeaderKey leader = io.etcd.jetcd.api.LeaderKey.newBuilder()
            .setKey(leaderKey.getKey()).setName(leaderKey.getName())
            .setLease(leaderKey.getLease()).setRev(leaderKey.getRevision())
            .build();
        ResignRequest request = ResignRequest.newBuilder().setLeader(leader).build();

        final StreamObserverDelegate observer = new StreamObserverDelegate<>(
            ResignResponse::new);
        stub.resign(request, observer);

        return observer.getFuture();
    }

    @Override
    public void close() {
    }

    private static class StreamObserverDelegate implements StreamObserver {
        private final CompletableFuture future = new CompletableFuture<>();
        private final Function converter;

        public StreamObserverDelegate(Function converter) {
            this.converter = converter;
        }

        @Override
        public void onNext(S value) {
            future.complete(converter.apply(value));
        }

        @Override
        public void onError(Throwable error) {
            if (error instanceof StatusRuntimeException) {
                StatusRuntimeException exception = (StatusRuntimeException) error;
                String description = exception.getStatus().getDescription();
                // different APIs use different messages. we cannot distinguish missing leader error otherwise,
                // because communicated status is always UNKNOWN
                if ("election: not leader".equals(description)) {
                    future.completeExceptionally(NotLeaderException.INSTANCE);
                } else if ("election: no leader".equals(description)) {
                    future.completeExceptionally(NoLeaderException.INSTANCE);
                }
            }
            future.completeExceptionally(EtcdExceptionFactory.toEtcdException(error));
        }

        @Override
        public void onCompleted() {
        }

        public CompletableFuture getFuture() {
            return future;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy