io.github.vastframework.codecs.handlers.Handlers.java Source code

Java tutorial

Introduction

Here is the source code for io.github.vastframework.codecs.handlers.Handlers.java

Source

/*
 * Copyright 2016 The vast-codecs 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 io.github.vastframework.codecs.handlers;

import org.jetbrains.annotations.NotNull;

import java.lang.reflect.Type;
import java.util.List;

import io.github.vastframework.codecs.Codec;
import io.github.vastframework.codecs.CodecInfo;
import io.github.vastframework.codecs.CodecProvider;
import io.github.vastframework.codecs.DecodingContext;
import io.github.vastframework.codecs.Direction;
import io.github.vastframework.codecs.EncodingContext;
import io.github.vastframework.codecs.exception.NoSuchCodecException;
import io.github.vastframework.codecs.primitives.DecodingPrimitiveSupport;
import io.github.vastframework.codecs.primitives.EncodingPrimitiveSupport;
import io.github.vastframework.codecs.primitives.PrimitiveSupport;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPipeline;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.handler.codec.MessageToByteEncoder;

import static io.github.vastframework.codecs.internal.$VastCodecs$Validate.checkNotNull;

/**
 * Provides some handy {@code ChannelHandlers}
 */
public class Handlers {

    // Constants

    /**
     * The default DecodingContextProvider used in this class.
     *
     * <p>Implementation details of the DecodingContext provided:</p>
     *
     * <p>{@link DecodingContext#decode(Type) decode(Type)}: Gets the Codec from the CodecProvider
     * passed to the DecodingContextProvider. If it is null, it will throw a NoSuchCodecException.
     * Then it calls its {@link Codec#decode(ByteBuf, DecodingContext)
     * decode(ByteBuf, DecodingContext)} method with the ByteBuf passed to the
     * DecodingContextProvider</p>
     *
     * <p>{@link DecodingContext#decodeById() decodeById()}: Does the exact same as
     * <code>decode(Type)</code>. The only difference is how the Codec is gotten. This implementation
     * will invoke {@link DecodingPrimitiveSupport#decodeInt(ByteBuf)} on the DecodingPrimitiveSupport
     * instance gotten by calling {@link DecodingContext#getPrimitiveSupport() getPrimitiveSupport()}.
     * The integer returned is used as the Codec-ID. The Codec is then gotten by calling
     * {@link CodecProvider#getCodec(int, Direction)}.</p>
     *
     * <p>{@link DecodingContext#getPrimitiveSupport() getPrimitiveSupport()}: Invokes
     * {@link CodecProvider#getPrimitiveSupport()} on the CodecProvider passed to the
     * DecodingContextProvider.</p>
     *
     * <p>Any call to CodecProvider, which needs a {@link Direction} to be defined, will use
     * {@link Direction#INCOMING INCOMING}.</p>
     */
    public static final DecodingContextProvider DEFAULT_DECODING_CONTEXT_PROVIDER = new SimpleDecodingContext.SimpleDecodingContextProvider();

    /**
     * The default EncodingContextProvider used in this class.
     *
     * <p>Implementation details of the EncodingContext provided:</p>
     *
     * <p>{@link EncodingContext#encode(Type, Object) encode(Type, Object)}: Gets the Codec from the
     * CodecProvider passed to the EncodingContextProvider. If it is null, it will throw a
     * NoSuchCodecException. Then it calls its {@link Codec#encode(Object, ByteBuf, EncodingContext)
     * encode(Object, ByteBuf, EncodingContext)} method with the ByteBuf passed to the
     * EncodingContextProvider</p>
     *
     * <p>{@link EncodingContext#encodeWithId(Type, Object) encodeWithId(Type, Object)}: Does the
     * exact same as <code>encode(Type, Object)</code>. The only difference is that the Codec-ID
     * ({@link CodecInfo#getId()}) is written before invoking the encode method of the Codec by
     * calling {@link EncodingPrimitiveSupport#encodeInt(ByteBuf, int)} method on the
     * EncodingPrimitiveSupport instance gotten by invoking
     * {@link EncodingContext#getPrimitiveSupport() getPrimitiveSupport()}.</p>
     *
     * <p>{@link EncodingContext#getPrimitiveSupport() getPrimitiveSupport()}: Invokes
     * {@link CodecProvider#getPrimitiveSupport()} on the CodecProvider passed to the
     * EncodingContextProvider.</p>
     *
     * <p>Any call to CodecProvider, which needs a {@link Direction} to be defined, will use
     * {@link Direction#OUTGOING OUTGOING}.</p>
     */
    public static final EncodingContextProvider DEFAULT_ENCODING_CONTEXT_PROVIDER = new SimpleEncodingContext.SimpleEncodingContextProvider();

