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

io.axoniq.axonserver.connector.impl.ContextConnection Maven / Gradle / Ivy

/*
 * Copyright (c) 2020. AxonIQ
 *
 * 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.axoniq.axonserver.connector.impl;

import io.axoniq.axonserver.connector.AxonServerConnection;
import io.axoniq.axonserver.connector.command.CommandChannel;
import io.axoniq.axonserver.connector.command.impl.CommandChannelImpl;
import io.axoniq.axonserver.connector.control.ControlChannel;
import io.axoniq.axonserver.connector.event.EventChannel;
import io.axoniq.axonserver.connector.event.impl.EventChannelImpl;
import io.axoniq.axonserver.connector.query.QueryChannel;
import io.axoniq.axonserver.connector.query.impl.QueryChannelImpl;
import io.axoniq.axonserver.grpc.control.ClientIdentification;
import io.grpc.ConnectivityState;
import io.grpc.StatusRuntimeException;

import java.util.Optional;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Supplier;
import java.util.function.UnaryOperator;

import static io.axoniq.axonserver.connector.impl.ObjectUtils.doIfNotNull;

/**
 * Implementation of the {@link AxonServerConnection}, carrying context information with the overall connection. The
 * context information can be used to support the notion of bounded contexts or multi-tenancy for example.
 */
public class ContextConnection implements AxonServerConnection {

    private final ClientIdentification clientIdentification;
    private final ControlChannelImpl controlChannel;
    private final AtomicReference commandChannel = new AtomicReference<>();
    private final AtomicReference eventChannel = new AtomicReference<>();
    private final AtomicReference queryChannel = new AtomicReference<>();
    private final ScheduledExecutorService executorService;
    private final AxonServerManagedChannel connection;
    private final String context;

    /**
     * Construct a {@link ContextConnection} carrying context information.
     *
     * @param clientIdentification         client information identifying whom has connected. This information is used
     *                                     to pass on to message
     * @param executorService              a {@link ScheduledExecutorService} used to schedule reconnects in the
     *                                     channels this connection provides
     * @param connection                   the {@link AxonServerManagedChannel} used to form the connections with
     *                                     AxonServer
     * @param processorInfoUpdateFrequency the update frequency in milliseconds of event processor information
     * @param context                      the context this connection belongs to
     */
    public ContextConnection(ClientIdentification clientIdentification,
                             ScheduledExecutorService executorService,
                             AxonServerManagedChannel connection,
                             long processorInfoUpdateFrequency,
                             String context) {
        this.clientIdentification = clientIdentification;
        this.executorService = executorService;
        this.connection = connection;
        this.context = context;
        this.controlChannel = new ControlChannelImpl(clientIdentification,
                                                     context,
                                                     executorService,
                                                     connection,
                                                     processorInfoUpdateFrequency,
                                                     this::reconnectChannels);
    }

    private void reconnectChannels() {
        connection.requestReconnect();
        doIfNotNull(commandChannel.get(), CommandChannelImpl::reconnect);
        doIfNotNull(queryChannel.get(), QueryChannelImpl::reconnect);
        doIfNotNull(controlChannel, ControlChannelImpl::reconnect);
        doIfNotNull(eventChannel.get(), EventChannelImpl::reconnect);
    }

    @Override
    public boolean isConnectionFailed() {
        return connection.getState(false) == ConnectivityState.TRANSIENT_FAILURE;
    }

    @Override
    public boolean isReady() {
        return isConnected()
                && Optional.ofNullable(commandChannel.get()).map(CommandChannelImpl::isReady).orElse(true)
                && Optional.ofNullable(queryChannel.get()).map(QueryChannelImpl::isReady).orElse(true)
                && Optional.ofNullable(eventChannel.get()).map(EventChannelImpl::isReady).orElse(true)
                && controlChannel.isReady();
    }

    @Override
    public boolean isConnected() {
        return connection.getState(false) == ConnectivityState.READY;
    }

    @Override
    public void disconnect() {
        doIfNotNull(controlChannel, ControlChannelImpl::disconnect);
        doIfNotNull(commandChannel.get(), CommandChannelImpl::disconnect);
        doIfNotNull(queryChannel.get(), QueryChannelImpl::disconnect);
        doIfNotNull(eventChannel.get(), EventChannelImpl::disconnect);
        connection.shutdown();
        try {
            if (!connection.awaitTermination(5, TimeUnit.SECONDS)) {
                connection.shutdownNow();
            }
        } catch (InterruptedException e) {
            connection.shutdownNow();
            Thread.currentThread().interrupt();
        }
    }

    @Override
    public ControlChannel controlChannel() {
        return controlChannel;
    }

    /**
     * Ensure there is a connection with the {@link ControlChannel} this connection provides.
     */
    public void connect() {
        ensureConnected(controlChannel);
    }

    @Override
    public CommandChannel commandChannel() {
        CommandChannelImpl channel = this.commandChannel.updateAndGet(
                createIfNull(() -> new CommandChannelImpl(clientIdentification, context, 5000, 2000, executorService, connection))
        );
        return ensureConnected(channel);
    }

    @Override
    public EventChannel eventChannel() {
        EventChannelImpl channel = this.eventChannel.updateAndGet(
                createIfNull(() -> new EventChannelImpl(clientIdentification, executorService, connection))
        );
        return ensureConnected(channel);
    }

    @Override
    public QueryChannel queryChannel() {
        QueryChannelImpl channel = this.queryChannel.updateAndGet(
                createIfNull(() -> new QueryChannelImpl(clientIdentification, context, 5000, 2000, executorService, connection))
        );
        return ensureConnected(channel);
    }

    private  UnaryOperator createIfNull(Supplier factory) {
        return existing -> existing == null ? factory.get() : existing;
    }

    private  T ensureConnected(T channel) {
        if (!channel.isReady()) {
            ConnectivityState state = connection.getState(true);
            if (state != ConnectivityState.SHUTDOWN && state != ConnectivityState.TRANSIENT_FAILURE) {
                try {
                    channel.connect();
                } catch (StatusRuntimeException e) {
                    connection.notifyWhenStateChanged(state, channel::connect);
                }
            } else {
                connection.notifyWhenStateChanged(state, channel::connect);
            }
        }
        return channel;
    }

    /**
     * Retrieve the {@link AxonServerManagedChannel} used to create connection with.
     *
     * @return the {@link AxonServerManagedChannel} used to create connection with
     */
    public AxonServerManagedChannel getManagedChannel() {
        return connection;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy