package org.ripple.power.server;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.AttributeKey;
import io.netty.util.concurrent.GlobalEventExecutor;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import javax.swing.JTextField;
import org.ripple.power.server.chat.AMessage;
import org.ripple.power.server.chat.ByteMesDecoder;
import org.ripple.power.server.chat.ChatMessage;
import org.ripple.power.server.chat.HeartHandler;
import org.ripple.power.server.chat.LoginMessage;
import org.ripple.power.server.chat.LoginOutMessage;
import org.ripple.power.server.chat.LoginUsersMessage;
import org.ripple.power.server.chat.MesToByteEncoder;
import org.ripple.power.server.chat.MessageRecognizer;
import org.ripple.power.server.chat.MessageType;
import org.ripple.power.ui.RPComboBox;
public class P2PServer extends ChannelInitializer<SocketChannel> {
private final ChannelGroup channels = new DefaultChannelGroup(
GlobalEventExecutor.INSTANCE);
protected final BlockingQueue<AMessage> queue = new LinkedBlockingQueue<AMessage>();
private ExecutorService EXECUTOR = Executors.newCachedThreadPool();
private LinkList userLinkList;
private RPComboBox combobox;
public P2PServer(RPComboBox combobox, final JTextField sysMessage) {
this.combobox = combobox;
final RPComboBox co = combobox;
EXECUTOR.execute(new Runnable() {
@Override
public void run() {
while (true) {
try {
AMessage msg = queue.take();
short type = msg.getMessageType();
switch (type) {
case MessageType.CS_CHAT: {
ChatMessage _msg = (ChatMessage) msg;
short channelType = _msg.getType();
if (channelType == 2) {
broadcasts(_msg);
sysMessage.setText("");
} else {
Node node = userLinkList.findUser(_msg
.getToUser());
try {
node.channel.writeAndFlush(_msg);
} catch (Exception e) {
e.printStackTrace();
}
sysMessage.setText("");
}
break;
}
case MessageType.CS_LOGIN: {
LoginMessage _msg = (LoginMessage) msg;
co.addItem(_msg.getUsername());
broadcasts(new LoginUsersMessage(userLinkList
.users()));
break;
}
case MessageType.CS_LOGIN_OUT: {
LoginOutMessage _msg = (LoginOutMessage) msg;
broadcasts(_msg);
co.removeItem(_msg.getUsername());
break;
}
default:
break;
}
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}
});
}
private final LoggingHandler LOGGING_HANDLER = new LoggingHandler();
private final ServerHandler serverHandler = new ServerHandler();
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new ByteMesDecoder(new MessageRecognizer())).addLast(
new MesToByteEncoder());
pipeline.addLast("LOGGING_HANDLER", LOGGING_HANDLER);
pipeline.addLast("handler", serverHandler);
pipeline.addLast("idleStateHandler", new IdleStateHandler(5, 5, 8,
TimeUnit.SECONDS));
pipeline.addLast("heartHandler", new HeartHandler());
}
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
public void connect(int port, LinkList userLinkList) throws Exception {
this.userLinkList = userLinkList;
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 100)
.handler(new LoggingHandler(LogLevel.DEBUG)).childHandler(this);
ChannelFuture f = b.bind(port).sync();
f.channel().closeFuture().sync();
}
public void broadcasts(AMessage msg) {
channels.writeAndFlush(msg);
}
public void addChannel(Channel channel) {
channels.add(channel);
}
public void stopServer() throws Exception {
EXECUTOR.shutdown();
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
@Sharable
private class ServerHandler extends SimpleChannelInboundHandler<AMessage> {
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
private AttributeKey<Node> STATE = AttributeKey.valueOf("client");
@Override
public void channelRegistered(final ChannelHandlerContext ctx) {
Channel channel = ctx.channel();
Node client = new Node();
channel.attr(STATE).setIfAbsent(client);
client.channel = channel;
channels.add(channel);
userLinkList.addUser(client);
ctx.fireChannelRegistered();
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
Channel channel = ctx.channel();
channels.remove(channel);
Node client = channel.attr(STATE).get();
userLinkList.delUser(client);
combobox.removeItem(client.username);
super.channelUnregistered(ctx);
broadcasts(new LoginOutMessage(client.username));
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, AMessage msg)
throws Exception {
Channel channel = ctx.channel();
if (msg instanceof LoginMessage) {
Node client = channel.attr(STATE).get();
client.username = ((LoginMessage) msg).getUsername();
}
queue.add(msg);
}
}
}