org.diorite.impl.connection.ByteToMessageCodec.java Source code

Java tutorial

Introduction

Here is the source code for org.diorite.impl.connection.ByteToMessageCodec.java

Source

/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2016. Diorite (by Bartomiej Mazur (aka GotoFinal))
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package org.diorite.impl.connection;

import java.util.List;

import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;

import org.diorite.impl.connection.packets.Packet;
import org.diorite.impl.connection.packets.PacketClass;
import org.diorite.utils.math.DioriteMathUtils;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.handler.codec.MessageToByteEncoder;
import io.netty.util.internal.TypeParameterMatcher;

/**
 * Diorite edited copy of {@link io.netty.handler.codec.ByteToMessageCodec}
 * <br>
 * A Codec for on-the-fly encoding/decoding of bytes to messages and vise-versa.
 * <br>
 * This can be thought of as a combination of {@link ByteToMessageDecoder} and {@link MessageToByteEncoder}.
 * <br>
 * Be aware that sub-classes of {@link ByteToMessageCodec} <strong>MUST NOT</strong>
 * annotated with {@link io.netty.channel.ChannelHandler.Sharable}.
 */
@SuppressWarnings("ThrowFromFinallyBlock")
public abstract class ByteToMessageCodec<I> extends ChannelDuplexHandler {
    private final TypeParameterMatcher outboundMsgMatcher;
    private final MessageToByteEncoder<I> encoder;
    private final ByteToMessageDecoder decoder = new ByteToMessageDecoder() {
        @Override
        public void decode(final ChannelHandlerContext ctx, final ByteBuf in, final List<Object> out)
                throws Exception {
            ByteToMessageCodec.this.decode(ctx, in, out);
        }

        @Override
        protected void decodeLast(final ChannelHandlerContext ctx, final ByteBuf in, final List<Object> out)
                throws Exception {
            ByteToMessageCodec.this.decodeLast(ctx, in, out);
        }
    };

    public abstract static class PacketByteToMessageCodec extends ByteToMessageCodec<Packet<?>> {
        @Override
        protected ByteBuf allocateBuffer(final ChannelHandlerContext ctx, final Packet<?> msg,
                final boolean preferDirect) throws Exception {
            final PacketClass pc = msg.getPacketData();
            final int size = DioriteMathUtils.varintSize(pc.id()) + pc.size();
            if (size < 0) {
                throw new IllegalArgumentException("Size can't be lower than 0!");
            }
            if (preferDirect) {
                return ctx.alloc().ioBuffer(size);
            } else {
                return ctx.alloc().heapBuffer(size);
            }
        }
    }

    public abstract static class PacketByteBufByteToMessageCodec extends ByteToMessageCodec<ByteBuf> {
        @Override
        protected ByteBuf allocateBuffer(final ChannelHandlerContext ctx, final ByteBuf msg,
                final boolean preferDirect) throws Exception {
            final int dataSize = msg.readableBytes();
            final int size = DioriteMathUtils.varintSize(dataSize) + dataSize;
            if (size < 0) {
                throw new IllegalArgumentException("Size can't be lower than 0!");
            }
            if (preferDirect) {
                return ctx.alloc().ioBuffer(size);
            } else {
                return ctx.alloc().heapBuffer(size);
            }
        }
    }

    /**
     * @see #ByteToMessageCodec(boolean) with {@code true} as boolean parameter.
     */
    protected ByteToMessageCodec() {
        this(true);
    }

    /**
     * @param outboundMessageType type of message.
     *
     * @see #ByteToMessageCodec(Class, boolean) with {@code true} as boolean value.
     */
    protected ByteToMessageCodec(final Class<? extends I> outboundMessageType) {
        this(outboundMessageType, true);
    }

    /**
     * Create a new instance which will try to detect the types to match out of the type parameter of the class.
     *
     * @param preferDirect {@code true} if a direct {@link ByteBuf} should be tried to be used as target for
     *                     the encoded messages. If {@code false} is used it will allocate a heap
     *                     {@link ByteBuf}, which is backed by an byte array.
     */
    protected ByteToMessageCodec(final boolean preferDirect) {
        this.outboundMsgMatcher = TypeParameterMatcher.find(this, ByteToMessageCodec.class, "I");
        this.encoder = new Encoder(preferDirect);
    }

    /**
     * Create a new instance
     *
     * @param outboundMessageType The type of messages to match
     * @param preferDirect        {@code true} if a direct {@link ByteBuf} should be tried to be used as target for
     *                            the encoded messages. If {@code false} is used it will allocate a heap
     *                            {@link ByteBuf}, which is backed by an byte array.
     */
    protected ByteToMessageCodec(final Class<? extends I> outboundMessageType, final boolean preferDirect) {
        this.outboundMsgMatcher = TypeParameterMatcher.get(outboundMessageType);
        this.encoder = new Encoder(preferDirect);
    }

