Package com.addthis.meshy

Source Code of com.addthis.meshy.ChannelState

/*
* 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.addthis.meshy;

import java.net.InetSocketAddress;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import com.addthis.basis.util.Parameter;

import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.ChannelFutureListener;
import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


/**
* TODO implement pinger to teardown "dead" peers
*/
public class ChannelState extends SimpleChannelHandler {

    private static final Logger log = LoggerFactory.getLogger(ChannelState.class);
    private static final int excessiveTargets = Parameter.intValue("meshy.channel.report.targets", 2000);
    private static final int excessiveSources = Parameter.intValue("meshy.channel.report.sources", 2000);

    protected static final BufferAllocator bufferFactory = new BufferAllocator();

    protected static enum READMODE {
        ReadType, ReadSession, ReadLength, ReadData
    }

    public static ChannelBuffer allocateSendBuffer(int type, int session, byte[] data) {
        return allocateSendBuffer(type, session, data, 0, data.length);
    }

    public static ChannelBuffer allocateSendBuffer(int type, int session, byte[] data, int off, int len) {
        ChannelBuffer sendBuffer = allocateSendBuffer(type, session, len);
        sendBuffer.writeBytes(data, off, len);
        return sendBuffer;
    }

    public static ChannelBuffer allocateSendBuffer(int type, int session, int length) {
        ChannelBuffer sendBuffer = bufferFactory.allocateBuffer(4 + 4 + 4 + length);
        sendBuffer.writeInt(type);
        sendBuffer.writeInt(session);
        sendBuffer.writeInt(length);
        return sendBuffer;
    }

    public static ChannelBuffer allocateSendBuffer(int type, int session, ChannelBuffer from, int length) {
        ChannelBuffer sendBuffer = bufferFactory.allocateBuffer(4 + 4 + 4 + length);
        sendBuffer.writeInt(type);
        sendBuffer.writeInt(session);
        sendBuffer.writeInt(length);
        from.readBytes(sendBuffer, length);
        return sendBuffer;
    }

    public static void returnSendBuffer(ChannelBuffer buffer) {
        bufferFactory.returnBuffer(buffer);
    }

    private final ConcurrentHashMap<Integer, SessionHandler> targetHandlers = new ConcurrentHashMap<>();
    private final ConcurrentHashMap<Integer, SourceHandler> sourceHandlers = new ConcurrentHashMap<>();
    private final ChannelMaster master;
    private final Channel channel;

    private ChannelBuffer buffer = bufferFactory.allocateBuffer(16384);
    private READMODE mode = READMODE.ReadType;
    private int type;
    private int session;
    private int length;
    private String name;
    //    private int remotePort;
    private InetSocketAddress remoteAddress;

    ChannelState(ChannelMaster master, Channel channel) {
        super();
        this.master = master;
        this.channel = channel;
    }

    @Override
    public String toString() {
        return master + "[CS#" + hashCode() + ":n=" + getName() + ",r=" + remoteAddress + ",c=" + (channel != null ? channel.getRemoteAddress() : "null") + ",t=" + targetHandlers.size() + ",s=" + sourceHandlers.size() + "]";
    }

    /* helper to debug close() with open sessions/targets */
    protected void debugSessions() {
        if (!targetHandlers.isEmpty()) {
            log.info(this + " targets --> " + targetHandlers);
        }
        if (!sourceHandlers.isEmpty()) {
            log.info(this + " sources --> " + sourceHandlers);
        }
    }

