org.heliosphere.thot.akka.chat.client.TerminalActor.java Source code

Java tutorial

Introduction

Here is the source code for org.heliosphere.thot.akka.chat.client.TerminalActor.java

Source

/*
 * Copyright(c) 2017 - Heliosphere Corp.
 * ---------------------------------------------------------------------------
 * This file is part of the Heliosphere's project which is licensed under the 
 * Apache license version 2 and use is subject to license terms.
 * You should have received a copy of the license with the project's artifact
 * binaries and/or sources.
 * 
 * License can be consulted at http://www.apache.org/licenses/LICENSE-2.0
 * ---------------------------------------------------------------------------
 */
package org.heliosphere.thot.akka.chat.client;

import java.awt.Color;
import java.time.LocalTime;
import java.util.Locale;

import org.apache.commons.collections4.ListUtils;
import org.heliosphere.thot.akka.chat.client.command.ChatCommandDomainType;
import org.heliosphere.thot.akka.chat.client.command.ChatCommandProtocol;
import org.heliosphere.thot.akka.chat.server.lobby.LobbyMessageProtocol;
import org.heliosphere.thot.akka.chat.server.room.RoomMessageProtocol;
import org.heliosphere.thot.akka.chat.server.supervisor.ChatSupervisorProtocol;
import org.heliosphere.thot.akka.chat.server.user.UserMessageProtocol;

import com.heliosphere.athena.base.command.internal.ICommand;
import com.heliosphere.athena.base.command.internal.ICommandListener;
import com.heliosphere.athena.base.command.internal.ICommandParameter;
import com.heliosphere.athena.base.command.internal.coordinator.ICommandCoordinator;
import com.heliosphere.athena.base.command.internal.exception.CommandException;
import com.heliosphere.athena.base.command.internal.exception.CommandNotFoundException;
import com.heliosphere.athena.base.command.internal.protocol.ICommandProtocolType;
import com.heliosphere.athena.base.command.processor.HelpCommandProcessor;
import com.heliosphere.athena.base.command.protocol.DefaultCommandCodeType;
import com.heliosphere.athena.base.command.protocol.DefaultCommandProtocol;
import com.heliosphere.athena.base.command.response.ICommandResponse;
import com.heliosphere.athena.base.file.internal.FileException;
import com.heliosphere.athena.base.message.protocol.DefaultMessageProtocolUsingClasses;
import com.heliosphere.athena.base.terminal.CommandTerminal;
import com.heliosphere.athena.base.terminal.OutputTerminal;

import akka.actor.AbstractActor;
import akka.actor.ActorRef;
import akka.actor.ActorSelection;
import akka.actor.Props;
import akka.actor.Status;
import akka.actor.Terminated;
import akka.event.Logging;
import akka.event.LoggingAdapter;
import lombok.NonNull;

/**
 * A actor managing a {@code terminal / console}.
 * <p>
 * Terminal actor is responsible to create the actor tree to handle the processing of commands. Typically it will
 * receive commands that have been validated by the underlying terminal object. It will then dispatch the commands 
 * according to their type to a specialized actor. When it receives the command's response, it takes the appropriate 
 * action such as quitting or displaying the result of the command on the console.
 * <hr>
 * @author <a href="mailto:christophe.resse@gmail.com">Christophe Resse - Heliosphere</a>
 * @version 1.0.0
 */
public class TerminalActor extends AbstractActor implements ICommandListener {
    /**
     * Akka logging adapter.
     */
    private final LoggingAdapter LOG = Logging.getLogger(getContext().getSystem(), this);

    /**
     * Terminal.
     */
    private CommandTerminal terminal = null;

    /**
     * Output terminal for the discussion.
     */
    private OutputTerminal output = null;

    /**
     * Command coordinator actor.
     */
    private ICommandCoordinator coordinator = null;

    /**
     * Reference to the chat system actor.
     */
    private ActorRef chatSystem = null;

    /**
     * Reference to the lobby actor.
     */
    private ActorRef lobbyProxy = null;

    /**
     * Reference to the room actor.
     */
    private ActorRef roomProxy = null;

    /**
     * Current lobby.
     */
    private Locale lobby = null;

