com.hazelcast.simulator.protocol.connector.AbstractServerConnector.java Source code

Java tutorial

Introduction

Here is the source code for com.hazelcast.simulator.protocol.connector.AbstractServerConnector.java

Source

/*
 * Copyright (c) 2008-2015, Hazelcast, Inc. 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.hazelcast.simulator.protocol.connector;

import com.hazelcast.simulator.protocol.core.Response;
import com.hazelcast.simulator.protocol.core.ResponseFuture;
import com.hazelcast.simulator.protocol.core.ResponseType;
import com.hazelcast.simulator.protocol.core.SimulatorAddress;
import com.hazelcast.simulator.protocol.core.SimulatorMessage;
import com.hazelcast.simulator.protocol.core.SimulatorProtocolException;
import com.hazelcast.simulator.protocol.operation.SimulatorOperation;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import org.apache.log4j.Logger;

import java.net.InetSocketAddress;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

import static com.hazelcast.simulator.protocol.core.ResponseFuture.createFutureKey;
import static com.hazelcast.simulator.protocol.core.ResponseFuture.createInstance;
import static com.hazelcast.simulator.protocol.operation.OperationCodec.toJson;
import static com.hazelcast.simulator.protocol.operation.OperationType.getOperationType;
import static com.hazelcast.simulator.utils.CommonUtils.joinThread;
import static com.hazelcast.simulator.utils.CommonUtils.sleepMillis;
import static java.lang.String.format;

/**
 * Abstract {@link ServerConnector} class for Simulator Agent and Worker.
 */
abstract class AbstractServerConnector implements ServerConnector {

    private static final Logger LOGGER = Logger.getLogger(AbstractServerConnector.class);

    private static final SimulatorMessage POISON_PILL = new SimulatorMessage(null, null, 0, null, null);

    private final EventLoopGroup bossGroup = new NioEventLoopGroup();
    private final EventLoopGroup workerGroup = new NioEventLoopGroup();

    private final AtomicLong messageIds = new AtomicLong();
    private final ConcurrentMap<String, ResponseFuture> messageQueueFutures = new ConcurrentHashMap<String, ResponseFuture>();
    private final BlockingQueue<SimulatorMessage> messageQueue = new LinkedBlockingQueue<SimulatorMessage>();
    private final MessageQueueThread messageQueueThread = new MessageQueueThread();

    private final ConcurrentMap<String, ResponseFuture> futureMap;
    private final SimulatorAddress localAddress;
    private final int addressIndex;
    private final int port;

    private Channel channel;

    AbstractServerConnector(ConcurrentMap<String, ResponseFuture> futureMap, SimulatorAddress localAddress,
            int port) {
        this.futureMap = futureMap;
        this.localAddress = localAddress;
        this.addressIndex = localAddress.getAddressIndex();
        this.port = port;
    }

    abstract void configureServerPipeline(ChannelPipeline pipeline, ServerConnector serverConnector);

    abstract void connectorShutdown();

    abstract ChannelGroup getChannelGroup();

    @Override
    public void start() {
        messageQueueThread.start();

        ServerBootstrap bootstrap = getServerBootstrap();
        ChannelFuture future = bootstrap.bind().syncUninterruptibly();
        channel = future.channel();

        LOGGER.info(format("ServerConnector %s listens on %s", localAddress, channel.localAddress()));
    }

