reactor.io.net.impl.netty.NettyNetChannelInboundHandler.java Source code

Java tutorial

Introduction

Here is the source code for reactor.io.net.impl.netty.NettyNetChannelInboundHandler.java

Source

/*
 * Copyright (c) 2011-2014 Pivotal Software, Inc.
 *
 *  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,
 *  WITHIN 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 reactor.io.net.impl.netty;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.EmptyByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import org.reactivestreams.Subscriber;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import reactor.io.buffer.Buffer;
import reactor.io.net.Spec;
import reactor.rx.subscription.PushSubscription;

/**
 * Netty {@link io.netty.channel.ChannelInboundHandler} implementation that passes data to a Reactor {@link
 * reactor.io.net.ChannelStream}.
 *
 * @author Jon Brisbin
 * @author Stephane Maldini
 * @author Andy Wilkinson
 */
public class NettyNetChannelInboundHandler<IN> extends ChannelInboundHandlerAdapter {

    private final Logger log = LoggerFactory.getLogger(getClass());

    private final Subscriber<? super IN> subscriber;
    protected final NettyChannelStream<IN, ?> channelStream;

    private volatile ByteBuf remainder;
    private volatile PushSubscription<IN> channelSubscription;

    public NettyNetChannelInboundHandler(Subscriber<? super IN> subscriber,
            NettyChannelStream<IN, ?> channelStream) {
        this.subscriber = subscriber;
        this.channelStream = channelStream;
    }

    public PushSubscription<IN> subscription() {
        return channelSubscription;
    }

    public NettyChannelStream<IN, ?> channelStream() {
        return channelStream;
    }

    @Override
    public void channelActive(final ChannelHandlerContext ctx) throws Exception {
        try {
            if (this.channelSubscription != null) {
                super.channelActive(ctx);
                if (log.isDebugEnabled()) {
                    log.debug("RESUME: " + ctx.channel());
                }
                return;
            }

            this.channelSubscription = new PushSubscription<IN>(null, subscriber) {
                @Override
                protected void onRequest(long n) {
                    if (n == Long.MAX_VALUE) {
                        ctx.channel().config().setAutoRead(true);
                    }
                    ctx.read();
                }

                @Override
                public void cancel() {
                    super.cancel();
                    if (ctx.channel().isOpen()) {
                        ctx.close();
                    }
                }
            };
            channelStream.registerOnPeer();
            subscriber.onSubscribe(channelSubscription);
            super.channelActive(ctx);
        } catch (Throwable err) {
            subscriber.onError(err);
        }
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        if (channelSubscription.isComplete()) {
            return;
        }

        try {
            super.channelReadComplete(ctx);
            if (channelSubscription.pendingRequestSignals() != Long.MAX_VALUE
                    && channelSubscription.pendingRequestSignals() > 1l) {
                ctx.read();
            }
        } catch (Throwable throwable) {
            channelSubscription.onError(throwable);
        }
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        try {
            channelSubscription.onComplete();
            super.channelInactive(ctx);
        } catch (Throwable err) {
            channelSubscription.onError(err);
        }
    }

    @Override
    @SuppressWarnings("unchecked")
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        try {

            if (channelSubscription.isComplete() || msg.getClass() == EmptyByteBuf.class) {
                return;
            }

            if (channelStream.getDecoder() == Spec.NOOP_DECODER
                    || !ByteBuf.class.isAssignableFrom(msg.getClass())) {
                channelSubscription.onNext((IN) msg);
                return;
            } else if (channelStream.getDecoder() == null) {
                channelSubscription.onNext((IN) new Buffer(((ByteBuf) msg).nioBuffer()));
                return;
            }

            ByteBuf data = (ByteBuf) msg;
            if (remainder == null) {
                try {
                    passToConnection(data);
                } finally {
                    if (data.isReadable()) {
                        remainder = data;
                    } else {
                        data.release();
                    }
                }
                return;
            }

            if (!bufferHasSufficientCapacity(remainder, data)) {
                ByteBuf combined = createCombinedBuffer(remainder, data, ctx);
                remainder.release();
                remainder = combined;
            } else {
                remainder.writeBytes(data);
            }
            data.release();

            try {
                passToConnection(remainder);
            } finally {
                if (remainder.isReadable()) {
                    remainder.discardSomeReadBytes();
                } else {
                    remainder.release();
                    remainder = null;
                }
            }
        } catch (Throwable t) {
            channelSubscription.onError(t);
        }

    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        if ("Broken pipe".equals(cause.getMessage()) || "Connection reset by peer".equals(cause.getMessage())) {
            if (log.isDebugEnabled()) {
                log.debug(ctx.channel().toString() + " " + cause.getMessage());
            }
        }
        channelSubscription.onError(cause);
    }

    private boolean bufferHasSufficientCapacity(ByteBuf receiver, ByteBuf provider) {
        return receiver.writerIndex() <= receiver.maxCapacity() - provider.readableBytes();
    }

    private ByteBuf createCombinedBuffer(ByteBuf partOne, ByteBuf partTwo, ChannelHandlerContext ctx) {
        ByteBuf combined = ctx.alloc().buffer(partOne.readableBytes() + partTwo.readableBytes());
        combined.writeBytes(partOne);
        combined.writeBytes(partTwo);
        return combined;
    }

    private void passToConnection(ByteBuf data) {
        Buffer b = new Buffer(data.nioBuffer());
        int start = b.position();
        if (null != channelStream.getDecoder() && null != b.byteBuffer()) {
            IN read = channelStream.getDecoder().apply(b);
            if (read != null) {
                channelSubscription.onNext(read);
            }
        }

        //data.remaining() > 0;
        data.skipBytes(b.position() - start);
    }

}