    /**
     * Current room.
     */
    private String room = null;

    /**
     * Underlying user for this chat client.
     */
    private String user = null;

    /**
     * Static creation pattern for a {@link TerminalActor}.
     * <hr>
     * @param name Terminal's session name.
     * @param terminalConfigFilename Terminal configuration file name.
     * @param commandFileName Command file name.
     * @return {@link Props}.
     */
    public static Props props(final String name, final String terminalConfigFilename,
            final String commandFileName) {
        return Props.create(TerminalActor.class, name, terminalConfigFilename, commandFileName);
    }

    /**
     * Creates a new terminal actor.
     * <hr>
     * @param name Terminal's session name.
     * @param terminalConfigFilename Terminal configuration file name.
     * @param commandFileName Command XML file name.
     * @throws FileException Thrown in case an error occurred while trying to access the XML commands file.
     */
    @SuppressWarnings("nls")
    public TerminalActor(String name, String terminalConfigFilename, final String commandFileName)
            throws FileException {
        try {
            // Create the command terminal.
            terminal = new CommandTerminal(name, terminalConfigFilename, commandFileName);
            terminal.registerListener(this);

            // Create the chat window console (output only)
            output = new OutputTerminal("Discussion Console", "/config/terminal/terminal-discussion.properties");

            // Register some pre-implemented commands.
            terminal.getCoordinator().registerExecutable(
                    new HelpCommandProcessor(terminal.getInterpreter().getCommandDefinitions()));

            // Contact the chat system supervisor and send him a message to get its time.
            //ActorSelection selection = getContext().actorSelection("/user/chat-supervisor");
            ActorSelection selection = getContext()
                    .actorSelection("akka.tcp://ChatSystem@127.0.0.1:2551/user/chat-supervisor");

            selection.tell(new ChatSupervisorProtocol.InitiateConversation(), getSelf());
        } catch (FileException e) {
            LOG.error(String.format("Unable to load file: %1s due to: %2s", commandFileName, e.getMessage()));
        }
    }

    @Override
    public Receive createReceive() {
        return receiveBuilder()
                //.match(ICommand.class, command -> handleAndDispatchCommand(command)) // Generic handler & dispatcher for commands.
                //.match(IMessage.class, message -> handleAndDispatchMessage(message))
                //.match(ICommandResponse.class, response -> handleResponse(response))
                .match(ChatSupervisorProtocol.ConversationInitiated.class,
                        response -> handleConversationInitiated())
                .match(ChatSupervisorProtocol.UserRegistered.class, response -> handleUserRegistered(response))
                .match(LobbyMessageProtocol.LobbyList.class, response -> handleMessageLobbyList(response))
                .match(LobbyMessageProtocol.LobbyCreated.class, response -> handleMessageLobbyCreated(response))
                .match(LobbyMessageProtocol.LobbyDeleted.class, response -> handleMessageLobbyDeleted(response))
                .match(LobbyMessageProtocol.LobbyJoined.class, response -> handleMessageLobbyJoined(response))
                .match(RoomMessageProtocol.RoomList.class, response -> handleMessageRoomList(response))
                .match(RoomMessageProtocol.RoomCreated.class, response -> handleMessageRoomCreated(response))
                .match(RoomMessageProtocol.RoomJoined.class, response -> handleMessageRoomConnected(response))
                .match(RoomMessageProtocol.RoomLeft.class, response -> handleMessageRoomDisconnected(response))
                .match(UserMessageProtocol.UserList.class, response -> handleMessageUserList(response))
                .match(UserMessageProtocol.Said.class, message -> handleSaid(message))
                .match(UserMessageProtocol.Whispered.class, message -> handleWhispered(message))
                .match(DefaultMessageProtocolUsingClasses.SubmitCommand.class,
                        message -> handleSubmitCommand(message))
                .match(Status.Failure.class, failure -> handleFailure(failure))
                .match(Terminated.class, this::onTerminated).matchAny(any -> handleUnknown(any)).build();
    }

