/*
* Copyright 2014-2017 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.archiver.client;
import io.aeron.*;
import io.aeron.archiver.codecs.*;
import org.agrona.*;
public class ArchiveClient
{
private static final int HEADER_LENGTH = MessageHeaderEncoder.ENCODED_LENGTH;
private final ExpandableArrayBuffer buffer = new ExpandableArrayBuffer();
private final Publication controlRequest;
private final Subscription recordingEvents;
private final MessageHeaderEncoder messageHeaderEncoder = new MessageHeaderEncoder();
private final ConnectRequestEncoder connectRequestEncoder = new ConnectRequestEncoder();
private final StartRecordingRequestEncoder startRecordingRequestEncoder = new StartRecordingRequestEncoder();
private final ReplayRequestEncoder replayRequestEncoder = new ReplayRequestEncoder();
private final AbortReplayRequestEncoder abortReplayRequestEncoder = new AbortReplayRequestEncoder();
private final StopRecordingRequestEncoder stopRecordingRequestEncoder = new StopRecordingRequestEncoder();
private final ListRecordingsRequestEncoder listRecordingsRequestEncoder = new ListRecordingsRequestEncoder();
private final MessageHeaderDecoder messageHeaderDecoder = new MessageHeaderDecoder();
private final RecordingStartedDecoder recordingStartedDecoder = new RecordingStartedDecoder();
private final RecordingProgressDecoder recordingProgressDecoder = new RecordingProgressDecoder();
private final RecordingStoppedDecoder recordingStoppedDecoder = new RecordingStoppedDecoder();
private final ReplayAbortedDecoder replayAbortedDecoder = new ReplayAbortedDecoder();
private final ReplayStartedDecoder replayStartedDecoder = new ReplayStartedDecoder();
private final RecordingDescriptorDecoder recordingDescriptorDecoder = new RecordingDescriptorDecoder();
private final ControlResponseDecoder archiverResponseDecoder = new ControlResponseDecoder();
private final RecordingNotFoundResponseDecoder recordingNotFoundResponseDecoder =
new RecordingNotFoundResponseDecoder();
public ArchiveClient(
final Publication controlRequest,
final Subscription recordingEvents)
{
this.controlRequest = controlRequest;
this.recordingEvents = recordingEvents;
}
public boolean connect(
final String channel,
final int streamId)
{
connectRequestEncoder
.wrapAndApplyHeader(buffer, 0, messageHeaderEncoder)
.responseStreamId(streamId)
.responseChannel(channel);
return offer(connectRequestEncoder.encodedLength());
}
public boolean startRecording(
final String channel,
final int streamId,
final long correlationId)
{
startRecordingRequestEncoder
.wrapAndApplyHeader(buffer, 0, messageHeaderEncoder)
.correlationId(correlationId)
.streamId(streamId)
.channel(channel);
return offer(startRecordingRequestEncoder.encodedLength());
}
public boolean stopRecording(
final long recordingId,
final long correlationId)
{
stopRecordingRequestEncoder
.wrapAndApplyHeader(buffer, 0, messageHeaderEncoder)
.correlationId(correlationId)
.recordingId(recordingId);
return offer(stopRecordingRequestEncoder.encodedLength());
}
public boolean replay(
final long recordingId,
final long position,
final long length,
final String replayChannel,
final int replayStreamId,
final long correlationId)
{
replayRequestEncoder
.wrapAndApplyHeader(buffer, 0, messageHeaderEncoder)
.correlationId(correlationId)
.recordingId(recordingId)
.position(position)
.length(length)
.replayStreamId(replayStreamId)
.replayChannel(replayChannel);
return offer(replayRequestEncoder.encodedLength());
}
public boolean abortReplay(final int replayId, final long correlationId)
{
abortReplayRequestEncoder
.wrapAndApplyHeader(buffer, 0, messageHeaderEncoder)
.correlationId(correlationId)
.replayId(replayId);
return offer(abortReplayRequestEncoder.encodedLength());
}
public boolean listRecordings(
final long fromId,
final long toId,
final long correlationId)
{
listRecordingsRequestEncoder
.wrapAndApplyHeader(buffer, 0, messageHeaderEncoder)
.correlationId(correlationId)
.fromId(fromId)
.toId(toId);
return offer(listRecordingsRequestEncoder.encodedLength());
}
public int pollResponses(
final Subscription reply,
final ResponseListener responseListener,
final int count)
{
return reply.poll(
(buffer, offset, length, header) ->
{
messageHeaderDecoder.wrap(buffer, offset);
switch (messageHeaderDecoder.templateId())
{
case ControlResponseDecoder.TEMPLATE_ID:
handleArchiverResponse(responseListener, buffer, offset);
break;
case ReplayAbortedDecoder.TEMPLATE_ID:
handleReplayAborted(responseListener, buffer, offset);
break;
case ReplayStartedDecoder.TEMPLATE_ID:
handleReplayStarted(responseListener, buffer, offset);
break;
case RecordingDescriptorDecoder.TEMPLATE_ID:
handleRecordingDescriptor(responseListener, buffer, offset);
break;
case RecordingNotFoundResponseDecoder.TEMPLATE_ID:
handleRecordingNotFoundResponse(responseListener, buffer, offset);
break;
default:
throw new IllegalStateException();
}
}, count);
}
private void handleRecordingNotFoundResponse(
final ResponseListener responseListener,
final DirectBuffer buffer,
final int offset)
{
recordingNotFoundResponseDecoder.wrap(
buffer,
offset + HEADER_LENGTH,
messageHeaderDecoder.blockLength(),
messageHeaderDecoder.version());
responseListener.onRecordingNotFound(
recordingNotFoundResponseDecoder.recordingId(),
recordingNotFoundResponseDecoder.maxRecordingId(),
recordingNotFoundResponseDecoder.correlationId());
}
private void handleArchiverResponse(
final ResponseListener responseListener,
final DirectBuffer buffer,
final int offset)
{
archiverResponseDecoder.wrap(
buffer,
offset + HEADER_LENGTH,
messageHeaderDecoder.blockLength(),
messageHeaderDecoder.version());
responseListener.onResponse(
archiverResponseDecoder.errorMessage(),
archiverResponseDecoder.correlationId());
}
private void handleReplayAborted(
final ResponseListener responseListener,
final DirectBuffer buffer,
final int offset)
{
replayAbortedDecoder.wrap(
buffer,
offset + HEADER_LENGTH,
messageHeaderDecoder.blockLength(),
messageHeaderDecoder.version());
responseListener.onReplayAborted(
replayAbortedDecoder.lastPosition(),
replayAbortedDecoder.correlationId());
}
private void handleReplayStarted(
final ResponseListener responseListener,
final DirectBuffer buffer,
final int offset)
{
replayStartedDecoder.wrap(
buffer,
offset + HEADER_LENGTH,
messageHeaderDecoder.blockLength(),
messageHeaderDecoder.version());
responseListener.onReplayStarted(
replayStartedDecoder.replayId(),
replayStartedDecoder.correlationId());
}
private void handleRecordingDescriptor(
final ResponseListener responseListener,
final DirectBuffer buffer,
final int offset)
{
recordingDescriptorDecoder.wrap(
buffer,
offset + HEADER_LENGTH,
messageHeaderDecoder.blockLength(),
messageHeaderDecoder.version());
responseListener.onRecordingDescriptor(
recordingDescriptorDecoder.recordingId(),
recordingDescriptorDecoder.segmentFileLength(),
recordingDescriptorDecoder.termBufferLength(),
recordingDescriptorDecoder.startTime(),
recordingDescriptorDecoder.joiningPosition(),
recordingDescriptorDecoder.endTime(),
recordingDescriptorDecoder.lastPosition(),
recordingDescriptorDecoder.source(),
recordingDescriptorDecoder.sessionId(),
recordingDescriptorDecoder.channel(),
recordingDescriptorDecoder.streamId(),
recordingDescriptorDecoder.correlationId()
);
}
public int pollEvents(final RecordingEventsListener recordingEventsListener, final int count)
{
return recordingEvents.poll(
(buffer, offset, length, header) ->
{
messageHeaderDecoder.wrap(buffer, offset);
switch (messageHeaderDecoder.templateId())
{
case RecordingProgressDecoder.TEMPLATE_ID:
recordingProgressDecoder.wrap(
buffer,
offset + MessageHeaderDecoder.ENCODED_LENGTH,
messageHeaderDecoder.blockLength(),
messageHeaderDecoder.version());
recordingEventsListener.onProgress(
recordingProgressDecoder.recordingId(),
recordingProgressDecoder.joiningPosition(),
recordingProgressDecoder.currentPosition()
);
break;
case RecordingStartedDecoder.TEMPLATE_ID:
recordingStartedDecoder.wrap(
buffer,
offset + MessageHeaderDecoder.ENCODED_LENGTH,
messageHeaderDecoder.blockLength(),
messageHeaderDecoder.version());
recordingEventsListener.onStart(
recordingStartedDecoder.recordingId(),
recordingStartedDecoder.channel(), recordingStartedDecoder.sessionId(),
recordingStartedDecoder.source(), recordingStartedDecoder.streamId()
);
break;
case RecordingStoppedDecoder.TEMPLATE_ID:
recordingStoppedDecoder.wrap(
buffer,
offset + MessageHeaderDecoder.ENCODED_LENGTH,
messageHeaderDecoder.blockLength(),
messageHeaderDecoder.version());
recordingEventsListener.onStop(recordingStoppedDecoder.recordingId());
break;
default:
throw new IllegalStateException();
}
}, count);
}
private boolean offer(final int length)
{
final long newPosition = controlRequest.offer(buffer, 0, HEADER_LENGTH + length);
return newPosition >= 0;
}
}