com.openteach.diamond.network.waverider.network.DefaultNetWorkClient.java Source code

Java tutorial

Introduction

Here is the source code for com.openteach.diamond.network.waverider.network.DefaultNetWorkClient.java

Source

/**
 * Copyright 2013 openteach
 *
 *  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.openteach.diamond.network.waverider.network;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.openteach.diamond.network.waverider.command.Command;
import com.openteach.diamond.network.waverider.command.CommandFactory;
import com.openteach.diamond.network.waverider.config.WaveriderConfig;

/**
 * <p>
 * TCP/IP?Slave
 * </p>
 * 
 * @author <a href="mailto:sihai@taobao.com">sihai</a>
 *
 */
public class DefaultNetWorkClient implements NetWorkClient, Runnable {

    private static final Log logger = LogFactory.getLog(DefaultNetWorkClient.class);

    private String hostName; // ??
    private int port; // ??
    private SocketChannel socketChannel; // Socket?
    private Selector selector; // ?
    private Thread thread; // ?, ?
    private AtomicInteger state; // ?
    private AtomicBoolean isWeakuped; // ?
    private SocketChannelOPSChangeRequest opsChangeRequest; // ?

    // Buffer
    private BlockingQueue<ByteBuffer> inputBuffer; // ?
    private BlockingQueue<Command> outputBuffer; // ?

    private byte[] waitMoreDataLock; // 

    //=============================================================
    //            
    //=============================================================
    public DefaultNetWorkClient() {
        this(null, WaveriderConfig.WAVERIDER_DEFAULT_PORT);
    }

    public DefaultNetWorkClient(String hostName) {
        this(hostName, WaveriderConfig.WAVERIDER_DEFAULT_PORT);
    }

    public DefaultNetWorkClient(String hostName, int port) {
        this.hostName = hostName;
        this.port = port;
        state = new AtomicInteger(NetworkStateEnum.NETWORK_STATE_DISCONNECT.value);
        isWeakuped = new AtomicBoolean(false);
        inputBuffer = new LinkedBlockingQueue<ByteBuffer>();
        outputBuffer = new LinkedBlockingQueue<Command>();
        waitMoreDataLock = new byte[0];
    }

    //=============================================================
    //            LifeCycle
    //=============================================================
    @Override
    public boolean init() {
        state.set(NetworkStateEnum.NETWORK_STATE_DISCONNECT.value);
        isWeakuped.set(false);
        thread = new Thread(this, NET_WORK_CLIENT_THREAD_NAME);
        thread.setDaemon(true);
        return true;
    }

    @Override
    public boolean start() {
        // ?
        if (hostName == null || port == 0) {
            throw new IllegalArgumentException("hostName and port must be supply");
        }
        // ?
        while (!connect() && !Thread.currentThread().isInterrupted()) {
            try {
                logger.warn("Can not connect to server , so sleep 60s, then try again");
                Thread.sleep(60 * 1000);
            } catch (InterruptedException e) {
                // 
                logger.error("OOPSException", e);
                state.set(NetworkStateEnum.NETWORK_STATE_DISCONNECT.value);
                Thread.currentThread().interrupt();
            }
        }

        // ??
        thread.start();

        return state.get() == NetworkStateEnum.NETWORK_STATE_CONNECTED.value;
    }

    @Override
    public boolean stop() {
        // ?
        thread.interrupt();
        // ?
        disconnect();
        inputBuffer.clear();
        outputBuffer.clear();
        thread = null;
        socketChannel = null;
        selector = null;
        isWeakuped.set(false);

        return true;
    }

    @Override
    public boolean restart() {
        return stop() && init() && start();
    }

    @Override
    public boolean notifyRead(SocketChannel channel) {
        logger.debug("notifyRead");
        if (state.get() == NetworkStateEnum.NETWORK_STATE_CONNECTED.value) {
            // 
            opsChangeRequest.addOps(SelectionKey.OP_READ);
            // ?selector
            weakup();
            return true;
        }

        return false;
    }