    /**
     * Handles unknown objects.
     * <hr>
     * @param any Object received.
     */
    @SuppressWarnings("nls")
    private final void handleUnknown(Object any) {
        LOG.info(this + "Received an unknown object: " + any);
        terminal.appendToPane("Received an unknown object: " + any + "\n", Color.ORANGE);
        terminal.resume();
    }

    /**
     * Handles failures.
     * <hr>
     * @param failure Failure.
     */
    @SuppressWarnings("nls")
    private final void handleFailure(final Status.Failure failure) {
        LOG.error("Received a failure: " + failure.cause().getMessage());
        terminal.appendToPane("Failure: " + failure.cause().getMessage() + "\n\n", Color.ORANGE);
        terminal.resume();
    }

    /**
     * Handles a {@link org.heliosphere.thot.akka.chat.server.supervisor.ChatSupervisorProtocol.ConversationInitiated} message.
     */
    @SuppressWarnings("nls")
    private final void handleConversationInitiated() {
        chatSystem = getSender();
        terminal.appendToPane("Chat server has been contacted and has answered.\nPlease register with a user.\n\n",
                Color.WHITE);

        terminal.start();
        output.start();
    }

    //   /**
    //    * Handles and dispatch messages.
    //    * <hr>
    //    * @param message Message to process.
    //    */
    //   @SuppressWarnings("nls")
    //   protected void handleAndDispatchMessage(final IMessage message)
    //   {
    //      LOG.info(String.format("Received message [category=%1s, type=%2s, sender=%3s]", message.getCategoryType(), message.getType(), getSender()));
    //
    //      try
    //      {
    //         switch (message.getCategoryType())
    //         {
    //            case REQUEST:
    //               // So then it to the server side to be processed.
    //               chatSystem.tell(message, getSelf());
    //               break;
    //
    //            case NOTIFICATION:
    //               // So then it to the server side to be processed.
    //               chatSystem.tell(message, getSelf());
    //               break;
    //
    //            case REPLY:
    //               // Reply messages are coming from server side.
    //               handleReplyMessage(message);
    //               break;
    //
    //            default:
    //               break;
    //         }
    //      }
    //      catch (Exception e)
    //      {
    //         // In case of a failure notify the end-user!
    //         terminal.getTerminal().println(String.format("An error occurred due to: %1s", e.getMessage()));
    //      }
    //   }

    //   /**
    //    * Handles reply {@link Message}.
    //    * <hr>
    //    * @param message Reply message to process.
    //    * @throws Exception Thrown in case an error occurred while processing a message.
    //    */
    //   @SuppressWarnings("nls")
    //   private final void handleReplyMessage(final IMessage message) throws Exception
    //   {
    //      switch ((ChatMessageType) message.getType())
    //      {
    //         case QUERY_SERVER_TIME:
    //            // This is the first request made to the server to get its reference.
    //            chatSystem = getSender();
    //            System.out.println("Server time is: " + ((DefaultMessageData) message.getContent()).getData());
    //            break;
    //
    //         case REGISTER_USER:
    //            break;
    //
    //         case QUERY_WHO:
    //         case STATUS_AFK:
    //
    //         case NONE:
    //         default:
    //            LOG.warning(this + " does not handle message (protocol) of type: " + message.getType());
    //            break;
    //      }
    //
    //      // Resume the terminal so that end-user can continue working with it.
    //      terminal.resume();
    //   }

    //   /**
    //    * Handles and dispatch (normal) commands.
    //    * <hr>
    //    * @param command Command to process.
    //    */
    //   @SuppressWarnings("nls")
    //   protected void handleAndDispatchCommand(final ICommand command)
    //   {
    //      if (command.getMetadata().getProtocolType() instanceof ChatCommandProtocol)
    //      {
    //         switch ((ChatCommandProtocol) command.getMetadata().getProtocolType())
    //         {
    //            case LOBBY:
    //               if (chatSystem != null && user != null)
    //               {
    //                  handleLobbyCommand(command);
    //               }
    //               break;
    //
    //            case USER_REGISTER:
    //
    //            default:
    //               LOG.warning("Does not handle command (protocol) of type: " + command.getMetadata().getProtocolType());
    //               break;
    //         }
    //      }
    //
    //      //      // Check the command type.
    //      //      if (command.getMetadata().getProtocolCategory() == DefaultCommandCategoryType.NORMAL)
    //      //      {
    //      //      }
    //      //      else
    //      //      {
    //      //         CommandException exception = new CommandException(String.format("Cannot process command: %1s, expected a 'normal' command category type!", command.getMetadata().getProtocolCategory()));
    //      //         getSender().tell(new Status.Failure(exception), getSelf());
    //      //      }
    //   }

