Package org.menacheri.jetserver.handlers.netty

Source Code of org.menacheri.jetserver.handlers.netty.WebSocketLoginHandler

package org.menacheri.jetserver.handlers.netty;

import java.util.List;

import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.ChannelFutureListener;
import org.jboss.netty.channel.ChannelHandler.Sharable;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
import org.jboss.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import org.menacheri.jetserver.app.GameRoom;
import org.menacheri.jetserver.app.Player;
import org.menacheri.jetserver.app.PlayerSession;
import org.menacheri.jetserver.app.Session;
import org.menacheri.jetserver.communication.NettyTCPMessageSender;
import org.menacheri.jetserver.event.Event;
import org.menacheri.jetserver.event.Events;
import org.menacheri.jetserver.event.impl.DefaultEvent;
import org.menacheri.jetserver.event.impl.ReconnetEvent;
import org.menacheri.jetserver.service.LookupService;
import org.menacheri.jetserver.service.UniqueIDGeneratorService;
import org.menacheri.jetserver.service.impl.ReconnectSessionRegistry;
import org.menacheri.jetserver.util.Credentials;
import org.menacheri.jetserver.util.JetConfig;
import org.menacheri.jetserver.util.SimpleCredentials;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.gson.Gson;

/**
* This login handler will parse incoming login events to get the
* {@link Credentials} and lookup {@link Player} and {@link GameRoom} objects.
* It kicks of the session creation process and will then send the
* {@link Events#START} event object to websocket client.
*
* @author Abraham Menacherry
*
*/
@Sharable
public class WebSocketLoginHandler extends SimpleChannelUpstreamHandler
{
  private static final Logger LOG = LoggerFactory
      .getLogger(WebSocketLoginHandler.class);

  private LookupService lookupService;
  protected ReconnectSessionRegistry reconnectRegistry;
  protected UniqueIDGeneratorService idGeneratorService;
 
  private Gson gson;

  @SuppressWarnings({ "rawtypes", "unchecked" })
  @Override
  public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
      throws Exception
  {
    Channel channel = ctx.getChannel();
    if (e.getMessage() instanceof TextWebSocketFrame)
    {
      TextWebSocketFrame frame = (TextWebSocketFrame) e.getMessage();
      String data = frame.getText();
      LOG.trace("From websocket: " + data);
      Event event = gson.fromJson(data, DefaultEvent.class);
      int type = event.getType();
      if (Events.LOG_IN == type)
      {
        LOG.trace("Login attempt from {}", channel.getRemoteAddress());
        List<String> credList = null;
        credList = (List) event.getSource();
        Player player = lookupPlayer(credList.get(0), credList.get(1));
        handleLogin(player, channel);
        handleGameRoomJoin(player, channel, credList.get(2));
      }
      else if (type == Events.RECONNECT)
      {
        LOG.debug("Reconnect attempt from {}", channel.getRemoteAddress());
        PlayerSession playerSession = lookupSession((String)event.getSource());
        handleReconnect(playerSession, channel);
      }
      else
      {
        LOG.error(
            "Invalid event {} sent from remote address {}. "
                + "Going to close channel {}",
            new Object[] { event.getType(),
                channel.getRemoteAddress(), channel.getId() });
        closeChannelWithLoginFailure(channel);
      }
    }
    else
    {
      closeChannelWithLoginFailure(channel);
    }
  }

  public PlayerSession lookupSession(final String reconnectKey)
  {
    PlayerSession playerSession = (PlayerSession)reconnectRegistry.getSession(reconnectKey);
    if(null != playerSession)
    {
      synchronized(playerSession){
        // if its an already active session then do not allow a
        // reconnect. So the only state in which a client is allowed to
        // reconnect is if it is "NOT_CONNECTED"
        if(playerSession.getStatus() == Session.Status.NOT_CONNECTED)
        {
          playerSession.setStatus(Session.Status.CONNECTING);
        }
        else
        {
          playerSession = null;
        }
      }
    }
    return playerSession;
  }
 
  protected void handleReconnect(PlayerSession playerSession, Channel channel)
  {
    if (null != playerSession)
    {
      channel.write(eventToFrame(Events.LOG_IN_SUCCESS, null));
      GameRoom gameRoom = playerSession.getGameRoom();
      gameRoom.disconnectSession(playerSession);
      if (null != playerSession.getTcpSender())
        playerSession.getTcpSender().close();

      handleReJoin(playerSession, gameRoom, channel);
    }
    else
    {
      // Write future and close channel
      closeChannelWithLoginFailure(channel);
    }
  }
 