    @Override
    public boolean notifyWrite(SocketChannel channel) {
        logger.debug("notifyWrite");
        if (state.get() == NetworkStateEnum.NETWORK_STATE_CONNECTED.value) {
            // 
            opsChangeRequest.addOps(SelectionKey.OP_WRITE);
            // ?selector
            weakup();
            return true;
        }

        return false;
    }

    @Override
    public void waitMoreData(long timeout) throws InterruptedException {
        synchronized (waitMoreDataLock) {
            waitMoreDataLock.wait(timeout);
        }
    }

    @Override
    public void run() {

        while (!Thread.currentThread().isInterrupted()) {
            try {
                dispatch();
            } catch (IOException e) {
                logger.error("OOPSException", e);
                // ?
                logger.warn(String.format("Slave try to reconnect to %s", hostName));
                reconnect();
            } catch (InterruptedException e) {
                // 
                logger.error("OOPSException", e);
                Thread.currentThread().interrupt();
            }
        }
        logger.info(new StringBuilder("Waverider-NetWork-Client stoped ").toString());
    }

    // ?
    private boolean connect() {
        try {
            state.set(NetworkStateEnum.NETWORK_STATE_CONNECTING.value);
            socketChannel = SocketChannel.open();
            // 
            socketChannel.connect(new InetSocketAddress(hostName, port));
            // ??
            socketChannel.configureBlocking(false);
            selector = Selector.open();
            socketChannel.register(selector, SelectionKey.OP_READ);
            opsChangeRequest = new SocketChannelOPSChangeRequest(socketChannel, 0);
            // 
            state.set(NetworkStateEnum.NETWORK_STATE_CONNECTED.value);
            logger.warn(String.format("Slave connected to %s", hostName));
            return true;
        } catch (IOException e) {
            logger.error("OOPSException", e);
        }
        state.set(NetworkStateEnum.NETWORK_STATE_DISCONNECT.value);
        return false;
    }

    // 
    private void disconnect() {
        state.set(NetworkStateEnum.NETWORK_STATE_DISCONNECT.value);
        try {
            selector.close();
        } catch (IOException e) {
            logger.error("OOPSException", e);
        }
        try {
            socketChannel.close();
        } catch (IOException e) {
            logger.error("OOPSException", e);
        }
        socketChannel = null;
        selector = null;
        opsChangeRequest = null;
        isWeakuped.set(false);
    }

    // ?
    private void reconnect() {
        // ?
        disconnect();
        while (!connect() && !Thread.currentThread().isInterrupted()) {
            try {
                logger.warn("Can not connect to server , so sleep 60s, then try again");
                Thread.sleep(60 * 1000);
            } catch (InterruptedException e) {
                // 
                logger.error("OOPSException", e);
                Thread.currentThread().interrupt();
            }
        }
    }

    /**
     * ?, ?, ?, ??
     * @throws IOException
     * @throws InterruptedException
     */
    private void dispatch() throws IOException, InterruptedException {
        logger.debug("try dispatch");
        SelectionKey key = null;
        key = opsChangeRequest.getChannel().keyFor(selector);
        if (key != null) {
            if ((opsChangeRequest.getOps() & SelectionKey.OP_WRITE) == SelectionKey.OP_WRITE) {
                // 
                key.interestOps(SelectionKey.OP_WRITE);
                opsChangeRequest.clearOps(SelectionKey.OP_WRITE);
            } else if ((opsChangeRequest.getOps() & SelectionKey.OP_READ) == SelectionKey.OP_READ) {
                key.interestOps(SelectionKey.OP_READ);
                opsChangeRequest.clearOps(SelectionKey.OP_READ);
            }
        }

        // ?,  
        isWeakuped.set(false);
        if (selector.select(WaveriderConfig.WAVERIDER_DEFAULT_NETWORK_TIME_OUT) <= 0) {
            return;
        }

        // ?
        Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
        while (iterator.hasNext()) {
            key = (SelectionKey) iterator.next();
            iterator.remove();
            if (!key.isValid()) {
                continue;
            } else if (key.isReadable()) {
                onRead(key);
            } else if (key.isWritable()) {
                onWrite(key);
            }
        }
    }