    /**
     * @param command
     * @throws CommandException
     */
    @SuppressWarnings({ "nls", "incomplete-switch" })
    private final void handleMessageCommand(final ICommand command) throws CommandException {
        String text = null;
        String user = null;

        if (roomProxy == null) {
            throw new CommandException("Please join a room first!");
        }

        switch ((ChatCommandProtocol) command.getMetadata().getProtocolType()) {
        case MESSAGE_SAY:
            text = (String) command.getParameter("text").getValue();
            if (text == null) {
                throw new CommandException("Cannot send a null message!");
            }

            roomProxy.tell(new UserMessageProtocol.Say(this.user, text), getSelf());
            break;

        case MESSAGE_WHISPER:
            text = (String) command.getParameter("text").getValue();
            user = (String) command.getParameter("user").getValue();
            if (user == null) {
                throw new CommandException("Recipient user name cannot be null!");
            } else if (text == null) {
                throw new CommandException("Cannot send a null message!");
            }
            roomProxy.tell(new UserMessageProtocol.Whisper(user, text, this.user), getSelf());
            break;

        default:
            break;
        }

        terminal.resume();
    }

    /**
     * @param command
     */
    @SuppressWarnings("nls")
    private final void handleLobbyCommand(final ICommand command) {
        Locale locale = null;

        switch ((ChatCommandProtocol) command.getProtocol()) {
        case LOBBY_LIST:
            chatSystem.tell(new LobbyMessageProtocol.LobbyList(this.user, null), getSelf());
            break;

        case LOBBY_CREATE:
            locale = new Locale((String) command.getParameter("create").getValue());
            chatSystem.tell(new LobbyMessageProtocol.LobbyCreate(this.user, locale), getSelf());
            break;

        case LOBBY_DELETE:
            locale = new Locale((String) command.getParameter("delete").getValue());
            chatSystem.tell(new LobbyMessageProtocol.LobbyDelete(this.user, locale), getSelf());
            break;

        case LOBBY_JOIN:
            locale = new Locale((String) command.getParameter("join").getValue());
            chatSystem.tell(new LobbyMessageProtocol.LobbyJoin(this.user, locale), getSelf());
            break;

        case LOBBY_LEAVE:
            locale = new Locale((String) command.getParameter("leave").getValue());
            chatSystem.tell(new LobbyMessageProtocol.LobbyLeave(this.user, locale), getSelf());
            break;

        default:
            terminal.getTerminal().println("Error -> Unable to process command: " + command.getText());
            terminal.getTerminal().println();
            terminal.resume();
            break;
        }
    }

    /**
     * @param command
     * @throws CommandException
     */
    @SuppressWarnings({ "nls", "incomplete-switch" })
    private final void handleRoomCommand(final ICommand command) throws CommandException {
        String room = null;

        switch ((ChatCommandProtocol) command.getProtocol()) {
        case ROOM_LIST:
            if (lobbyProxy == null) {
                throw new CommandException("You must have joined a lobby first before querying for rooms!");
            }
            lobbyProxy.tell(new RoomMessageProtocol.RoomList(this.user, lobby, null), getSelf());
            break;

        case ROOM_CREATE:
            room = (String) command.getParameter("create").getValue();
            if (lobbyProxy == null) {
                throw new CommandException("You must have joined a lobby first to create a room!");
            }
            lobbyProxy.tell(new RoomMessageProtocol.RoomCreate(this.user, room), getSelf());
            break;

        case ROOM_DELETE:
            room = (String) command.getParameter("delete").getValue();
            if (lobbyProxy == null) {
                throw new CommandException("You must have joined a lobby first to delete a room!");
            }
            lobbyProxy.tell(new RoomMessageProtocol.RoomDelete(this.user, room), getSelf());
            break;

        case ROOM_JOIN:
            room = (String) command.getParameter("join").getValue();
            if (lobbyProxy == null) {
                throw new CommandException("You must have joined a lobby first to join a room!");
            }
            lobbyProxy.tell(new RoomMessageProtocol.RoomJoin(this.user, room), getSelf());
            break;

        case ROOM_LEAVE:
            room = (String) command.getParameter("leave").getValue();
            if (roomProxy == null) {
                throw new CommandException("You must have joined a room first to leave it!");
            }
            roomProxy.tell(new RoomMessageProtocol.RoomLeave(this.user, room), getSelf());
            break;
        }
    }

