org.commoncrawl.rpc.thriftrpc.ThriftRPCServerChannel.java Source code

Java tutorial

Introduction

Here is the source code for org.commoncrawl.rpc.thriftrpc.ThriftRPCServerChannel.java

Source

package org.commoncrawl.rpc.thriftrpc;

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you 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.
 */

import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.thrift.TException;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.protocol.TProtocolFactory;
import org.apache.thrift.server.TServer.AbstractServerArgs;
import org.apache.thrift.transport.TFramedTransport;
import org.apache.thrift.transport.TNonblockingServerTransport;
import org.apache.thrift.transport.TNonblockingSocket;
import org.apache.thrift.transport.TNonblockingTransport;
import org.apache.thrift.transport.TTransportException;
import org.commoncrawl.async.EventLoop;
import org.commoncrawl.async.Timer;
import org.commoncrawl.io.internal.NIOClientSocket;
import org.commoncrawl.io.internal.NIOServerSocketListener;
import org.commoncrawl.io.internal.NIOServerTCPSocket;
import org.commoncrawl.io.internal.NIOSocket;
import org.commoncrawl.util.shared.CCStringUtils;

/**
 * A nonblocking TServer implementation. This allows for fairness amongst all
 * connected clients in terms of invocations.
 *
 * This server is inherently single-threaded. If you want a limited thread pool
 * coupled with invocation-fairness, see THsHaServer.
 *
 * To use this server, you MUST use a TFramedTransport at the outermost
 * transport, otherwise this server will be unable to determine when a whole
 * method call has been read off the wire. Clients must also use TFramedTransport.
 */
public class ThriftRPCServerChannel implements NIOServerSocketListener {

    public static final Log LOG = LogFactory.getLog(ThriftRPCServerChannel.class);

    public static class Args extends AbstractNonblockingServerArgs<Args> {
        public Args(TNonblockingServerTransport transport) {
            super(transport);
        }
    }

    public static abstract class AbstractNonblockingServerArgs<T extends AbstractNonblockingServerArgs<T>>
            extends AbstractServerArgs<T> {
        public long maxReadBufferBytes = Long.MAX_VALUE;

        public AbstractNonblockingServerArgs(TNonblockingServerTransport transport) {
            super(transport);
            transportFactory(new TFramedTransport.Factory());
        }
    }

    /**
     * The maximum amount of memory we will allocate to client IO buffers at a
     * time. Without this limit, the server will gladly allocate client buffers
     * right into an out of memory exception, rather than waiting.
     */
    final long MAX_READ_BUFFER_BYTES;

    /**
     * How many bytes are currently allocated to read buffers.
     */
    long readBufferBytesAllocated = 0;

    final ThriftRPCMessageDispatcher processor;
    final TProtocolFactory inputProtocolFactory = new TBinaryProtocol.Factory();
    final TProtocolFactory outputProtocolFactory = new TBinaryProtocol.Factory();
    EventLoop eventLoop;
    boolean isServing = false;
    NIOServerTCPSocket _socket;
    InetSocketAddress _address;
    int _reconnectDelay = 0;
    Timer _reconnectTimer = null;
    private static int INITIAL_RECONNECT_DELAY = 1000;

    private enum State {
        OPEN, OPEN_CONNECTED, CLOSED
    }

    State _state = State.CLOSED;

    public ThriftRPCServerChannel(EventLoop eventLoop, InetSocketAddress bindAddress,
            ThriftRPCMessageDispatcher processor, long max_read_buffer_size) {

        MAX_READ_BUFFER_BYTES = max_read_buffer_size;
        this._address = bindAddress;
        this.processor = processor;
        this.eventLoop = eventLoop;
    }

    public void open() throws IOException {
        if (_state == State.CLOSED) {
            rebind();
        }
    }

    public void close() {
        if (_state != State.CLOSED) {
            release();
        }
    }

    void rebind() throws IOException {

        release();

        if (_reconnectDelay == 0) {
            _reconnectDelay = INITIAL_RECONNECT_DELAY;
            bind();
        } else {
            _reconnectTimer = new Timer(_reconnectDelay, false,

                    new Timer.Callback() {

                        // @Override
                        public void timerFired(Timer timer) {
                            try {
                                bind();
                            } catch (IOException e) {
                                e.printStackTrace();
                                try {
                                    rebind();
                                } catch (IOException e2) {

                                }

                            }
                        }
                    });

            eventLoop.setTimer(_reconnectTimer);
        }
    }

    void bind() throws IOException {
        // increate reconnect delay ...
        _reconnectDelay = _reconnectDelay * 2;
        _reconnectTimer = null;

        if (_socket == null) {
            _socket = new NIOServerTCPSocket(this);
            _socket.open(_address);
            eventLoop.getSelector().registerForAccept(_socket);
            // update state ...
            _state = State.OPEN_CONNECTED;
        }
    }

    void release() {
        // stop accepting sockets on this host ...
        if (_reconnectTimer != null) {
            eventLoop.cancelTimer(_reconnectTimer);
            _reconnectTimer = null;
        }
        if (_socket != null) {
            eventLoop.getSelector().cancelRegistration(_socket);
            _socket.close();
            _socket = null;
        }
        _state = State.CLOSED;
    }

    /**
     * Perform an invocation. This method could behave several different ways
     * - invoke immediately inline, queue for separate execution, etc.
     */
    protected boolean requestInvoke(ThriftRPCClientChannel channel) throws TException {
        // invoke on the channel 
        channel.invoke();
        // and prepare for the next frame 
        channel.prepareRead();
        return true;
    }

    /**
     * A ThriftAsyncRemoteClientChannel wants to change its selection preferences, but might not be
     * in the select thread.
     */
    protected void requestSelectInterestChange(final ThriftRPCClientChannel buffer) {
        eventLoop.setTimer(new Timer(0, false, new Timer.Callback() {

            @Override
            public void timerFired(Timer timer) {
                buffer.changeSelectInterests();
            }
        }));
    }

    /**
     * Do connection-close cleanup on a given SelectionKey.
     */
    private void cleanupSelectionkey(SelectionKey key) {
        // remove the records from the two maps
        ThriftRPCClientChannel buffer = (ThriftRPCClientChannel) key.attachment();
        if (buffer != null) {
            // close the buffer
            buffer.close();
        }
        // cancel the selection key
        key.cancel();
    }

    @Override
    public void Accepted(NIOClientSocket theSocket) throws IOException {
        SelectionKey clientKey = null;
        TNonblockingTransport client = null;
        try {
            // accept the connection
            client = new TNonblockingSocket((SocketChannel) theSocket.getChannel());
            try {
                clientKey = client.registerSelector(eventLoop.getSelector().getSelector(), SelectionKey.OP_READ);
            } catch (IOException e) {
                LOG.error(CCStringUtils.stringifyException(e));
                throw new TTransportException(e);
            }

            // add this key to the map
            ThriftRPCClientChannel frameBuffer = new ThriftRPCClientChannel(this, this, client, clientKey);
            clientKey.attach(frameBuffer);
        } catch (TTransportException tte) {
            // something went wrong accepting.
            LOG.warn("Exception trying to accept!", tte);
            tte.printStackTrace();
            if (clientKey != null)
                cleanupSelectionkey(clientKey);
            if (client != null)
                client.close();
        }
    }

    @Override
    public void Disconnected(NIOSocket theSocket, Exception optionalException) throws IOException {

    }

    @Override
    public void Excepted(NIOSocket socket, Exception e) {
    }

}