    private ServerBootstrap getServerBootstrap() {
        ServerBootstrap bootstrap = new ServerBootstrap();
        bootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
                .localAddress(new InetSocketAddress(port)).childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    public void initChannel(SocketChannel channel) {
                        configureServerPipeline(channel.pipeline(), AbstractServerConnector.this);
                    }
                });
        return bootstrap;
    }

    @Override
    public void shutdown() {
        messageQueueThread.shutdown();
        connectorShutdown();
        channel.close().syncUninterruptibly();

        workerGroup.shutdownGracefully(DEFAULT_SHUTDOWN_QUIET_PERIOD, DEFAULT_SHUTDOWN_TIMEOUT, TimeUnit.SECONDS)
                .syncUninterruptibly();
        bossGroup.shutdownGracefully(DEFAULT_SHUTDOWN_QUIET_PERIOD, DEFAULT_SHUTDOWN_TIMEOUT, TimeUnit.SECONDS)
                .syncUninterruptibly();
    }

    @Override
    public SimulatorAddress getAddress() {
        return localAddress;
    }

    @Override
    public int getPort() {
        return port;
    }

    @Override
    public ConcurrentMap<String, ResponseFuture> getFutureMap() {
        return futureMap;
    }

    @Override
    public ResponseFuture submit(SimulatorAddress destination, SimulatorOperation operation) {
        return submit(localAddress, destination, operation);
    }

    @Override
    public Response write(SimulatorAddress destination, SimulatorOperation operation) {
        return write(localAddress, destination, operation);
    }

    @Override
    public Response write(SimulatorAddress source, SimulatorAddress destination, SimulatorOperation operation) {
        try {
            return writeAsync(source, destination, operation).get();
        } catch (InterruptedException e) {
            throw new SimulatorProtocolException("ResponseFuture.get() got interrupted!", e);
        }
    }

    @Override
    public ResponseFuture writeAsync(SimulatorAddress destination, SimulatorOperation operation) {
        return writeAsync(localAddress, destination, operation);
    }

    @Override
    public ResponseFuture writeAsync(SimulatorAddress source, SimulatorAddress destination,
            SimulatorOperation operation) {
        SimulatorMessage message = createSimulatorMessage(source, destination, operation);
        return writeAsync(message);
    }

    int getMessageQueueSize() {
        return messageQueue.size();
    }

    ResponseFuture submit(SimulatorAddress source, SimulatorAddress destination, SimulatorOperation operation) {
        SimulatorMessage message = createSimulatorMessage(source, destination, operation);
        String futureKey = createFutureKey(source, message.getMessageId(), 0);
        ResponseFuture responseFuture = createInstance(messageQueueFutures, futureKey);
        messageQueue.add(message);
        return responseFuture;
    }

    private SimulatorMessage createSimulatorMessage(SimulatorAddress src, SimulatorAddress dst,
            SimulatorOperation op) {
        return new SimulatorMessage(dst, src, messageIds.incrementAndGet(), getOperationType(op), toJson(op));
    }

    private ResponseFuture writeAsync(SimulatorMessage message) {
        long messageId = message.getMessageId();
        String futureKey = createFutureKey(message.getSource(), messageId, addressIndex);
        ResponseFuture future = createInstance(futureMap, futureKey);
        if (LOGGER.isTraceEnabled()) {
            LOGGER.trace(format("[%d] %s created ResponseFuture %s", messageId, localAddress, futureKey));
        }
        getChannelGroup().writeAndFlush(message);

        return future;
    }

    private final class MessageQueueThread extends Thread {

        private static final int WAIT_FOR_EMPTY_QUEUE_MILLIS = 100;

        private MessageQueueThread() {
            super("ServerConnectorMessageQueueThread");
        }

        @Override
        public void run() {
            while (true) {
                try {
                    SimulatorMessage message = messageQueue.take();
                    if (POISON_PILL.equals(message)) {
                        LOGGER.info("ServerConnectorMessageQueueThread received POISON_PILL and will stop...");
                        break;
                    }

                    Response response = writeAsync(message).get();

                    String futureKey = createFutureKey(message.getSource(), message.getMessageId(), 0);
                    ResponseFuture responseFuture = messageQueueFutures.get(futureKey);
                    if (responseFuture != null) {
                        responseFuture.set(response);
                    }

                    ResponseType responseType = response.getFirstErrorResponseType();
                    if (!responseType.equals(ResponseType.SUCCESS)) {
                        LOGGER.error("Got response type " + responseType + " for " + message);
                    }
                } catch (Exception e) {
                    LOGGER.error("Error while sending message from messageQueue", e);
                    throw new SimulatorProtocolException("Error while sending message from messageQueue", e);
                }
            }
        }

        public void shutdown() {
            messageQueue.add(POISON_PILL);

            SimulatorMessage message = messageQueue.peek();
            while (message != null) {
                if (!POISON_PILL.equals(message)) {
                    int queueSize = messageQueue.size();
                    LOGGER.info(
                            format("%d messages pending on messageQueue, first message: %s", queueSize, message));
                }
                sleepMillis(WAIT_FOR_EMPTY_QUEUE_MILLIS);
                message = messageQueue.peek();
            }

            joinThread(messageQueueThread);
        }
    }
}