    @SuppressWarnings({ "nls", "incomplete-switch" })
    private final void handleUserCommand(final ICommand command) throws CommandException {
        String user = null;

        switch ((ChatCommandProtocol) command.getProtocol()) {
        case USER_LIST:
            if (roomProxy == null) {
                throw new CommandException("You must have joined a room first before querying for users!");
            }

            roomProxy.tell(new UserMessageProtocol.UserList(room, null), getSelf());
            break;

        case USER_REGISTER:
            user = (String) command.getParameter("register").getValue();
            chatSystem.tell(new ChatSupervisorProtocol.RegisterUser(user), getSelf());
            break;

        case USER_UNREGISTER:
            if (lobbyProxy == null) {
                throw new CommandException("You must have joined a lobby first to delete a room!");
            }

            //lobbyProxy.tell(new ChatSupervisorProtocol.UnregisterUser(this.user), getSelf());
            break;
        }
    }

    @SuppressWarnings("nls")
    private final void handleSayCommand(final ICommand command) {
        ICommandParameter text = command.getParameter("text");

        if (text != null) {
            roomProxy.tell(new UserMessageProtocol.Say(user, (String) text.getValue()), getSelf());
        } else {
            // TODO Throw exception as message!
        }

        terminal.resume();
    }

    @SuppressWarnings("nls")
    private final void handleWhisperCommand(final ICommand command) {
        ICommandParameter user = command.getParameter("user");
        ICommandParameter text = command.getParameter("text");

        if (user == null) {
            // TODO Throw Exception message!
        } else if (text == null) {
            // TODO Throw Exception message!
        } else {
            roomProxy.tell(
                    new UserMessageProtocol.Whisper((String) user.getValue(), (String) text.getValue(), this.user),
                    getSelf());
        }

        terminal.resume();
    }

    @SuppressWarnings("nls")
    @Override
    public final void onCommand(final ICommand command) {
        // Pauses the terminal thread until the command response has been received.
        terminal.pause();

        try {
            // Before issuing a command, the end-user must register its user name.
            if (user == null && command.getProtocol() != ChatCommandProtocol.USER_REGISTER) {
                throw new CommandException("Please register a user before trying to issue a command!");
            }

            if (command.getProtocol() instanceof DefaultCommandProtocol) {
                handleDefaultCommandProtocol(command);
            } else if (command.getProtocol() instanceof ChatCommandProtocol) {
                switch ((ChatCommandDomainType) ((ICommandProtocolType) command.getProtocol()).getDomain()) {
                case USER:
                    handleUserCommand(command);
                    break;

                case LOBBY:
                    handleLobbyCommand(command);
                    break;

                case ROOM:
                    handleRoomCommand(command);
                    break;

                case MESSAGE:
                    handleMessageCommand(command);
                    break;

                default:
                    break;
                }
            }
        } catch (CommandException e) {
            terminal.appendToPane(String.format("Error-> %1s\n\n", e.getMessage()), Color.ORANGE);
            terminal.resume();
        }
    }

    /**
     * Handles command from protocol: {@link DefaultCommandProtocol}.
     * <hr>
     * @param command Command to handle.
     */
    @SuppressWarnings("nls")
    private final void handleDefaultCommandProtocol(final ICommand command) {
        switch ((DefaultCommandProtocol) command.getMetadata().getProtocolType()) {
        case HELP:
            // This command is handled by a pre-defined command processor.
            try {
                coordinator.execute(command);
            } catch (CommandNotFoundException e) {
                terminal.appendToPane(String.format("[ERROR] %1s\n", e.getMessage()), Color.ORANGE);
            } finally {
                terminal.resume();
            }
            break;

        case AFK:
            break;

        case QUIT:
            break;

        default:
            handleCommandUnknown(command);
            break;
        }
    }