    // API

    /**
     * Returns a ChannelHandler, which decodes Objects by using an DecodingContext.
     *
     * <p>This method is a convenience call to
     * {@link #createCodecDecoder(CodecProvider, DecodingContextProvider)}
     * with {@link #DEFAULT_DECODING_CONTEXT_PROVIDER}.</p>
     * @see #createCodecDecoder(CodecProvider, DecodingContextProvider)
     */
    public static ChannelHandler createCodecDecoder(@NotNull CodecProvider codecProvider) {
        return Handlers.createCodecDecoder(codecProvider, Handlers.DEFAULT_DECODING_CONTEXT_PROVIDER);
    }

    /**
     * Returns a ChannelHandler, which encodes Objects by using an EncodingContext.
     *
     * <p>This method is a convenience call to
     * {@link #createCodecEncoder(CodecProvider, EncodingContextProvider)}
     * with {@link #DEFAULT_ENCODING_CONTEXT_PROVIDER}.</p>
     * @see #createCodecEncoder(CodecProvider, EncodingContextProvider)
     */
    public static ChannelHandler createCodecEncoder(@NotNull CodecProvider codecProvider) {
        return Handlers.createCodecEncoder(codecProvider, Handlers.DEFAULT_ENCODING_CONTEXT_PROVIDER);
    }

    /**
     * Returns a ChannelHandler, which encodes Objects by using an EncodingContext.
     *
     * <p>The returned ChannelHandler extends {@code MessageToByteEncoder}. When its {@code encode}
     * method is invoked, it will invoke the
     * {@link EncodingContextProvider#provide(CodecProvider, ChannelHandlerContext, ByteBuf) provide}
     * method of the given EncodingContextProvider. After that it will call the
     * {@link EncodingContext#encodeWithId(Type, Object) encodeWithId} method of the returned
     * {@code EncodingContext}.</p>
     */
    public static ChannelHandler createCodecEncoder(@NotNull CodecProvider codecProvider,
            @NotNull EncodingContextProvider contextProvider) {
        return new CodecBasedEncoder(codecProvider, contextProvider);
    }

    /**
     * Returns a ChannelHandler, which decodes Objects by using an DecodingContext.
     *
     * <p>The returned ChannelHandler extends {@code ByteToMessageDecoder}. When its {@code decode}
     * method is invoked, it will invoke the
     * {@link DecodingContextProvider#provide(CodecProvider, ChannelHandlerContext, ByteBuf) provide}
     * method of the given EncodingContextProvider. After that it will call the
     * {@link DecodingContext#decodeById() decodeById} method of the returned
     * {@code DecodingContext} and add the returned Object to the output.</p>
     */
    public static ChannelHandler createCodecDecoder(@NotNull CodecProvider codecProvider,
            @NotNull DecodingContextProvider contextProvider) {
        return new CodecBasedDecoder(codecProvider, contextProvider);
    }

    /**
     * Sets the CodecProvider of all ChannelHandlers created by this class in the given
     * ChannelPipeline.
     *
     * @param pipeline the pipeline, in which the ChannelHandlers, whose CodecProvider has to be set,
     *     are contained
     * @param codecProvider the CodecProvider to set
     */
    public static void setCodecProvider(@NotNull ChannelPipeline pipeline, @NotNull CodecProvider codecProvider) {
        checkNotNull(codecProvider, "codecProvider cannot be null");
        checkNotNull(pipeline, "pipeline cannot be null");

        CodecBasedEncoder encoder = pipeline.get(CodecBasedEncoder.class);
        if (encoder != null) {
            encoder.setCodecProvider(codecProvider);
        }
        CodecBasedDecoder decoder = pipeline.get(CodecBasedDecoder.class);
        if (decoder != null) {
            decoder.setCodecProvider(codecProvider);
        }
    }

    // ChannelHandlers

    private static class CodecBasedDecoder extends ByteToMessageDecoder {

        private CodecProvider codecProvider;
        private DecodingContextProvider contextProvider;

        public CodecBasedDecoder(CodecProvider codecProvider, DecodingContextProvider contextProvider) {
            this.codecProvider = checkNotNull(codecProvider, "codecProvider cannot be null");
            this.contextProvider = checkNotNull(contextProvider, "contextProvider cannot be null");
        }

        @Override
        protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
            out.add(this.contextProvider.provide(this.codecProvider, ctx, in).decodeById());
        }

