package edu.uw.zookeeper.net.intravm; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; import io.netty.buffer.CompositeByteBuf; import io.netty.buffer.Unpooled; import java.io.IOException; import java.net.SocketAddress; import java.nio.channels.ClosedChannelException; import java.util.concurrent.Executor; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import com.google.common.base.Optional; import com.google.common.collect.Queues; import edu.uw.zookeeper.net.Codec; import edu.uw.zookeeper.net.LoggingMarker; public final class IntraVmCodecEndpoint<I,O,T extends Codec<I,? extends O,? extends I,?>> extends AbstractIntraVmEndpoint<I,O,ByteBuf,ByteBuf> { public static <I,O,T extends Codec<I,? extends O,? extends I,?>> IntraVmCodecEndpoint<I,O,T> newInstance( ByteBufAllocator allocator, T codec, SocketAddress address, Executor executor) { Logger logger = LogManager.getLogger(IntraVmCodecEndpoint.class); return new IntraVmCodecEndpoint<I,O,T>( allocator, codec, address, executor, logger, IntraVmPublisher.<O>defaults(executor, logger)); } protected final ByteBufAllocator allocator; protected final T codec; private final DecodingEndpointReader reader; private final DecodingEndpointWriter writer; protected IntraVmCodecEndpoint( ByteBufAllocator allocator, T codec, SocketAddress address, Executor executor, Logger logger, IntraVmPublisher<O> publisher) { super(address, executor, logger, publisher); this.allocator = allocator; this.codec = codec; this.reader = new DecodingEndpointReader(); this.writer = new DecodingEndpointWriter(); } public T codec() { return codec; } @Override public DecodingEndpointReader reader() { return reader; } @Override public DecodingEndpointWriter writer() { return writer; } public final class DecodingEndpointReader extends AbstractEndpointReader { protected DecodingEndpointReader() { super(Queues.<ByteBuf>newLinkedBlockingDeque()); } @Override protected boolean apply(ByteBuf input) throws Exception { Optional<? extends O> output; try { output = codec.decode(input); } catch (IOException e) { logger.warn(LoggingMarker.NET_MARKER.get(), "{} ({})", input, this, e); throw e; } if (output.isPresent()) { O message = output.get(); logger.trace(LoggingMarker.NET_MARKER.get(), "DECODED {} ({})", message, this); return publisher.send(message); } else { ByteBuf next = mailbox().poll(); if (next != null) { CompositeByteBuf composite; if (input instanceof CompositeByteBuf) { composite = (CompositeByteBuf) input; } else { composite = Unpooled.compositeBuffer(); composite.addComponent(input); } composite.addComponent(next); input = composite; } mailbox().addFirst(input); if (state() == State.TERMINATED) { mailbox().poll(); return false; } return (next != null); } } } public final class DecodingEndpointWriter extends AbstractEndpointWriter { protected DecodingEndpointWriter() { super(Queues.<EndpointWrite<? extends I,ByteBuf>>newConcurrentLinkedQueue()); } @Override protected boolean apply(EndpointWrite<? extends I,ByteBuf> input) throws Exception { ByteBuf output = allocator.buffer(); logger.trace(LoggingMarker.NET_MARKER.get(), "ENCODING {} ({})", input, this); try { codec.encode(input.message(), output); } catch (IOException e) { logger.warn(LoggingMarker.NET_MARKER.get(), "{} ({})", input, this, e); throw e; } if (input.remote().reader().send(output)) { input.run(); return true; } else { Exception e = new ClosedChannelException(); input.setException(e); throw e; } } } }