    //   /**
    //    * Handles command from protocol: {@link ChatCommandProtocol}.
    //    * <hr>
    //    * @param command Command to handle.
    //    */
    //   private final void handleChatCommandProtocol(final ICommand command)
    //   {
    //      switch ((ChatCommandProtocol) command.getMetadata().getProtocolType())
    //      {
    //         case USER_REGISTER:
    //            handleCommandUserRegister(command);
    //            break;
    //
    //         case LOBBY_LIST:
    //            handleCommandLobbyList(command);
    //            break;
    //
    //         case LOBBY_CONNECT:
    //         case LOBBY_CREATE:
    //         case LOBBY_DELETE:
    //
    //         default:
    //            handleCommandUnknown(command);
    //            break;
    //      }
    //   }

    /**
     * Handles normal commands.
     * <hr>
     * @param command Command to process.
     */
    protected void handleCommandNormal(final ICommand command) {
        //coordinator.tell(command, getSelf());
    }

    /**
     * Handles administration commands.
     * <hr>
     * @param command Command to process.
     */
    protected void handleCommandAdministration(final @NonNull ICommand command) {
        // Empty, must be overridden by subclasses.
    }

    /**
     * Handles debug commands.
     * <hr>
     * @param command Command to process.
     */
    protected void handleCommandDebug(final @NonNull ICommand command) {
        // Empty, must be overridden by subclasses.
    }

    /**
     * Handles super administration commands.
     * <hr>
     * @param command Command to process.
     */
    protected void handleCommandSuperAdministration(final @NonNull ICommand command) {
        // Empty, must be overridden by subclasses.
    }

    /**
     * Handles system commands.
     * <hr>
     * @param command Command to process.
     */
    protected void handleCommandSystem(final @NonNull ICommand command) {
        // Empty, must be overridden by subclasses.
    }

    /**
     * Handles a command response.
     * <hr>
     * @param response Command response to handle.
     */
    @SuppressWarnings("nls")
    protected void handleResponse(final @NonNull ICommandResponse response) {
        switch ((DefaultCommandCodeType) response.getOrder()) {
        case DISPLAY_TERMINAL:

            // Display the status of the command execution then the exceptions and then the messages.
            terminal.getTerminal().println("[status: " + response.getStatus() + " for command: "
                    + response.getCommand().getMetadata().getName() + "]");

            for (Exception e : ListUtils.emptyIfNull(response.getExceptions())) {
                terminal.getTerminal().println("   Error: " + e.getMessage());
            }

            for (String element : ListUtils.emptyIfNull(response.getMessages())) {
                terminal.getTerminal().println(element);
            }
            break;

        case QUIT:
            getContext().system().terminate();
            terminal.getTerminal().println("System is shutting down...");
            break;

        default:
            break;
        }

        // Resumes the terminal thread.
        terminal.resume();
    }

    /**
     * Handles {@link ChatCommandProtocol#LOBBY_LIST} command.
     * <hr>
     * @param command Command to handle.
     */
    private void handleCommandLobbyList(final ICommand command) {
        if (chatSystem != null) {
            if (user != null) {
                chatSystem.tell(new LobbyMessageProtocol.LobbyList(user, null), getSelf());
            }
        }
    }

    /**
     * Handles a {@link org.heliosphere.thot.akka.chat.server.supervisor.ChatSupervisorProtocol.UserRegistered} message.
     * <hr>
     * @param response Message to handle.
     */
    @SuppressWarnings("nls")
    private void handleUserRegistered(final ChatSupervisorProtocol.UserRegistered response) {
        this.user = response.getUser();

        terminal.setPrompt(String.format("Command (%1s):>", response.getUser()));
        terminal.appendToPane(String.format("User: %1s is registered with chat system. \n\n", response.getUser()),
                Color.WHITE);

        terminal.resume();
    }

