package io.ripc.transport.netty4.tcp;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.ripc.protocol.tcp.TcpHandler;
import io.ripc.protocol.tcp.TcpServer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
public class Netty4TcpServer<R, W> extends TcpServer<R, W> {
private static final Logger logger = LoggerFactory.getLogger(Netty4TcpServer.class);
private int port;
private final ChannelInitializer<Channel> initializer;
private ServerBootstrap bootstrap;
private ChannelFuture bindFuture;
protected Netty4TcpServer(int port) {
this(port, null);
}
protected Netty4TcpServer(int port, ChannelInitializer<Channel> initializer) {
this.port = port;
this.initializer = initializer;
bootstrap = new ServerBootstrap()
.group(new NioEventLoopGroup())
.channel(NioServerSocketChannel.class);
}
@Override
protected Netty4TcpServer<R, W> doStart(final TcpHandler<R, W> handler) {
bootstrap.childHandler(new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel ch) throws Exception {
if (initializer != null) {
ch.pipeline().addLast(initializer);
}
ch.config().setAutoRead(false);
ch.pipeline().addLast("server_handler", new ChannelToConnectionBridge<>(handler));
}
});
try {
bindFuture = bootstrap.bind(port).sync();
if (!bindFuture.isSuccess()) {
throw new RuntimeException(bindFuture.cause());
}
SocketAddress localAddress = bindFuture.channel().localAddress();
if (localAddress instanceof InetSocketAddress) {
port = ((InetSocketAddress) localAddress).getPort();
logger.info("Started server at port: " + port);
}
} catch (InterruptedException e) {
logger.error("Error waiting for binding server port: " + port, e);
}
return this;
}
@Override
public void awaitShutdown() {
try {
bindFuture.channel().closeFuture().await();
} catch (InterruptedException e) {
Thread.interrupted(); // Reset the interrupted status
logger.error("Interrupted while waiting for the server socket to close.", e);
}
}
@Override
public boolean doShutdown() {
try {
bindFuture.channel().close().sync();
return true;
} catch (InterruptedException e) {
logger.error("Failed to shutdown the server.", e);
return false;
}
}
@Override
public int getPort() {
return port;
}
public static <R, W> TcpServer<R, W> create(int port) {
return new Netty4TcpServer<>(port);
}
public static <R, W> TcpServer<R, W> create(int port, ChannelInitializer<Channel> initializer) {
return new Netty4TcpServer<R, W>(port, initializer);
}
}