Package net.sf.cindy.session.nio.reactor

Source Code of net.sf.cindy.session.nio.reactor.DefaultReactor$ReactorHandlerInterest

/*
* Copyright 2004-2006 the original author or authors.
*
* 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 net.sf.cindy.session.nio.reactor;

import java.io.IOException;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Map;

import net.sf.cindy.util.ChannelUtils;
import net.sf.cindy.util.Configuration;
import net.sf.cindy.util.ElapsedTime;
import net.sf.cindy.util.LogThreadGroup;
import net.sf.cindy.util.NamedThreadFactory;

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

import edu.emory.mathcs.backport.java.util.Queue;
import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap;
import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentLinkedQueue;
import edu.emory.mathcs.backport.java.util.concurrent.ThreadFactory;

/**
* Default reactor implementation.
*
* @author <a href="chenrui@gmail.com">Roger Chen</a>
* @version $id$
*/
public class DefaultReactor implements Reactor {

    private static final Log log = LogFactory.getLog(DefaultReactor.class);

    private static final ThreadFactory reactorThreadFactory = new NamedThreadFactory(
            new ThreadGroup(LogThreadGroup.CINDY_THREAD_GROUP, "Reactor"),
            false, "Reactor");

    private static final int SELECT_TIMEOUT = 1000;

    /**
     * All operate need be done in the same thread.
     */
    private final Queue registerColl, deregisterColl, interestColl;

    /**
     * The attachment associted with inner <code>SelectionKey</code>.
     *
     * @author <a href="chenrui@gmail.com">Roger Chen</a>
     * @version $id$
     */
    private static class Attachment {

        private final ReactorHandler handler;

        private int idleTime; // store session idle time
        private boolean writing; // do not check session timeout when writing

        public Attachment(ReactorHandler handler) {
            this.handler = handler;
        }

    }

    /**
     * Saved all registed <code>ReactorHandlerAttachment</code>. Key is
     * <code>ReactorHandler</code>, Value is <code>Attachment</code>.
     */
    private final Map registered = new ConcurrentHashMap();

    private Selector selector;
    private Thread selectThread = null;
    private volatile boolean close = false;

    /**
     * when no session registed on the reator, don't stop select immediately.
     */
    private static final int CLOSE_TICK = Configuration
            .getDispatcherKeepAliveTime()
            / SELECT_TIMEOUT;
    private int currentTick = 0;

    /**
     * judge session timeout.
     */
    private ElapsedTime lastSelectTime;

    public DefaultReactor() {
        registerColl = new ConcurrentLinkedQueue();
        deregisterColl = new ConcurrentLinkedQueue();
        interestColl = new ConcurrentLinkedQueue();
    }

    private synchronized void start() {
        if (selectThread != null)
            return;

        lastSelectTime = new ElapsedTime();
        try {
            selector = Selector.open();
            selectThread = reactorThreadFactory.newThread(new Runnable() {

                public void run() {
                    try {
                        while (!close) {
                            beforeSelect();
                            if (close) // after beforeSelect, close may be true
                                break;
                            try {
                                selector.select(SELECT_TIMEOUT);
                            } catch (IOException e) {
                                log.error(e, e);
                                break;
                            }
                            afterSelect();
                        }
                    } finally {
                        DefaultReactor.this.stop();
                    }
                }
            });
            selectThread.start();
        } catch (IOException e) {
            ChannelUtils.close(selector);
            log.error(e, e);
        }
    }

    private synchronized void stop() {
        for (Iterator iter = registered.keySet().iterator(); iter.hasNext();) {
            ReactorHandler handler = (ReactorHandler) iter.next();
            dispatchDeregistered(handler);
        }
        registered.clear();
        lastSelectTime = null;
        ChannelUtils.close(selector);
        selector = null;
        registerColl.clear();
        interestColl.clear();
        deregisterColl.clear();
        currentTick = 0;
        close = false;
        selectThread = null;
    }

    private void dispatchDeregistered(ReactorHandler handler) {
        SelectableChannel[] channels = handler.getChannels();
        for (int i = 0; i < channels.length; i++) {
            SelectionKey key = channels[i].keyFor(selector);
            if (key != null)
                key.cancel();
        }
        handler.onDeregistered();
    }

    public void register(ReactorHandler handler) {
        if (Thread.currentThread() == selectThread) {
            changeRegister(new Attachment(handler));
        } else {
            registerColl.offer(new Attachment(handler));
            start(); // auto start when register
            selector.wakeup();
        }
    }

    public void deregister(ReactorHandler handler) {
        if (Thread.currentThread() == selectThread) {
            changeDeregister(handler);
        } else {
            deregisterColl.offer(handler);
            selector.wakeup();
        }
    }

    /**
     * Store reactor handler and interest ops.
     *
     * @author <a href="chenrui@gmail.com">Roger Chen</a>
     * @version $id$
     */
    private static class ReactorHandlerInterest {

        private final ReactorHandler handler;
        private final int ops;

        public ReactorHandlerInterest(ReactorHandler handler, int ops) {
            this.handler = handler;
            this.ops = ops;
        }

    }

