package io.jafka.network; import io.jafka.common.ErrorMapping; import io.jafka.common.annotations.NotThreadSafe; import io.jafka.utils.Closer; import io.jafka.utils.KV; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.channels.*; import java.util.concurrent.locks.ReentrantLock; /** * A simple blocking channel with timeouts correctly enabled. * * @author adyliu (imxylz@gmail.com) * @since 1.3 */ @NotThreadSafe public class BlockingChannel { public static final int DEFAULT_BUFFER_SIZE = -1; private final String host; private final int port; private final int readBufferSize; private final int writeBufferSize; private final int readTimeoutMs; private boolean connected = false; private SocketChannel channel; private ReadableByteChannel readChannel; private GatheringByteChannel writeChannel; private final ReentrantLock lock = new ReentrantLock(); public BlockingChannel(String host, int port, int readBufferSize, int writeBufferSize, int readTimeoutMs) { this.host = host; this.port = port; this.readBufferSize = readBufferSize; this.writeBufferSize = writeBufferSize; this.readTimeoutMs = readTimeoutMs; } public void connect() throws IOException { lock.lock(); try { if (!connected) { channel = SocketChannel.open(); if (readBufferSize > 0) { channel.socket().setReceiveBufferSize(readBufferSize); } if (writeBufferSize > 0) { channel.socket().setSendBufferSize(writeBufferSize); } channel.configureBlocking(true); channel.socket().setSoTimeout(readTimeoutMs); channel.socket().setKeepAlive(true); channel.socket().setTcpNoDelay(true); channel.connect(new InetSocketAddress(host, port)); writeChannel = channel; readChannel = Channels.newChannel(channel.socket().getInputStream()); connected = true; } } finally { lock.unlock(); } } public void disconnect() { lock.lock(); try { if (connected || channel != null) { Closer.closeQuietly(channel); Closer.closeQuietly(channel.socket()); Closer.closeQuietly(readChannel); channel = null; readChannel = null; writeChannel = null; connected = false; } } finally { lock.unlock(); } } public boolean isConnected() { return connected; } public int send(Request request) throws IOException { if (!isConnected()) { throw new ClosedChannelException(); } return new BoundedByteBufferSend(request).writeCompletely(writeChannel); } public KV<Receive, ErrorMapping> receive() throws IOException{ BoundedByteBufferReceive response = new BoundedByteBufferReceive(); response.readCompletely(readChannel); return new KV<Receive, ErrorMapping>(response, ErrorMapping.valueOf(response.buffer().getShort())); } }