package io.ripc.reactor.protocol.tcp; import com.fasterxml.jackson.databind.ObjectMapper; import io.netty.buffer.ByteBuf; import io.netty.channel.Channel; import io.netty.channel.ChannelConfig; import io.netty.channel.ChannelDuplexHandler; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; import io.netty.channel.ChannelPromise; import io.netty.channel.FixedRecvByteBufAllocator; import io.netty.handler.codec.LineBasedFrameDecoder; import io.netty.handler.codec.json.JsonObjectDecoder; import io.netty.handler.codec.string.StringDecoder; import io.netty.handler.codec.string.StringEncoder; import io.netty.util.CharsetUtil; import io.ripc.protocol.tcp.TcpServer; import io.ripc.transport.netty4.tcp.Netty4TcpServer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import reactor.rx.Streams; import java.nio.charset.Charset; public class CodecSample { private static final Logger LOG = LoggerFactory.getLogger(CodecSample.class); public static void main(String... args) throws InterruptedException { //runLineBasedFrameDecoder(); echoJsonStreamDecoding(); } private static void runLineBasedFrameDecoder() { TcpServer<String, String> transport = Netty4TcpServer.<String, String>create( 0, new ChannelInitializer<Channel>() { @Override protected void initChannel(Channel channel) throws Exception { int bufferSize = 1; ChannelConfig config = channel.config(); config.setOption(ChannelOption.SO_RCVBUF, bufferSize); config.setOption(ChannelOption.RCVBUF_ALLOCATOR, new FixedRecvByteBufAllocator(bufferSize)); channel.pipeline().addFirst( new LineBasedFrameDecoder(256), new StringDecoder(CharsetUtil.UTF_8), new StringEncoder(CharsetUtil.UTF_8)); } }); ReactorTcpServer.create(transport).start(connection -> { connection.log("input") .observeComplete(v -> LOG.info("Connection input complete")) .capacity(1) .consume(line -> { String response = "Hello " + line + "\n"; Streams.wrap(connection.writeWith(Streams.just(response))).consume(); }); return Streams.never(); }); } private static void echoJsonStreamDecoding() { TcpServer<Person, Person> transport = Netty4TcpServer.<Person, Person>create( 0, new ChannelInitializer<Channel>() { @Override protected void initChannel(Channel channel) throws Exception { channel.pipeline().addFirst( new JsonObjectDecoder(), new JacksonJsonCodec()); } }); ReactorTcpServer.create(transport) .start(connection -> { connection.log("input") .observeComplete(v -> LOG.info("Connection input complete")) .capacity(1) .consume(person -> { person = new Person(person.getLastName(), person.getFirstName()); Streams.wrap(connection.writeWith(Streams.just(person))).consume(); }); return Streams.never(); }); } private static class JacksonJsonCodec extends ChannelDuplexHandler { private final ObjectMapper mapper = new ObjectMapper(); @Override public void channelRead(ChannelHandlerContext context, Object message) throws Exception { if (message instanceof ByteBuf) { Charset charset = Charset.defaultCharset(); message = this.mapper.readValue(((ByteBuf) message).toString(charset), Person.class); } super.channelRead(context, message); } @Override public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { if (msg instanceof Person) { byte[] buff = mapper.writeValueAsBytes(msg); ByteBuf bb = ctx.alloc().buffer(buff.length); bb.writeBytes(buff); msg = bb; } super.write(ctx, msg, promise); } } private static class Person { private String firstName; private String lastName; public Person() { } public Person(String firstName, String lastName) { this.firstName = firstName; this.lastName = lastName; } public String getFirstName() { return firstName; } public Person setFirstName(String firstName) { this.firstName = firstName; return this; } public String getLastName() { return lastName; } public Person setLastName(String lastName) { this.lastName = lastName; return this; } } }