edu.tsinghua.lumaqq.qq.net.Porter.java Source code

Java tutorial

Introduction

Here is the source code for edu.tsinghua.lumaqq.qq.net.Porter.java

Source

/*
* LumaQQ - Java QQ Client
*
* Copyright (C) 2004 notXX
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package edu.tsinghua.lumaqq.qq.net;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.DatagramChannel;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Vector;

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

import edu.tsinghua.lumaqq.qq.packets.PacketParseException;

/**
 * ??
 * 
 * @author notxx
 */
public final class Porter extends Thread {
    /** Logger */
    private static final Log log = LogFactory.getLog(Porter.class);

    /** ?? */
    protected boolean shutdown = false;
    /** ? */
    protected Selector selector;

    // port
    private List<IPort> ports;
    // proxy
    private List<IProxy> proxies;

    // 
    private Queue<Object> disposeQueue;

    // 
    private List<Object> newConnections;

    /**
     * Porter.
     */
    public Porter() {
        ports = new ArrayList<IPort>();
        proxies = new ArrayList<IProxy>();
        newConnections = new Vector<Object>();
        disposeQueue = new LinkedList<Object>();
        setName("Porter");
        setDaemon(true);
        // Selector
        try {
            selector = Selector.open();
        } catch (IOException e) {
            log.debug(e);
            throw new RuntimeException(e);
        }
    }

    /**
     * portporter
     * 
     * @param port
     *       IPort
     * @throws ClosedChannelException
     *       
     */
    public void register(IPort port) throws ClosedChannelException {
        SelectableChannel channel = port.channel();
        if (channel instanceof SocketChannel)
            channel.register(selector, SelectionKey.OP_CONNECT, port.getNIOHandler());
        else if (channel instanceof DatagramChannel)
            channel.register(selector, SelectionKey.OP_READ, port.getNIOHandler());
        if (!ports.contains(port))
            ports.add(port);
    }

    /**
     * ?channel
     * 
     * @param port
     * @param ops
     * @throws ClosedChannelException
     */
    public void register(IPort port, int ops) throws ClosedChannelException {
        SelectableChannel channel = port.channel();
        if (channel instanceof SocketChannel)
            channel.register(selector, ops, port.getNIOHandler());
        else if (channel instanceof DatagramChannel)
            channel.register(selector, ops, port.getNIOHandler());
        if (!ports.contains(port))
            ports.add(port);
    }

    /**
     * ???
     * 
     * @param proxy
     *       IProxy
     * @throws ClosedChannelException
     *       
     */
    public void register(IProxy proxy) throws ClosedChannelException {
        SelectableChannel channel = proxy.channel();
        if (channel instanceof SocketChannel)
            channel.register(selector, SelectionKey.OP_CONNECT, proxy.getNIOHandler());
        else if (channel instanceof DatagramChannel)
            channel.register(selector, SelectionKey.OP_READ, proxy.getNIOHandler());
        if (!proxies.contains(proxy))
            proxies.add(proxy);
    }

    /**
     * portportchannel
     * 
     * @param port
     *       IPort
     * @throws IOException
     */
    private void deregister(IPort port) {
        if (port == null)
            return;

        if (!ports.remove(port))
            return;
        SelectionKey key = port.channel().keyFor(selector);
        if (key != null)
            key.cancel();
        port.dispose();
    }

    /**
     * proxy
     * 
     * @param proxy
     */
    private void deregister(IProxy proxy) {
        if (proxy == null)
            return;

        if (!proxies.remove(proxy))
            return;
        SelectionKey key = proxy.channel().keyFor(selector);
        if (key != null)
            key.cancel();
        proxy.dispose();
    }

    /**
     * ??port
     * 
     * @param e
     *       ??Exception
     */
    private void dispatchErrorToAll(Exception e) {
        for (IPort port : ports)
            port.getNIOHandler().processError(e);
        for (IProxy proxy : proxies)
            proxy.getNIOHandler().processError(e);
    }