    public boolean send(final ChannelBuffer sendBuffer, final SendWatcher watcher, final int reportBytes) {
        if (channel.isOpen()) {
            ChannelFuture future = channel.write(sendBuffer);
            future.addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture ignored) throws Exception {
                    master.sentBytes(reportBytes);
                    returnSendBuffer(sendBuffer);
                    if (watcher != null) {
                        watcher.sendFinished(reportBytes);
                    }
                }
            });
            return true;
        } else {
            if (reportBytes > 0) {
                /**
                 * if bytes == 0 then it's a sendComplete() and
                 * there are plenty of legit cases when a client would
                 * disconnect before sendComplete() makes it back. best
                 * example is StreamService when EOF framing tells the
                 * client we're done before sendComplete() framing does.
                 */
                log.info(this + " writing [" + reportBytes + "] to dead channel");
                if (watcher != null) {
                    /**
                     * for accounting, rate limiting reasons, we have to report these as sent.
                     * no need to report 0 bytes since it's an accounting no-op.
                     */
                    watcher.sendFinished(reportBytes);
                }
            }
            return false;
        }
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        log.debug(this + " setName=" + name);
        this.name = name;
    }

    public InetSocketAddress getRemoteAddress() {
        return remoteAddress;
    }

    public void setRemoteAddress(InetSocketAddress addr) {
        this.remoteAddress = addr;
    }

    public Channel getChannel() {
        return channel;
    }

    public InetSocketAddress getChannelRemoteAddress() {
        return channel != null ? (InetSocketAddress) channel.getRemoteAddress() : null;
    }

    public ChannelMaster getChannelMaster() {
        return master;
    }

    public void addSourceHandler(int sessionID, SourceHandler handler) {
        sourceHandlers.put(sessionID, handler);
        if (sourceHandlers.size() >= excessiveSources) {
            log.debug("excessive sources reached: " + sourceHandlers.size());
            if (log.isTraceEnabled()) {
                debugSessions();
            }
        }
    }

    public void removeHandlerOnComplete(TargetHandler targetHandler) {
        /* ensure target session closure */
        if (targetHandlers.remove(targetHandler.getSessionId()) != null) {
            log.debug("handler lingering on complete: " + targetHandler);
        }
    }

    public void channelConnected(ChannelStateEvent e) {
        log.debug(this + " channel:connect [" + this.hashCode() + "] " + e);
    }

    public void channelClosed(ChannelStateEvent e) throws Exception {
        log.debug(this + " channel:close [" + this.hashCode() + "] " + e);
        for (Map.Entry<Integer, SessionHandler> entry : targetHandlers.entrySet()) {
            entry.getValue().receiveComplete(this, entry.getKey());
        }
        for (Map.Entry<Integer, SourceHandler> entry : sourceHandlers.entrySet()) {
            entry.getValue().receiveComplete(this, entry.getKey());
        }
    }

    public void messageReceived(MessageEvent msg) {
        if (log.isTraceEnabled()) {
            log.trace(this + " recv msg=" + msg);
        }
        final ChannelBuffer in = (ChannelBuffer) msg.getMessage();
        master.recvBytes(in.readableBytes());
        if (buffer.writableBytes() >= in.readableBytes()) {
            buffer.writeBytes(in);
        } else {
            ChannelBuffer out = bufferFactory.allocateBuffer(in.readableBytes() + buffer.readableBytes());
            out.writeBytes(buffer);
            out.writeBytes(in);
            returnSendBuffer(buffer);
            buffer = out;
            if (log.isDebugEnabled()) {
                log.debug(this + " recv.reallocate: " + out.writableBytes());
            }
        }
        loop:
        while (true) {
            switch (mode) {
                // zero signifies a reply to a source
                case ReadType:
                    if (buffer.readableBytes() < 4) {
                        break loop;
                    }
                    type = buffer.readInt();
                    mode = READMODE.ReadSession;
                    continue;
                case ReadSession:
                    if (buffer.readableBytes() < 4) {
                        break loop;
                    }
                    session = buffer.readInt();
                    mode = READMODE.ReadLength;
                    continue;
                    // zero length signifies end of session
                case ReadLength:
                    if (buffer.readableBytes() < 4) {
                        break loop;
                    }
                    length = buffer.readInt();
                    mode = READMODE.ReadData;
                    continue;
                case ReadData:
                    int readable = buffer.readableBytes();
                    if (readable < length) {
                        break loop;
                    }
                    SessionHandler handler = null;
                    if (type == MeshyConstants.KEY_RESPONSE) {
                        handler = sourceHandlers.get(session);
                    } else {
                        handler = targetHandlers.get(session);
                        if (handler == null && master instanceof MeshyServer) {
                            handler = master.createHandler(type);
                            ((TargetHandler) handler).setContext(((MeshyServer) master), this, session);
                            if (log.isDebugEnabled()) {
                                log.debug(this + " createHandler " + handler + " session=" + session);
                            }
                            if (targetHandlers.put(session, handler) != null) {
                                log.debug("clobbered session " + session + " with " + handler);
                            }
                            if (targetHandlers.size() >= excessiveTargets) {
                                log.debug("excessive targets reached, current targetHandlers = " + targetHandlers.size());
                                if (log.isTraceEnabled()) {
                                    debugSessions();
                                }
                            }
                        }
                    }
                    if (handler != null) {
                        try {
                            if (length == 0) {
                                handler.receiveComplete(this, session);
                                if (type == MeshyConstants.KEY_RESPONSE) {
                                    sourceHandlers.remove(session);
                                    if (log.isDebugEnabled()) {
                                        log.debug(this + " dropSession session=" + session);
                                    }
                                } else {
                                    targetHandlers.remove(session);
                                }
                            } else {
                                handler.receive(this, session, length, buffer);
                            }
                        } catch (Exception ex) {
                            log.warn("messageReceived error", ex);
                        }
                    }
                    int read = readable - buffer.readableBytes();
                    if (read < length) {
                        if (handler != null || log.isDebugEnabled()) {
                            log.debug(this + " recv type=" + type + " handler=" + handler + " ssn=" + session + " did not consume all bytes (read=" + read + " of " + length + ")");
                        }
                        buffer.skipBytes(length - read);
                    }
                    mode = READMODE.ReadType;
                    continue;
                default:
                    throw new RuntimeException("invalid state");
            }
        }
        buffer.discardReadBytes();
    }
}
TOP

Related Classes of com.addthis.meshy.ChannelState

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.