Package net.yura.lobby.netty

Source Code of net.yura.lobby.netty.Server

package net.yura.lobby.netty;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
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.util.AttributeKey;
import io.netty.util.concurrent.GlobalEventExecutor;
import java.lang.management.ManagementFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathFactory;
import net.yura.lobby.model.Message;
import net.yura.lobby.server.LobbyServer;
import net.yura.lobby.server.LobbySession;
import net.yura.lobby.server.SocketServer;
import org.w3c.dom.Document;

/**
* @author Yura Mamyrin
*/
public class Server implements SocketServer,NettyMXBean {

    public static final boolean DEBUG_SEND = false;

    public static final Logger logger = Logger.getLogger(Server.class.getName());

    private final int port;
    private final LobbyServer server;

    public Server(int port) {
        this.port = port;

        server = new LobbyServer(this);

        bossGroup = new NioEventLoopGroup();
        workerGroup = new NioEventLoopGroup();

        if (DEBUG_SEND) {
            logger.setLevel(Level.ALL);
        }
    }

    @Override
    public int getPort() {
        return port;
    }

    // HACK for some reason netty sends message in wrong order when they come from different threads
    // HACK so we send all message from the same thread
    private final Executor singleThread = Executors.newSingleThreadExecutor();

    @Override
    public void send(final LobbySession key, final Message message) {
        final Channel chc = getChannel(key);
        if (chc!=null) {
            if (DEBUG_SEND) {
                logger.log(Level.FINE, "sending "+System.identityHashCode(message)+" "+message);
            }
            // HACK to fix ordering of messages
            singleThread.execute(new Runnable() { @Override public void run() {
                try {
                    chc.writeAndFlush(message);
                }
                catch (Exception ex) {
                    logger.log(Level.WARNING,"Failed to send "+key+" "+message,ex);
                }
            }});
        }
        else {
            logger.warning("LobbySession: "+key+" NOT FOUND! can not send "+message);
        }
    }

    @Override
    public void kick(LobbySession key) {
        Channel chc = getChannel(key);
        if (chc!=null) {
            chc.close();
        }
        else {
            logger.warning("LobbySession: "+key+" NOT FOUND! can not kick");
        }
    }

    @Override
    public boolean isConnected(LobbySession key) {
        Channel chc = getChannel(key);
        if (chc!=null) {
            return chc.isOpen();
        }
        else {
            return false;
        }
    }

    @Override
    public List<LobbySession> getLobbySession(String username) {
        List<LobbySession> result = new ArrayList();
        for (Channel channel:allClientChannels) {
            LobbySession session = channel.attr( SESSION_KEY ).get();
            if (
                    (username == null) ||
                    (username.equals( session.getUsername() )) ||
                    ("".equals(username) && session.getUsername()==null)
                ) {
                result.add(session);
            }
        }
        return result;
    }

    public static final AttributeKey<LobbySession> SESSION_KEY = new AttributeKey("LobbySession");
    public final ChannelGroup allClientChannels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

    private Channel getChannel(LobbySession key) {
        // TODO this is not very good and would be much better if instead of the UUID
        // we could store the cannel ID and then we could use allClientChannels.find(Integer)
        for (Channel channel:allClientChannels) {
            LobbySession session = channel.attr( SESSION_KEY ).get();
            if (session == key) {
                return channel;
            }
        }
        return null;
    }

    @Override
    public int getClientCount() {
        return allClientChannels.size();
    }

    @Override
    public void killAllClients() {
        allClientChannels.close();
    }

    @Override
    public void killSocketServer() {
        serverChannel.close();
    }

    EventLoopGroup bossGroup,workerGroup;
    Channel serverChannel;

    @Override
    public void start() throws InterruptedException {

        ServerBootstrap b = new ServerBootstrap();
        b.group(bossGroup, workerGroup)
        .channel(NioServerSocketChannel.class)
        .childHandler(new ChannelInitializer<SocketChannel>() {
            @Override
            public void initChannel(SocketChannel ch) throws Exception {
                ch.pipeline().addLast("decoder",new ProtoDecoder(server));
                ch.pipeline().addLast("encoder",new ProtoEncoder(server));
                ch.pipeline().addLast("handler",new MessageHandler(server,allClientChannels));
            }
        });

        // Bind and start to accept incoming connections.
        ChannelFuture f = b.bind(port).sync();

        serverChannel = f.channel();

        logger.info("Netty server started on port "+port);

        // Wait until the server socket is closed.
        //serverChannel.closeFuture().sync();
    }

    @Override
    public void shutdown() {
        // Shut down all event loops to terminate all threads.
        workerGroup.shutdownGracefully();
        bossGroup.shutdownGracefully();
    }

    public static void main(String[] args) throws Exception {
          LobbyServer.args = args;

          LobbyServer.setupLogging();
          Server myServer = new Server(1964);

          MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
          mbs.registerMBean(myServer, new ObjectName("net.yura.lobby:type=NettyServer") );

          // main thread now becomes the netty thread
          myServer.start();
    }


    @Override
    public String getVersion() {
        try {
            DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
            Document xmlDocument = builder.parse( ServerBootstrap.class.getResourceAsStream("/META-INF/maven/io.netty/netty-all/pom.xml") );

            XPath xPath = XPathFactory.newInstance().newXPath();
            XPathExpression xPathExpression = xPath.compile("/project/parent/version");

            return xPathExpression.evaluate(xmlDocument);
        }
        catch (Exception ex) {
            ex.printStackTrace();
            return ex.toString();
        }
    }

}
TOP

Related Classes of net.yura.lobby.netty.Server

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.