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

io.aeron.archive.ControlSession Maven / Gradle / Ivy

There is a newer version: 1.46.2
Show newest version
/*
 * Copyright 2014-2019 Real Logic Ltd.
 *
 * 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.aeron.archive;

import io.aeron.Aeron;
import io.aeron.Publication;
import io.aeron.archive.client.ArchiveException;
import io.aeron.archive.codecs.ControlResponseCode;
import io.aeron.archive.codecs.SourceLocation;
import org.agrona.CloseHelper;
import org.agrona.concurrent.EpochClock;
import org.agrona.concurrent.UnsafeBuffer;

import java.util.ArrayDeque;
import java.util.function.BooleanSupplier;

import static io.aeron.archive.client.ArchiveException.GENERIC;
import static io.aeron.archive.codecs.ControlResponseCode.*;

/**
 * Control sessions are interacted with from the {@link ArchiveConductor}. The interaction may result in pending
 * send actions being queued for execution by the {@link ArchiveConductor}.
 */
class ControlSession implements Session
{
    enum State
    {
        INIT, ACTIVE, INACTIVE, CLOSED
    }

    private final ArchiveConductor conductor;
    private final EpochClock epochClock;
    private final ArrayDeque queuedResponses = new ArrayDeque<>(8);
    private final ControlResponseProxy controlResponseProxy;
    private final long controlSessionId;
    private final long correlationId;
    private final long connectTimeoutMs;
    private long activityDeadlineMs = -1;
    private final ControlSessionDemuxer demuxer;
    private final Publication controlPublication;
    private State state = State.INIT;
    private AbstractListRecordingsSession activeListRecordingsSession;

    ControlSession(
        final long controlSessionId,
        final long correlationId,
        final long connectTimeoutMs,
        final ControlSessionDemuxer demuxer,
        final Publication controlPublication,
        final ArchiveConductor conductor,
        final EpochClock epochClock,
        final ControlResponseProxy controlResponseProxy)
    {
        this.controlSessionId = controlSessionId;
        this.correlationId = correlationId;
        this.connectTimeoutMs = connectTimeoutMs;
        this.demuxer = demuxer;
        this.controlPublication = controlPublication;
        this.conductor = conductor;
        this.epochClock = epochClock;
        this.controlResponseProxy = controlResponseProxy;
    }

    public long sessionId()
    {
        return controlSessionId;
    }

    public void abort()
    {
        state = State.INACTIVE;
    }

    public void close()
    {
        state = State.CLOSED;
        demuxer.removeControlSession(this);
        CloseHelper.close(controlPublication);
    }

    public boolean isDone()
    {
        return state == State.INACTIVE;
    }

    public int doWork()
    {
        int workCount = 0;

        if (state == State.INIT)
        {
            workCount += waitForConnection();
        }

        if (state == State.ACTIVE)
        {
            workCount = sendQueuedResponses();
        }

        return workCount;
    }

    public AbstractListRecordingsSession activeListRecordingsSession()
    {
        return activeListRecordingsSession;
    }

    public void activeListRecordingsSession(final AbstractListRecordingsSession session)
    {
        activeListRecordingsSession = session;
    }

    public void onStopRecording(final long correlationId, final int streamId, final String channel)
    {
        conductor.stopRecording(correlationId, this, streamId, channel);
    }

    public void onStopRecordingSubscription(final long correlationId, final long subscriptionId)
    {
        conductor.stopRecordingSubscription(correlationId, this, subscriptionId);
    }

    public void onStartRecording(
        final long correlationId, final String channel, final int streamId, final SourceLocation sourceLocation)
    {
        conductor.startRecordingSubscription(correlationId, this, streamId, channel, sourceLocation);
    }

    public void onListRecordingsForUri(
        final long correlationId,
        final long fromRecordingId,
        final int recordCount,
        final int streamId,
        final byte[] channelFragment)
    {
        conductor.newListRecordingsForUriSession(
            correlationId,
            fromRecordingId,
            recordCount,
            streamId,
            channelFragment,
            this);
    }

    public void onListRecordings(final long correlationId, final long fromRecordingId, final int recordCount)
    {
        conductor.newListRecordingsSession(correlationId, fromRecordingId, recordCount, this);
    }

    public void onListRecording(final long correlationId, final long recordingId)
    {
        conductor.listRecording(correlationId, this, recordingId);
    }

    public void onFindLastMatchingRecording(
        final long correlationId,
        final long minRecordingId,
        final int sessionId,
        final int streamId,
        final byte[] channelFragment)
    {
        conductor.findLastMatchingRecording(
            correlationId,
            minRecordingId,
            sessionId,
            streamId,
            channelFragment,
            this);
    }

    public void onStartReplay(
        final long correlationId,
        final long recordingId,
        final long position,
        final long length,
        final int replayStreamId,
        final String replayChannel)
    {
        conductor.startReplay(
            correlationId,
            this,
            recordingId,
            position,
            length,
            replayStreamId,
            replayChannel);
    }

    public void onStopReplay(final long correlationId, final long replaySessionId)
    {
        conductor.stopReplay(correlationId, this, replaySessionId);
    }

    public void onExtendRecording(
        final long correlationId,
        final long recordingId,
        final String channel,
        final int streamId,
        final SourceLocation sourceLocation)
    {
        conductor.extendRecording(correlationId, this, recordingId, streamId, channel, sourceLocation);
    }