    /**
     * Handles a {@link org.heliosphere.thot.akka.chat.supervisor.ChatSupervisorProtocol.LobbyList} message.
     * <hr>
     * @param response Message to handle.
     */
    @SuppressWarnings("nls")
    private void handleMessageLobbyList(final LobbyMessageProtocol.LobbyList response) {
        if (response.getLobbies().isEmpty()) {
            terminal.getTerminal().println("No lobby!");
        } else {
            terminal.getTerminal().println(String.format("%1d existing lobby(ies):", response.getLobbies().size()));
            for (Locale locale : response.getLobbies()) {
                terminal.getTerminal().println(String.format("|   %1s", locale.toString()));
            }
            terminal.getTerminal().println();
        }

        terminal.resume();
    }

    /**
     * Handles a {@link org.heliosphere.thot.akka.chat.server.room.RoomMessageProtocol.RoomList} message.
     * <hr>
     * @param response Message to handle.
     */
    @SuppressWarnings("nls")
    private void handleMessageRoomList(final RoomMessageProtocol.RoomList response) {
        if (response.getRooms().isEmpty()) {
            terminal.getTerminal().println("No room for lobby: lobby-" + response.getLobby());
            terminal.getTerminal().println();
        } else {
            terminal.getTerminal().println(String.format("%1d existing room(s):", response.getRooms().size()));
            for (String room : response.getRooms()) {
                terminal.getTerminal().println(String.format("|   %1s", room));
            }
            terminal.getTerminal().println();
        }

        terminal.resume();
    }

    /**
     * Handles a {@link org.heliosphere.thot.akka.chat.server.lobby.LobbyMessageProtocol.LobbyCreated} message.
     * <hr>
     * @param response Message to handle.
     */
    @SuppressWarnings("nls")
    private void handleMessageLobbyCreated(final LobbyMessageProtocol.LobbyCreated response) {
        terminal.appendToPane(String.format("Lobby: lobby-%1s has been created.\n\n", response.getLobby()),
                Color.WHITE);

        terminal.resume();
    }

    /**
     * Handles a {@link org.heliosphere.thot.akka.chat.server.lobby.LobbyMessageProtocol.LobbyDeleted} message.
     * <hr>
     * @param response Message to handle.
     */
    @SuppressWarnings("nls")
    private void handleMessageLobbyDeleted(final LobbyMessageProtocol.LobbyDeleted response) {
        terminal.appendToPane(String.format("Lobby: %1s has been deleted.", response.getLobby()), Color.WHITE);

        terminal.resume();
    }

    /**
     * Handles a {@link org.heliosphere.thot.akka.chat.server.lobby.LobbyMessageProtocol.LobbyJoined} message.
     * <hr>
     * @param response Message to handle.
     */
    @SuppressWarnings("nls")
    private void handleMessageLobbyJoined(final LobbyMessageProtocol.LobbyJoined response) {
        lobbyProxy = getSender();
        lobby = response.getLobby();

        terminal.appendToPane(String.format("User: %1s has joined lobby-%2s.\n\n", response.getUser(),
                response.getLobby().toString()), Color.WHITE);

        output.setTitle(String.format("Chat window for user [%1s] on [lobby-%2s]", user, lobby.toString()));
        output.appendToPane(String.format("Welcome %1s in: lobby-%2s\n\n", response.getUser(), lobby.toString()),
                Color.WHITE);

        terminal.setPrompt(
                String.format("Command (%1s:%2s):>", response.getUser(), response.getLobby().toString()));

        output.getIO().getFrame().setVisible(true);
        terminal.getIO().getFrame().setVisible(true); // Give back focus to terminal window.
        terminal.resume();
    }

    /**
     * Handles a {@link org.heliosphere.thot.akka.chat.supervisor.ChatSupervisorProtocol.RoomCreated} message.
     * <hr>
     * @param response Message to handle.
     */
    @SuppressWarnings("nls")
    private void handleMessageRoomCreated(final RoomMessageProtocol.RoomCreated response) {
        terminal.appendToPane(String.format("Room: room-%1s has been created.\n\n", response.getRoom()),
                Color.WHITE);

        terminal.resume();
    }

