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

Java tutorial

Introduction

Here is the source code for com.openteach.diamond.network.waverider.network.DefaultNetWorkServer.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.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;

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

import com.openteach.diamond.network.waverider.config.WaveriderConfig;
import com.openteach.diamond.network.waverider.session.Session;
import com.openteach.diamond.network.waverider.session.SessionManager;

/**
 * <p>
 *    TCP/IPMaster?, NIO
 * </p>
 * 
 * @author <a href="mailto:sihai@taobao.com">sihai</a>
 *
 */
public class DefaultNetWorkServer implements NetWorkServer, Runnable {

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

    private String hostName; // ??
    private int port; // ??
    private ServerSocketChannel serverSocketChannel; // ?socket?
    private Selector selector; // ?
    private Thread netWorkServerThread; // ?, ?
    private SessionManager sessionManager; // ??
    //private ExecutorService readerExecutor;            // , ?, ??socket
    //private ExecutorService writerExecutor;            // , ?, ??socket
    private AtomicBoolean isWeakuped; // selector
    private ConcurrentHashMap<SocketChannel, SocketChannelOPSChangeRequest> opsChangeRequstMap; // ?

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

    public DefaultNetWorkServer(int port) {
        this(null, port);
    }

    public DefaultNetWorkServer(String hostName, int port) {
        this.hostName = hostName;
        this.port = port;
        isWeakuped = new AtomicBoolean(false);
    }

    //=============================================================
    //            LifeCycle
    //=============================================================
    @Override
    public boolean init() {
        try {
            selector = Selector.open();
            serverSocketChannel = ServerSocketChannel.open();
            serverSocketChannel.configureBlocking(false);
            serverSocketChannel.socket()
                    .bind(hostName == null ? new InetSocketAddress(port) : new InetSocketAddress(hostName, port));
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

            // ??
            // ?, socket?
            //readerExecutor = Executors.newFixedThreadPool(1, new WaveriderThreadFactory(NET_WORK_READER, null, true));
            //writerExecutor = Executors.newFixedThreadPool(1, new WaveriderThreadFactory(NET_WORK_WRITER, null, true));

            // ?
            netWorkServerThread = new Thread(this, NET_WORK_SERVER_THREAD_NAME);
            netWorkServerThread.setDaemon(true);
        } catch (IOException e) {
            logger.error("Init DefaultNetworkServer failed: ", e);
            throw new RuntimeException(e);
        }

        return true;
    }

    @Override
    public boolean start() {
        opsChangeRequstMap = new ConcurrentHashMap<SocketChannel, SocketChannelOPSChangeRequest>();
        // ??
        netWorkServerThread.start();
        return true;
    }

    @Override
    public boolean stop() {
        // ???
        //readerExecutor.shutdown();
        //writerExecutor.shutdown();
        // ??
        netWorkServerThread.interrupt();
        try {
            selector.close();
            serverSocketChannel.close();
            isWeakuped.set(false);
            opsChangeRequstMap.clear();
            opsChangeRequstMap = null;
        } catch (IOException e) {
            logger.error("OOPSException", e);
        }
        return true;
    }

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

    @Override
    public String getIp() {
        return serverSocketChannel.socket().getLocalSocketAddress().toString();
    }

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

    @Override
    public boolean notifyWrite(SocketChannel channel) {
        logger.debug("notifyWrite");
        if (channel == null) {
            return false;
        }

        SocketChannelOPSChangeRequest request = opsChangeRequstMap.get(channel);
        if (request == null) {
            // Socket
            return false;
        }

        // 
        request.addOps(SelectionKey.OP_WRITE);
        // ?selector
        weakup();

        return true;
    }

    @Override
    public boolean notifyRead(SocketChannel channel) {
        logger.debug("notifyRead");
        if (channel == null) {
            return false;
        }
        SocketChannelOPSChangeRequest request = opsChangeRequstMap.get(channel);
        if (request == null) {
            // Socket
            return false;
        }
        // 
        request.addOps(SelectionKey.OP_READ);
        // ?selector
        weakup();

        return true;
    }

    @Override
    public void waitMoreData(long timeout) throws InterruptedException {
        throw new UnsupportedOperationException();
    }