    /**
     * port??
     * @throws IOException
     */
    private void notifySend() {
        int size = ports.size();
        for (int i = 0; i < size; i++) {
            INIOHandler handler = null;
            try {
                handler = (ports.get(i)).getNIOHandler();
                handler.processWrite();
            } catch (IOException e) {
                log.error(e.getMessage());
                handler.processError(e);
            } catch (IndexOutOfBoundsException e) {
            }
        }

        size = proxies.size();
        for (int i = 0; i < size; i++) {
            INIOHandler handler = null;
            try {
                handler = (proxies.get(i)).getNIOHandler();
                handler.processWrite();
            } catch (IOException e) {
                log.error(e.getMessage());
                handler.processError(e);
            } catch (IndexOutOfBoundsException e) {
            }
        }
    }

    /**
     * ??IPort.
     * ???//.
     * @see IPort#send(ByteBuffer)
     * @see IPort#receive(ByteBuffer)
     * @see IPort#maintain()
     */
    @Override
    public void run() {
        log.debug("Porter??");
        int n = 0;
        while (!shutdown) {
            // do select
            try {
                n = selector.select(3000);
                // ?shutdownselector
                if (shutdown) {
                    selector.close();
                    break;
                }
            } catch (IOException e) {
                log.error(e.getMessage());
                dispatchErrorToAll(e);
            }

            // ?
            processDisposeQueue();

            // select0?
            if (n > 0) {
                for (Iterator<SelectionKey> i = selector.selectedKeys().iterator(); i.hasNext();) {
                    // Key
                    SelectionKey sk = i.next();
                    i.remove();
                    // ?
                    if (!sk.isValid())
                        continue;

                    // ?
                    INIOHandler handler = (INIOHandler) sk.attachment();
                    try {
                        if (sk.isConnectable())
                            handler.processConnect(sk);
                        else if (sk.isReadable())
                            handler.processRead(sk);
                    } catch (IOException e) {
                        log.error(e.getMessage());
                        handler.processError(e);
                    } catch (PacketParseException e) {
                        log.debug("?: " + e.getMessage());
                    } catch (RuntimeException e) {
                        log.error(e.getMessage());
                    }
                }

                n = 0;
            }

            checkNewConnection();
            notifySend();
        }

        selector = null;
        shutdown = false;
        log.debug("Porter?");
    }

    /**
     * 
     * 
     * @param p
     */
    public void addDisposeRequest(IPort p) {
        synchronized (disposeQueue) {
            disposeQueue.offer(p);
        }
    }

    /**
     * 
     * 
     * @param p
     */
    public void addDisposeRequest(IProxy p) {
        synchronized (disposeQueue) {
            disposeQueue.offer(p);
        }
    }

    /**
     * ??
     */
    private void checkNewConnection() {
        while (!newConnections.isEmpty()) {
            Object handler = newConnections.remove(0);
            if (handler instanceof IProxy) {
                try {
                    register((IProxy) handler);
                } catch (ClosedChannelException e1) {
                }
            } else if (handler instanceof IPort) {
                try {
                    register((IPort) handler);
                } catch (ClosedChannelException e1) {
                }
            }
        }
    }

    /**
     * ?
     */
    private void processDisposeQueue() {
        synchronized (disposeQueue) {
            while (!disposeQueue.isEmpty()) {
                Object obj = disposeQueue.poll();
                if (obj instanceof IPort)
                    deregister((IPort) obj);
                else if (obj instanceof IProxy)
                    deregister((IProxy) obj);
            }
        }
    }

    /**
     * porter
     */
    public void shutdown() {
        if (selector != null) {
            shutdown = true;
            selector.wakeup();
        }
    }

    /**
     * selector
     */
    public void wakeup() {
        selector.wakeup();
    }

    /**
     * selector?proxy
     * 
     * @param proxy
     */
    public void wakeup(Object handler) {
        newConnections.add(handler);
        selector.wakeup();
    }
}