  protected void handleReJoin(PlayerSession playerSession, GameRoom gameRoom, Channel channel)
  {
    // Set the tcp channel on the session.
    NettyTCPMessageSender sender = new NettyTCPMessageSender(channel);
    playerSession.setTcpSender(sender);
    // Connect the pipeline to the game room.
    gameRoom.connectSession(playerSession);
    channel.write(Events.GAME_ROOM_JOIN_SUCCESS, null);//assumes that the protocol applied will take care of event objects.
    playerSession.setWriteable(true);// TODO remove if unnecessary. It should be done in start event
    // Send the re-connect event so that it will in turn send the START event.
    playerSession.onEvent(new ReconnetEvent(sender));
  }
 
  public Player lookupPlayer(String username, String password)
  {
    Credentials credentials = new SimpleCredentials(username, password);
    Player player = lookupService.playerLookup(credentials);
    if (null == player)
    {
      LOG.error("Invalid credentials provided by user: {}", credentials);
    }
    return player;
  }

  public void handleLogin(Player player, Channel channel)
  {
    if (null != player)
    {
      channel.write(eventToFrame(Events.LOG_IN_SUCCESS, null));
    }
    else
    {
      // Write future and close channel
      closeChannelWithLoginFailure(channel);
    }
  }

  protected void closeChannelWithLoginFailure(Channel channel)
  {
    // Close the connection as soon as the error message is sent.
    channel.write(eventToFrame(Events.LOG_IN_FAILURE, null)).addListener(
        ChannelFutureListener.CLOSE);
  }

  public void handleGameRoomJoin(Player player, Channel channel, String refKey)
  {
    GameRoom gameRoom = lookupService.gameRoomLookup(refKey);
    if (null != gameRoom)
    {
      PlayerSession playerSession = gameRoom.createPlayerSession(player);
      gameRoom.onLogin(playerSession);
      String reconnectKey = (String)idGeneratorService
          .generateFor(playerSession.getClass());
      playerSession.setAttribute(JetConfig.RECONNECT_KEY, reconnectKey);
      playerSession.setAttribute(JetConfig.RECONNECT_REGISTRY, reconnectRegistry);
      LOG.trace("Sending GAME_ROOM_JOIN_SUCCESS to channel {}",
          channel.getId());
      ChannelFuture future = channel.write(eventToFrame(
          Events.GAME_ROOM_JOIN_SUCCESS, reconnectKey));
      connectToGameRoom(gameRoom, playerSession, future);
    }
    else
    {
      // Write failure and close channel.
      ChannelFuture future = channel.write(eventToFrame(
          Events.GAME_ROOM_JOIN_FAILURE, null));
      future.addListener(ChannelFutureListener.CLOSE);
      LOG.error(
          "Invalid ref key provided by client: {}. Channel {} will be closed",
          refKey, channel.getId());
    }
  }

  public void connectToGameRoom(final GameRoom gameRoom,
      final PlayerSession playerSession, ChannelFuture future)
  {
    future.addListener(new ChannelFutureListener()
    {
      @Override
      public void operationComplete(ChannelFuture future)
          throws Exception
      {
        Channel channel = future.getChannel();
        LOG.trace(
            "Sending GAME_ROOM_JOIN_SUCCESS to channel {} completed",
            channel.getId());
        if (future.isSuccess())
        {
          // Set the tcp channel on the session.
          NettyTCPMessageSender sender = new NettyTCPMessageSender(
              channel);
          playerSession.setTcpSender(sender);
          // Connect the pipeline to the game room.
          gameRoom.connectSession(playerSession);
          // Send the connect event so that it will in turn send the
          // START event.
          playerSession.onEvent(Events.connectEvent(sender));
        }
        else
        {
          LOG.error("Sending GAME_ROOM_JOIN_SUCCESS message to client was failure, channel will be closed");
          channel.close();
        }
      }
    });
  }

  protected TextWebSocketFrame eventToFrame(byte opcode, Object payload)
  {
    Event event = Events.event(payload, opcode);
    return new TextWebSocketFrame(gson.toJson(event));
  }

  public LookupService getLookupService()
  {
    return lookupService;
  }

  public void setLookupService(LookupService lookupService)
  {
    this.lookupService = lookupService;
  }

  public Gson getGson()
  {
    return gson;
  }

  public void setGson(Gson gson)
  {
    this.gson = gson;
  }

  public ReconnectSessionRegistry getReconnectRegistry()
  {
    return reconnectRegistry;
  }

  public void setReconnectRegistry(ReconnectSessionRegistry reconnectRegistry)
  {
    this.reconnectRegistry = reconnectRegistry;
  }

  public UniqueIDGeneratorService getIdGeneratorService()
  {
    return idGeneratorService;
  }

  public void setIdGeneratorService(UniqueIDGeneratorService idGeneratorService)
  {
    this.idGeneratorService = idGeneratorService;
  }

}
TOP

Related Classes of org.menacheri.jetserver.handlers.netty.WebSocketLoginHandler

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.