    private void dispatch() throws IOException {
        logger.debug("try dispatch");
        SelectionKey key = null;
        for (SocketChannelOPSChangeRequest request : opsChangeRequstMap.values()) {
            key = request.getChannel().keyFor(selector);
            if (key != null) {
                // 
                if ((request.getOps() & SelectionKey.OP_WRITE) == SelectionKey.OP_WRITE) {
                    key.interestOps(SelectionKey.OP_WRITE);
                    request.clearOps(SelectionKey.OP_WRITE);
                } else if ((request.getOps() & SelectionKey.OP_READ) == SelectionKey.OP_READ) {
                    key.interestOps(SelectionKey.OP_READ);
                    request.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();
            try {
                if (!key.isValid()) {
                    continue;
                } else if (key.isAcceptable()) {
                    onAccept(key);
                } else if (key.isReadable()) {
                    //readerExecutor.execute(new NetworkTask(key, NETWORK_OPERATION_READ));
                    onRead(key);
                } else if (key.isWritable()) {
                    //writerExecutor.execute(new NetworkTask(key, NETWORK_OPERATION_WRITE));
                    onWrite(key);
                }
            } catch (IOException e) {
                // 
                opsChangeRequstMap.remove((SocketChannel) key.channel());
                Session session = (Session) key.attachment();
                if (session != null) {
                    session.onException(e);
                    // Session
                    sessionManager.freeSession(session);
                }
                key.cancel();
                key.channel().close();
                e.printStackTrace();
                logger.error("OOPSException", e);
            }
        }
    }

    @Override
    public void run() {
        logger.info(new StringBuilder("Waverider-NetWork-Server started listen on ")
                .append(hostName == null ? "" : hostName + ":").append(port).toString());
        while (!Thread.currentThread().isInterrupted()) {
            try {
                dispatch();
            } catch (IOException e) {
                logger.error("OOPSException", e);
                e.printStackTrace();
            }
        }
        logger.info(new StringBuilder("Waverider-NetWork-Server stoped ").toString());
    }

    /**
     * ?, ?Session, ?Session, 
     * Session?
     * @param key
     * @throws IOException
     */
    private void onAccept(SelectionKey key) throws IOException {
        SocketChannel channel = ((ServerSocketChannel) key.channel()).accept();
        opsChangeRequstMap.put(channel, new SocketChannelOPSChangeRequest(channel, 0));
        if (logger.isWarnEnabled())
            logger.warn("Accept client from : " + channel.socket().getRemoteSocketAddress());
        channel.configureBlocking(false);
        Session session = sessionManager.newSession(this, channel, false);
        channel.register(selector, SelectionKey.OP_READ, session);
        session.start();
    }

    /**
     * ??, SessiononRead, ?Session?
     * @param key
     * @throws IOException
     * @throws InterruptedException
     */
    private void onRead(SelectionKey key) throws IOException {
        Session session = (Session) key.attachment();
        try {
            session.onRead();
        } catch (InterruptedException e) {
            logger.error("OOPS Exception:", e);
            Thread.currentThread().interrupt();
        }
    }

    /**
     * ??, SessiononWrite, Session??
     * @param key
     * @throws IOException
     */
    private void onWrite(SelectionKey key) throws IOException {
        key.interestOps(key.interestOps() & ~SelectionKey.OP_WRITE);
        Session session = (Session) key.attachment();
        session.onWrite();
        //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");
        }
    }

    @Override
    public void setSessionManager(SessionManager sessionManager) {
        this.sessionManager = sessionManager;
    }

    /**
     * ??
     * @author sihai
     *
     */
    private class NetworkTask implements Runnable {

        private SelectionKey key;
        private int operation;

        public NetworkTask(SelectionKey key, int operation) {
            this.key = key;
            this.operation = operation;
        }

        @Override
        public void run() {
            try {
                switch (operation) {
                case NETWORK_OPERATION_ACCEPT: {
                    onAccept(key);
                    break;
                }
                case NETWORK_OPERATION_READ: {
                    onRead(key);
                    break;
                }
                case NETWORK_OPERATION_WRITE: {
                    onWrite(key);
                    break;
                }
                }
            } catch (IOException e) {
                logger.error("OOPSException", e);
                try {
                    // 
                    Session session = (Session) key.attachment();
                    if (session != null) {
                        session.onException(e);
                        // Session
                        sessionManager.freeSession(session);
                    }
                    opsChangeRequstMap.remove((SocketChannel) key.channel());
                    key.cancel();
                    key.channel().close();
                } catch (IOException ex) {
                    logger.error("OOPSException", ex);
                    ex.printStackTrace();
                }
            }
        }
    }
}