    /**
     * Returns {@code true} if and only if the specified message can be encoded by this codec.
     *
     * @param msg the message
     *
     * @return {@code true} if and only if the specified message can be encoded by this codec.
     *
     * @throws Exception if any element of codec throw exception.
     */
    public boolean acceptOutboundMessage(final Object msg) throws Exception {
        return this.outboundMsgMatcher.match(msg);
    }

    @Override
    public void channelRead(final ChannelHandlerContext ctx, final Object msg) throws Exception {
        this.decoder.channelRead(ctx, msg);
    }

    @Override
    public void write(final ChannelHandlerContext ctx, final Object msg, final ChannelPromise promise)
            throws Exception {
        this.encoder.write(ctx, msg, promise);
    }

    @Override
    public void channelReadComplete(final ChannelHandlerContext ctx) throws Exception {
        this.decoder.channelReadComplete(ctx);
    }

    @Override
    public void channelInactive(final ChannelHandlerContext ctx) throws Exception {
        this.decoder.channelInactive(ctx);
    }

    @Override
    public void handlerAdded(final ChannelHandlerContext ctx) throws Exception {
        try {
            this.decoder.handlerAdded(ctx);
        } finally {
            this.encoder.handlerAdded(ctx);
        }
    }

    @Override
    public void handlerRemoved(final ChannelHandlerContext ctx) throws Exception {
        try {
            this.decoder.handlerRemoved(ctx);
        } finally {
            this.encoder.handlerRemoved(ctx);
        }
    }

    /**
     * Encode a message into a {@link ByteBuf}. This method will be called for each written message that can be handled
     * by this encoder.
     *
     * @param ctx the {@link ChannelHandlerContext} which this {@link MessageToByteEncoder} belongs to
     * @param msg the message to encode
     * @param out the {@link ByteBuf} into which the encoded message will be written
     *
     * @throws Exception is thrown if an error accour
     */
    protected abstract void encode(ChannelHandlerContext ctx, I msg, ByteBuf out) throws Exception;

    /**
     * Decode the from one {@link ByteBuf} to an other. This method will be called till either the input
     * {@link ByteBuf} has nothing to read when return from this method or till nothing was read from the input
     * {@link ByteBuf}.
     *
     * @param ctx the {@link ChannelHandlerContext} which this {@link ByteToMessageDecoder} belongs to
     * @param in  the {@link ByteBuf} from which to read data
     * @param out the {@link List} to which decoded messages should be added
     *
     * @throws Exception is thrown if an error accour
     */
    protected abstract void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception;

    /**
     * @see ByteToMessageDecoder#decodeLast(ChannelHandlerContext, ByteBuf, List)
     */
    protected void decodeLast(final ChannelHandlerContext ctx, final ByteBuf in, final List<Object> out)
            throws Exception {
        this.decode(ctx, in, out);
    }

    /**
     * Allocate a {@link ByteBuf} which will be used as argument of {@link #encode(ChannelHandlerContext, Object, ByteBuf)}.
     * Sub-classes may override this method to returna {@link ByteBuf} with a perfect matching {@code initialCapacity}.
     *
     * @param ctx          the {@link ChannelHandlerContext} which this {@link ByteToMessageDecoder} belongs to
     * @param msg          the message to encode
     * @param preferDirect if it should prefer direct buffer.
     *
     * @return created ByteBuf.
     *
     * @throws Exception if allocating buffer fail.
     */
    protected ByteBuf allocateBuffer(final ChannelHandlerContext ctx, final I msg, final boolean preferDirect)
            throws Exception {
        if (preferDirect) {
            return ctx.alloc().ioBuffer();
        } else {
            return ctx.alloc().heapBuffer();
        }
    }

    private final class Encoder extends MessageToByteEncoder<I> {
        Encoder(final boolean preferDirect) {
            super(preferDirect);
        }

        @Override
        public boolean acceptOutboundMessage(final Object msg) throws Exception {
            return ByteToMessageCodec.this.acceptOutboundMessage(msg);
        }

        @Override
        protected void encode(final ChannelHandlerContext ctx, final I msg, final ByteBuf out) throws Exception {
            ByteToMessageCodec.this.encode(ctx, msg, out);
        }

        @Override
        protected ByteBuf allocateBuffer(final ChannelHandlerContext ctx, final I msg, final boolean preferDirect)
                throws Exception {
            return ByteToMessageCodec.this.allocateBuffer(ctx, msg, preferDirect);
        }
    }

    @Override
    public String toString() {
        return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
                .append("outboundMsgMatcher", this.outboundMsgMatcher).append("encoder", this.encoder)
                .append("decoder", this.decoder).toString();
    }
}