Java tutorial
/* * 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); } }