    /**
     * Handles a {@link org.heliosphere.thot.akka.chat.supervisor.ChatSupervisorProtocol.RoomJoined} message.
     * <hr>
     * @param response Message to handle.
     */
    @SuppressWarnings("nls")
    private void handleMessageRoomConnected(final RoomMessageProtocol.RoomJoined response) {
        roomProxy = getSender();
        room = response.getRoom();
        terminal.appendToPane(String.format("User: %1s has joined room: %2s on lobby-%3s.\n\n", response.getUser(),
                response.getRoom(), lobby), Color.WHITE);

        output.setTitle(String.format("Chat window for user [%1s] on [lobby-%2s] in room [%3s]", user,
                lobby.toString(), room));
        output.appendToPane(String.format("Welcome %1s in room %2s.\n\n", response.getUser(), response.getRoom()),
                Color.WHITE);

        terminal.setPrompt(
                String.format("Command (%1s:%2s@%3s):>", response.getUser(), lobby.toString(), response.getRoom()));

        terminal.resume();
    }

    /**
     * Handles a {@link org.heliosphere.thot.akka.chat.supervisor.ChatSupervisorProtocol.RoomLeft} message.
     * <hr>
     * @param response Message to handle.
     */
    @SuppressWarnings("nls")
    private void handleMessageRoomDisconnected(final RoomMessageProtocol.RoomLeft response) {
        roomProxy = null;
        room = null;
        terminal.getTerminal()
                .println(String.format("User: %1s has left room: %2s.", response.getUser(), response.getRoom()));
        terminal.getTerminal().println();

        terminal.setPrompt(String.format("Command (%1s:%2s):>", response.getUser(), lobby.toString()));

        terminal.resume();
    }

    /**
     * Handles a {@link org.heliosphere.thot.akka.chat.server.user.UserMessageProtocol.UserList} message.
     * <hr>
     * @param response Message to handle.
     */
    @SuppressWarnings("nls")
    private void handleMessageUserList(final UserMessageProtocol.UserList response) {
        if (response.getUsers().isEmpty()) {
            terminal.getTerminal().println("No user in room: " + response.getRoom());
            terminal.getTerminal().println();
        } else {
            terminal.getTerminal().println(String.format("%1d existing user(s) in room: %2s...",
                    Integer.valueOf(response.getUsers().size()), response.getRoom()));
            for (String user : response.getUsers()) {
                terminal.getTerminal().println(String.format("|   %1s", user));
            }
            terminal.getTerminal().println();
        }

        terminal.resume();
    }

    //   private final void executeAndStart()
    //   {
    //      // Do we have some submitted commands to execute first?
    //      if (terminal.hasCommandToSubmit()) 
    //      {
    //         terminal.doSubmitCommand();         
    //      }
    //      
    //      // User can start entering some commands.
    //      terminal.start();
    //      output.start();
    //
    //   }

    /**
     * Handles a {@code said} message.
     * <hr>
     * @param message Message to handle.
     */
    private void handleSaid(final UserMessageProtocol.Said message) {
        output.printSay(LocalTime.now().toString(), message.getUser(), message.getMessage());
    }

    /**
     * Handles a {@code whispered} message.
     * <hr>
     * @param message Message to handle.
     */
    private void handleWhispered(final UserMessageProtocol.Whispered message) {
        output.printWhisper(LocalTime.now().toString(), message.getUser(), message.getMessage(),
                message.getSender());
    }

    /**
     * Handles a {@code submit command} message.
     * <hr>
     * @param message Message to handle.
     */
    private void handleSubmitCommand(final DefaultMessageProtocolUsingClasses.SubmitCommand message) {
        terminal.submitCommand(message);
    }

    /**
     * Handles unknown commands.
     * <hr>
     * @param command Unknown command to process.
     */
    protected void handleCommandUnknown(final @NonNull ICommand command) {
        // Empty, must be overridden by subclasses.
    }

    /**
     * Called when a child actor is terminated.
     * <hr>
     * @param message Message. 
     */
    @SuppressWarnings("nls")
    private void onTerminated(final Terminated message) {
        LOG.info("Actor {} has been terminated", message.getActor());
    }
}