        public void setCodecProvider(CodecProvider codecProvider) {
            this.codecProvider = codecProvider;
        }

    }

    private static class CodecBasedEncoder extends MessageToByteEncoder<Object> {

        private CodecProvider codecProvider;
        private EncodingContextProvider contextProvider;

        public CodecBasedEncoder(CodecProvider codecProvider, EncodingContextProvider contextProvider) {
            this.codecProvider = checkNotNull(codecProvider, "codecProvider cannot be null");
            this.contextProvider = checkNotNull(contextProvider, "contextProvider cannot be null");
        }

        @Override
        protected void encode(ChannelHandlerContext ctx, Object msg, ByteBuf out) throws Exception {
            this.contextProvider.provide(this.codecProvider, ctx, out).encodeWithId(msg.getClass(), msg);
        }

        public void setCodecProvider(CodecProvider codecProvider) {
            this.codecProvider = codecProvider;
        }

    }

    // Contexts

    private static class SimpleEncodingContext implements EncodingContext {

        private final CodecProvider codecProvider;
        private final ByteBuf buffer;

        public SimpleEncodingContext(CodecProvider codecProvider, ByteBuf buffer) {
            this.codecProvider = checkNotNull(codecProvider, "codecProvider cannot be null");
            this.buffer = checkNotNull(buffer, "buffer cannot be null");
        }

        public CodecProvider getCodecProvider() {
            return this.codecProvider;
        }

        @Override
        public void encode(@NotNull Type type, @NotNull Object value) throws Exception {
            this.getCodecInfoForType(type).getCodec().encode(value, this.buffer, this);
        }

        @Override
        public void encodeWithId(@NotNull Type type, @NotNull Object value) throws Exception {
            CodecInfo<Object> codecInfo = this.getCodecInfoForType(type);
            this.getPrimitiveSupport().encodeInt(this.buffer, codecInfo.getId());
            codecInfo.getCodec().encode(value, this.buffer, this);
        }

        private <T> CodecInfo<T> getCodecInfoForType(Type type) throws Exception {
            CodecInfo<T> codecInfo = this.codecProvider.getCodecInfo(type, Direction.OUTGOING);
            if (codecInfo == null) {
                throw new NoSuchCodecException("no codec for type " + type + " found");
            }
            return codecInfo;
        }

        @Override
        public PrimitiveSupport getPrimitiveSupport() {
            return this.getCodecProvider().getPrimitiveSupport();
        }

        private static class SimpleEncodingContextProvider implements EncodingContextProvider {

            @Override
            public EncodingContext provide(CodecProvider codecProvider, ChannelHandlerContext ctx, ByteBuf buffer) {
                return new SimpleEncodingContext(codecProvider, buffer);
            }
        }

    }

    private static class SimpleDecodingContext implements DecodingContext {

        private final CodecProvider codecProvider;
        private final ByteBuf buffer;

        public SimpleDecodingContext(CodecProvider codecProvider, ByteBuf buffer) {
            this.codecProvider = checkNotNull(codecProvider, "codecProvider cannot be null");
            this.buffer = checkNotNull(buffer, "buffer cannot be null");
        }

        public CodecProvider getCodecProvider() {
            return this.codecProvider;
        }

        @Override
        public <T> T decode(@NotNull Type type) throws Exception {
            CodecInfo<T> codecInfo = this.codecProvider.getCodecInfo(type, Direction.INCOMING);
            if (codecInfo == null) {
                throw new NoSuchCodecException("no codec for type " + type + " found");
            }
            return codecInfo.getCodec().decode(this.buffer, this);
        }

        @Override
        public <T> T decodeById() throws Exception {
            int id = this.getPrimitiveSupport().decodeInt(this.buffer);
            Codec<T> codec = this.codecProvider.getCodec(id, Direction.OUTGOING);
            if (codec == null) {
                throw new NoSuchCodecException("no codec for id " + id + " found");
            }
            return codec.decode(this.buffer, this);
        }

        @Override
        public PrimitiveSupport getPrimitiveSupport() {
            return this.getCodecProvider().getPrimitiveSupport();
        }

        private static class SimpleDecodingContextProvider implements DecodingContextProvider {

            @Override
            public DecodingContext provide(CodecProvider codecProvider, ChannelHandlerContext ctx, ByteBuf buffer) {
                return new SimpleDecodingContext(codecProvider, buffer);
            }

        }

    }

    // Interfaces

    /**
     * Provides EncodingContexts for ChannelHandlers created by the {@link Handlers} class.
     */
    public interface EncodingContextProvider {

        EncodingContext provide(CodecProvider codecProvider, ChannelHandlerContext ctx, ByteBuf buffer);

    }

    /**
     * Provides DecodingContexts for ChannelHandlers created by the {@link Handlers} class.
     */
    public interface DecodingContextProvider {

        DecodingContext provide(CodecProvider codecProvider, ChannelHandlerContext ctx, ByteBuf buffer);

    }

}