    public void interest(ReactorHandler handler, int ops) {
        if (Thread.currentThread() == selectThread) {
            changeInterest(new ReactorHandlerInterest(handler, ops));
        } else {
            interestColl.offer(new ReactorHandlerInterest(handler, ops));
            selector.wakeup();
        }
    }

    protected void beforeSelect() {
        Attachment attachment = null;
        while ((attachment = (Attachment) registerColl.poll()) != null)
            changeRegister(attachment);

        ReactorHandlerInterest interest = null;
        while ((interest = (ReactorHandlerInterest) interestColl.poll()) != null)
            changeInterest(interest);

        ReactorHandler handler = null;
        while ((handler = (ReactorHandler) deregisterColl.poll()) != null)
            changeDeregister(handler);

        checkNeedStop();
    }

    private void changeRegister(Attachment attachment) {
        ReactorHandler handler = attachment.handler;
        if (registered.containsKey(handler))
            return;
        SelectableChannel[] channels = handler.getChannels();
        try {
            for (int i = 0; i < channels.length; i++) {
                SelectableChannel channel = channels[i];
                channel.configureBlocking(false);
                int validOps = channel.validOps();
                // It's a bug of java nio, see:
                // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4960791
                boolean isConnected = (validOps & SelectionKey.OP_CONNECT) != 0
                        && ((SocketChannel) channel).isConnected();
                channel.register(selector, isConnected ? SelectionKey.OP_READ
                        : (validOps & ~SelectionKey.OP_WRITE), attachment);
            }
            registered.put(handler, attachment);
            handler.onRegistered();
        } catch (IOException e) {
            log.error(e, e);
            dispatchDeregistered(handler);
        }
    }

    private void changeInterest(ReactorHandlerInterest interest) {
        ReactorHandler handler = interest.handler;
        Attachment attachment = (Attachment) registered.get(handler);
        if (attachment == null)
            return;

        // write completed
        if ((interest.ops & OP_NON_WRITE) != 0)
            attachment.writing = false;

        SelectableChannel[] channels = handler.getChannels();
        for (int i = 0; i < channels.length; i++) {
            SelectableChannel channel = channels[i];
            SelectionKey key = channel.keyFor(selector);
            if (key != null)
                key.interestOps(key.interestOps()
                        | (channel.validOps() & interest.ops));
        }
    }

    private void changeDeregister(ReactorHandler handler) {
        if (registered.remove(handler) != null)
            dispatchDeregistered(handler);
    }

    private void checkNeedStop() {
        if (registered.isEmpty()) {
            if (++currentTick >= CLOSE_TICK)
                close = true;
        } else
            currentTick = 0;
    }

    protected void afterSelect() {
        checkSessionTimeout();
        processSelectedKeys();
    }

    private void checkSessionTimeout() {
        // reset active handler
        for (Iterator iter = selector.selectedKeys().iterator(); iter.hasNext();) {
            SelectionKey key = (SelectionKey) iter.next();
            Attachment attachment = (Attachment) key.attachment();
            attachment.idleTime = 0;
        }

        // imporve performance, do not check session timeout frequently
        if (lastSelectTime.getElapsedTime() < SELECT_TIMEOUT)
            return;
        int interval = (int) lastSelectTime.reset();

        // check timeout
        Loop: for (Iterator iter = registered.values().iterator(); iter
                .hasNext();) {
            Attachment attachment = (Attachment) iter.next();
            ReactorHandler handler = attachment.handler;
            int sessionTimeout = handler.getSession().getSessionTimeout();
            if (sessionTimeout <= 0 || attachment.writing) {
                attachment.idleTime = 0;
                continue;
            }

            SelectableChannel[] channels = handler.getChannels();
            for (int i = 0; i < channels.length; i++) {
                SelectionKey key = channels[i].keyFor(selector);
                if ((key.interestOps() & key.readyOps()) != 0) {
                    continue Loop;
                }
            }

            // no event happen
            int newIdleTime = interval + attachment.idleTime;
            if (newIdleTime > sessionTimeout) {
                attachment.idleTime = 0;
                handler.onTimeout();
            } else
                attachment.idleTime = newIdleTime;
        }
    }

    private void processSelectedKeys() {
        for (Iterator iter = selector.selectedKeys().iterator(); iter.hasNext();) {
            SelectionKey key = (SelectionKey) iter.next();
            iter.remove();

            key.interestOps(key.interestOps() & ~key.readyOps());
            Attachment attachment = (Attachment) key.attachment();
            ReactorHandler handler = attachment.handler;

            if (key.isWritable()) // do not check session timeout when writing
                attachment.writing = true;

            if (key.isAcceptable())
                handler.onAcceptable();
            if (key.isConnectable())
                handler.onConnectable();
            if (key.isValid() && key.isReadable())
                handler.onReadable();
            if (key.isValid() && key.isWritable())
                handler.onWritable();
        }
    }
}
TOP

Related Classes of net.sf.cindy.session.nio.reactor.DefaultReactor$ReactorHandlerInterest

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.