    /**
     * ??
     * @param key
     * @throws IOException
     */
    private void onConnected(SelectionKey key) throws IOException {
        if (socketChannel.isConnectionPending()) {
            socketChannel.finishConnect();
            key.interestOps(SelectionKey.OP_READ);
            // ?
            state.set(NetworkStateEnum.NETWORK_STATE_CONNECTED.value);
            logger.info(new StringBuilder("Connected to server:").append(hostName).append(":").append(port));
        }
    }

    /**
     * ?, ?inputBuffer
     * @param key
     * @throws IOException
     * @throws InterruptedException
     */
    private void onRead(SelectionKey key) throws IOException, InterruptedException {
        logger.debug("onRead");
        // ?
        ByteBuffer readByteBuffer = ByteBuffer.allocate(NetWorkConstants.DEFAULT_NETWORK_BUFFER_SIZE);
        int ret = 0;
        do {
            ret = socketChannel.read(readByteBuffer);
        } while (ret > 0);

        // , ???
        if (ret == -1) {
            throw new IOException("EOF");
        }
        readByteBuffer.flip();
        if (readByteBuffer.hasRemaining()) {
            inputBuffer.put(readByteBuffer);
            synchronized (waitMoreDataLock) {
                waitMoreDataLock.notifyAll();
            }
        }

        //logger.info("Slave read " + readByteBuffer.remaining() + " bytes");
    }

    /**
     * ?, outputBuffer
     * ?
     * @param key
     * @throws IOException
     */
    private void onWrite(SelectionKey key) throws IOException {
        logger.debug("onWrite");
        key.interestOps(key.interestOps() & ~SelectionKey.OP_WRITE);
        Command command = null;
        Packet packet = null;
        ByteBuffer data = null;
        //int size = 0;
        while ((command = outputBuffer.poll()) != null) {
            //size = 0;
            // ???
            packet = Packet.newDataPacket(command);
            // ???ByteBuffer
            data = packet.marshall();
            //size = data.remaining();
            while (data.hasRemaining()) {
                socketChannel.write(data);
            }
            socketChannel.socket().getOutputStream().flush();
            //logger.info("Slave write one packet, " + size + " bytes");
        }
        //key.interestOps(SelectionKey.OP_READ);
    }

    /**
     * ?selector, ??
     */
    private void weakup() {
        logger.debug("try to weakup");
        if (isWeakuped.compareAndSet(false, true)) {
            this.selector.wakeup();
            logger.debug("weakuped");
        }
    }

    // parse network data, convert to command
    private Command _parse_() throws IOException, InterruptedException {
        return CommandFactory.unmarshallCommandFromPacket(Packet.parse(inputBuffer, this, socketChannel));
    }

    @Override
    public void send(Command command) throws InterruptedException {
        outputBuffer.put(command);
        if (state.get() == NetworkStateEnum.NETWORK_STATE_CONNECTED.value) {
            notifyWrite(socketChannel);
        } else {
            //logger.warn("Not connected to server");
            // ??
            //restart();
            // ?
            Thread.sleep(1000);
        }
    }

    @Override
    public Command receive() throws IOException, InterruptedException {
        if (state.get() == NetworkStateEnum.NETWORK_STATE_CONNECTED.value) {
            return _parse_();
        } else {
            ///logger.warn("Not connected to server");
            // ??
            //restart();
            // ?
            Thread.sleep(1000);
        }
        return null;
    }

    // ?
    private enum NetworkStateEnum {
        NETWORK_STATE_CONNECTING(0, "Connecting"), NETWORK_STATE_CONNECTED(1,
                "Connected"), NETWORK_STATE_DISCONNECT(2, "Disconnect");
        private int value;
        private String desc;

        private NetworkStateEnum(int value, String desc) {
            this.value = value;
            this.desc = desc;
        }
    }
}