    public void onGetRecordingPosition(final long correlationId, final long recordingId)
    {
        conductor.getRecordingPosition(correlationId, this, recordingId);
    }

    public void onTruncateRecording(final long correlationId, final long recordingId, final long position)
    {
        conductor.truncateRecording(correlationId, this, recordingId, position);
    }

    public void onGetStopPosition(final long correlationId, final long recordingId)
    {
        conductor.getStopPosition(correlationId, this, recordingId);
    }

    void onListRecordingSessionClosed(final AbstractListRecordingsSession listRecordingsSession)
    {
        if (listRecordingsSession != activeListRecordingsSession)
        {
            throw new ArchiveException();
        }

        activeListRecordingsSession = null;
    }

    void sendOkResponse(final long correlationId, final ControlResponseProxy proxy)
    {
        if (!proxy.sendResponse(controlSessionId, correlationId, 0L, OK, null, controlPublication))
        {
            queueResponse(correlationId, 0, OK, null);
        }
    }

    void sendOkResponse(final long correlationId, final long relevantId, final ControlResponseProxy proxy)
    {
        if (!proxy.sendResponse(controlSessionId, correlationId, relevantId, OK, null, controlPublication))
        {
            queueResponse(correlationId, relevantId, OK, null);
        }
    }

    void sendRecordingUnknown(final long correlationId, final long recordingId, final ControlResponseProxy proxy)
    {
        if (!proxy.sendResponse(
            controlSessionId,
            correlationId,
            recordingId,
            RECORDING_UNKNOWN,
            null,
            controlPublication))
        {
            queueResponse(correlationId, recordingId, RECORDING_UNKNOWN, null);
        }
    }

    void sendErrorResponse(final long correlationId, final String errorMessage, final ControlResponseProxy proxy)
    {
        if (!proxy.sendResponse(controlSessionId, correlationId, 0, ERROR, errorMessage, controlPublication))
        {
            queueResponse(correlationId, 0, ERROR, errorMessage);
        }
    }

    void sendErrorResponse(
        final long correlationId, final long relevantId, final String errorMessage, final ControlResponseProxy proxy)
    {
        if (!proxy.sendResponse(controlSessionId, correlationId, relevantId, ERROR, errorMessage, controlPublication))
        {
            queueResponse(correlationId, relevantId, ERROR, errorMessage);
        }
    }

    void attemptErrorResponse(final long correlationId, final String errorMessage, final ControlResponseProxy proxy)
    {
        proxy.attemptErrorResponse(controlSessionId, GENERIC, correlationId, errorMessage, controlPublication);
    }

    void attemptErrorResponse(
        final long correlationId, final long relevantId, final String errorMessage, final ControlResponseProxy proxy)
    {
        proxy.attemptErrorResponse(controlSessionId, correlationId, relevantId, errorMessage, controlPublication);
    }

    int sendDescriptor(final long correlationId, final UnsafeBuffer descriptorBuffer, final ControlResponseProxy proxy)
    {
        return proxy.sendDescriptor(controlSessionId, correlationId, descriptorBuffer, controlPublication);
    }

    int maxPayloadLength()
    {
        return controlPublication.maxPayloadLength();
    }

    private void sendConnectResponse()
    {
        if (!controlResponseProxy.sendResponse(
            controlSessionId,
            correlationId,
            controlSessionId,
            OK,
            null,
            controlPublication))
        {
            queueResponse(correlationId, controlSessionId, OK, null);
        }
    }

    private int sendQueuedResponses()
    {
        int workCount = 0;
        if (!controlPublication.isConnected())
        {
            state = State.INACTIVE;
        }
        else
        {
            if (!queuedResponses.isEmpty())
            {
                if (sendFirst(queuedResponses))
                {
                    queuedResponses.pollFirst();
                    activityDeadlineMs = Aeron.NULL_VALUE;
                    workCount++;
                }
                else if (activityDeadlineMs == Aeron.NULL_VALUE)
                {
                    activityDeadlineMs = epochClock.time() + connectTimeoutMs;
                }
                else if (hasGoneInactive())
                {
                    state = State.INACTIVE;
                }
            }
        }

        return workCount;
    }

    private static boolean sendFirst(final ArrayDeque responseQueue)
    {
        return responseQueue.peekFirst().getAsBoolean();
    }

    private int waitForConnection()
    {
        int workCount = 0;
        if (activityDeadlineMs == Aeron.NULL_VALUE)
        {
            activityDeadlineMs = epochClock.time() + connectTimeoutMs;
        }
        else if (controlPublication.isConnected())
        {
            activityDeadlineMs = Aeron.NULL_VALUE;
            state = State.ACTIVE;
            sendConnectResponse();
            workCount += 1;
        }
        else if (hasGoneInactive())
        {
            state = State.INACTIVE;
        }

        return workCount;
    }

    private boolean hasGoneInactive()
    {
        return activityDeadlineMs != Aeron.NULL_VALUE && epochClock.time() > activityDeadlineMs;
    }

    private void queueResponse(
        final long correlationId, final long relevantId, final ControlResponseCode code, final String message)
    {
        queuedResponses.offer(() -> controlResponseProxy.sendResponse(
            controlSessionId,
            correlationId,
            relevantId,
            code,
            message,
            controlPublication));
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy