/* * Copyright (c) 2015-2017, Christoph Engelbert (aka noctarius) and * contributors. All rights reserved. * * 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 com.noctarius.snowcast.impl; import com.hazelcast.client.impl.protocol.ClientMessage; import com.hazelcast.client.impl.protocol.codec.SnowcastRegisterChannelCodec; import com.hazelcast.client.impl.protocol.codec.SnowcastRemoveChannelCodec; import com.hazelcast.client.spi.ClientContext; import com.hazelcast.client.spi.ClientProxy; import com.hazelcast.client.spi.EventHandler; import com.hazelcast.client.spi.impl.ListenerMessageCodec; import com.hazelcast.nio.serialization.Data; import com.hazelcast.spi.serialization.SerializationService; import com.noctarius.snowcast.SnowcastSequenceState; import com.noctarius.snowcast.SnowcastSequencer; import com.noctarius.snowcast.impl.notification.ClientDestroySequencerNotification; import javax.annotation.Nonnegative; import javax.annotation.Nonnull; import javax.validation.constraints.Max; import javax.validation.constraints.Min; public class ClientSequencer extends ClientProxy implements InternalSequencer { private final ClientSequencerContext sequencerContext; private final ClientSequencerService sequencerService; ClientSequencer(@Nonnull ClientSequencerService sequencerService, @Nonnull SequencerDefinition definition, @Nonnull ClientCodec clientCodec) { super(SnowcastConstants.SERVICE_NAME, definition.getSequencerName()); this.sequencerService = sequencerService; this.sequencerContext = new ClientSequencerContext(definition, clientCodec); } @Nonnull @Override public String getSequencerName() { return sequencerContext.getSequencerName(); } @Override public long next() throws InterruptedException { return sequencerContext.next(); } @Nonnull @Override public SnowcastSequenceState getSequencerState() { return sequencerContext.getSequencerState(); } @Nonnull @Override public SequencerDefinition getSequencerDefinition() { return sequencerContext.getSequencerDefinition(); } @Nonnull @Override public SnowcastSequencer attachLogicalNode() { sequencerContext.attachLogicalNode(); return this; } @Nonnull @Override public SnowcastSequencer detachLogicalNode() { sequencerContext.detachLogicalNode(); return this; } @Override @Nonnegative public long timestampValue(long sequenceId) { return sequencerContext.timestampValue(sequenceId); } @Override @Nonnegative public int logicalNodeId(long sequenceId) { return sequencerContext.logicalNodeId(sequenceId); } @Override @Nonnegative public int counterValue(long sequenceId) { return sequencerContext.counterValue(sequenceId); } @Override protected void onInitialize() { sequencerContext.initialize(this); } @Override public void stateTransition(@Nonnull SnowcastSequenceState newState) { sequencerContext.stateTransition(newState); } @Nonnull @Override public SequencerService getSequencerService() { return sequencerService; } private void unregisterClientChannel(@Nonnull String uuid) { ClientContext context = getContext(); context.getListenerService().deregisterListener(uuid); } private static class ClientSequencerContext extends AbstractSequencerContext { private static final Tracer TRACER = TracingUtils.tracer(ClientSequencerContext.class); private final ListenerMessageCodec listenerMessageCodec = new SequencerListenerMessageCodec(); private final ClientCodec clientCodec; private volatile String channelRegistration; private ClientSequencerContext(@Nonnull SequencerDefinition definition, @Nonnull ClientCodec clientCodec) { super(definition); this.clientCodec = clientCodec; } @Min(128) @Override @Max(8192) protected int doAttachLogicalNode(@Nonnull SequencerDefinition definition) { TRACER.trace("doAttachLogicalNode begin"); try { return clientCodec.attachLogicalNode(getSequencerName(), definition); } finally { TRACER.trace("doAttachLogicalNode end"); } } @Override protected void doDetachLogicalNode(@Nonnull SequencerDefinition definition, @Min(128) @Max(8192) int logicalNodeId) { TRACER.trace("doDetachLogicalNode begin"); try { clientCodec.detachLogicalNode(getSequencerName(), definition, logicalNodeId); } finally { TRACER.trace("doDetachLogicalNode end"); } } private void unregisterClientChannel(@Nonnull ClientSequencer clientSequencer) { TRACER.trace("unregister from channel for sequencer %s", clientSequencer.getSequencerName()); try { boolean result = clientCodec.removeChannel(getSequencerName(), channelRegistration); if (result) { clientSequencer.unregisterClientChannel(channelRegistration); } } finally { TRACER.trace("unregisterClientChannel end"); } } void initialize(@Nonnull ClientSequencer clientSequencer) { TRACER.trace("register on channel for sequencer %s", clientSequencer.getSequencerName()); ClientChannelHandler handler = new ClientChannelHandler(this, clientSequencer); channelRegistration = clientSequencer.registerListener(listenerMessageCodec, handler); } private class SequencerListenerMessageCodec implements ListenerMessageCodec { @Override public ClientMessage encodeAddRequest(boolean localOnly) { return SnowcastRegisterChannelCodec.encodeRequest(getSequencerName()); } @Override public String decodeAddResponse(ClientMessage clientMessage) { return SnowcastRegisterChannelCodec.decodeResponse(clientMessage).response; } @Override public ClientMessage encodeRemoveRequest(String realRegistrationId) { return SnowcastRemoveChannelCodec.encodeRequest(getSequencerName(), realRegistrationId); } @Override public boolean decodeRemoveResponse(ClientMessage clientMessage) { return SnowcastRemoveChannelCodec.decodeResponse(clientMessage).response; } } } private static class ClientChannelHandler extends SnowcastRegisterChannelCodec.AbstractEventHandler implements EventHandler<ClientMessage> { private static final Tracer TRACER = TracingUtils.tracer(ClientChannelHandler.class); private final ClientSequencerContext sequencerContext; private final ClientSequencer clientSequencer; private ClientChannelHandler(@Nonnull ClientSequencerContext sequencerContext, @Nonnull ClientSequencer clientSequencer) { this.sequencerContext = sequencerContext; this.clientSequencer = clientSequencer; } @Override public void handle(Data item, long publishTime, String uuid) { TRACER.trace("New event message retrieved"); ClientContext context = clientSequencer.getContext(); SerializationService serializationService = context.getSerializationService(); Object message = serializationService.toObject(item); if (message instanceof ClientDestroySequencerNotification) { TRACER.trace("ClientDestroySequencerNotification received"); clientSequencer.stateTransition(SnowcastSequenceState.Destroyed); sequencerContext.unregisterClientChannel(clientSequencer); } } @Override public void beforeListenerRegister() { } @Override public void onListenerRegister() { } } }