flexmud.engine.context.ClientContextHandler.java Source code

Java tutorial

Introduction

Here is the source code for flexmud.engine.context.ClientContextHandler.java

Source

/**************************************************************************************************
 * Copyright 2009 Chris Maguire (cwmaguire@gmail.com)                                             *
 *                                                                                                *
 * Flexmud is free software: you can redistribute it and/or modify                                *
 * it under the terms of the GNU General Public License as published by                           *
 * the Free Software Foundation, either version 3 of the License, or                              *
 * (at your option) any later version.                                                            *
 *                                                                                                *
 * Flexmud is distributed in the hope that it will be useful,                                     *
 * but WITHOUT ANY WARRANTY; without even the implied warranty of                                 *
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                  *
 * GNU General Public License for more details.                                                   *
 *                                                                                                *
 * You should have received a copy of the GNU General Public License                              *
 * along with flexmud.  If not, see <http://www.gnu.org/licenses/>.                               *
 **************************************************************************************************/
package flexmud.engine.context;

import flexmud.db.HibernateUtil;
import flexmud.engine.cmd.Command;
import flexmud.engine.cmd.CommandChainCommand;
import flexmud.engine.cmd.ContextOrGenericPromptCommand;
import flexmud.engine.cmd.menu.MenuCommand;
import flexmud.engine.exec.Executor;
import flexmud.net.Client;
import flexmud.security.Account;
import flexmud.security.AccountRole;
import org.apache.log4j.Logger;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Restrictions;

import java.util.*;
import java.util.concurrent.Future;

public class ClientContextHandler {
    private static final Logger LOGGER = Logger.getLogger(ClientContextHandler.class);

    private Context context;
    private Client client;
    private Map<Long, Integer> contextEntryCounts = new HashMap<Long, Integer>();

    public ClientContextHandler(Client client) {
        this.client = client;
    }

    public void init() {
        loadAndSetFirstContext();
    }

    public Context getContext() {
        return context;
    }

    public void setContext(Context newContext) {
        setContext(newContext, true);
    }

    public synchronized void setContext(Context newContext, boolean isPromptRequired) {

        if (doesExistsCheckFail(newContext))
            return;

        if (newContext.isSecure() && doesSecurityCheckFail(newContext))
            return;

        if (doesMaxEntriesCheckFail(newContext))
            return;

        incrementEntryCount(newContext);

        context = newContext;

        /* Future CM: it looks like CachedThreadPool executes jobs in order from a queue,
            but we may still need to put in a delay to prevent later commands
            from sneaking ahead of earlier commands
            We could also put in some sort of dependency check where if a command
            is processed out of order it gets put back in the queue
        */
        initializeAndExecuteCommand(createFlaggedContextCommandChain(isPromptRequired));
    }

    protected Command createFlaggedContextCommandChain(boolean isPromptRequired) {
        List<Command> commands = new ArrayList<Command>();
        Command menuCommand;

        commands.addAll(getEntryCommands());

        menuCommand = createMenuCommand();
        if (menuCommand != null) {
            commands.add(menuCommand);
        }

        if (isPromptRequired) {
            commands.add(getPromptCommand());
        }

        return new CommandChainCommand(commands);
    }

    private List<Command> getEntryCommands() {
        return getFlaggedCommandsWithParamsOrNull(ContextCommandFlag.ENTRY);
    }

    public Command getPromptCommand() {
        List<Command> flaggedPromptCommands = getFlaggedCommandsWithParamsOrNull(ContextCommandFlag.PROMPT);

        if ((flaggedPromptCommands != null && !flaggedPromptCommands.isEmpty())) {
            return flaggedPromptCommands.get(0);
        }

        return new ContextOrGenericPromptCommand();
    }

    private Command getDefaultCommand() {
        List<Command> flaggedDefaultCommands = getFlaggedCommandsWithParamsOrNull(ContextCommandFlag.DEFAULT);

        if ((flaggedDefaultCommands != null && !flaggedDefaultCommands.isEmpty())) {
            return flaggedDefaultCommands.get(0);
        }

        return null;
    }

    public Command createMenuCommand() {
        MenuCommand menuCommand;
        List<ContextCommand> cntxtMenuItems = getAccessibleContextCommands(
                context.getFlaggedContextCommands(ContextCommandFlag.MENU_ITEM));

        if ((cntxtMenuItems != null && !cntxtMenuItems.isEmpty())) {
            menuCommand = new MenuCommand();
            menuCommand.setMenuContextCommands(cntxtMenuItems);
            return menuCommand;
        }

        return null;
    }

    public List<ContextCommand> getAccessibleContextCommands(List<? extends ContextCommand> commands) {
        List<ContextCommand> accessibleCommands = null;
        Account account = client.getAccount();
        AccountRole role = account == null ? null : account.getAccountRole();

        if (role != null && commands != null) {
            accessibleCommands = new ArrayList<ContextCommand>();
            for (ContextCommand command : commands) {
                if (role.hasPermission(command)) {
                    accessibleCommands.add(command);
                }
            }
        }

        return accessibleCommands;
    }

    private boolean doesMaxEntriesCheckFail(Context newContext) {
        if (isMaxEntriesExceeded(newContext)) {
            LOGGER.info("Max context entries exceeded for client " + client.getConnectionID() + ", disconnecting");
            client.sendTextLn(context.getMaxEntriesExceededMessage());
            client.sendTextLn("disconnecting");
            client.disconnect();
            return true;
        }
        return false;
    }

    private boolean doesSecurityCheckFail(Context newContext) {
        Account account = client.getAccount();
        AccountRole role = account == null ? null : account.getAccountRole();

        if (role == null || !role.hasPermission(newContext)) {
            LOGGER.info("Client " + client.getConnectionID() + " does not have permissions on context "
                    + newContext.getName());
            client.sendTextLn("Access denied");
            // ToDO CM: need to reprompt at this point.
            //          or, we could simply re-enter them in their current context
            return true;
        }
        return false;
    }

    private boolean doesExistsCheckFail(Context newContext) {
        if (newContext == null && context == null) {
            LOGGER.error("Could not locate first context");
            client.sendText("Houston, we have a problem: we don't know where to send you. Disconnecting, sorry.");
            client.disconnect();
            return true;
        } else if (newContext == null) {
            LOGGER.error("Tried to send to null context, keeping client in old context.");
            client.sendText("The area you are trying to get to doesn't seem to exist.");
            // ToDO CM: need to reprompt at this point.
            //          or, we could simply re-enter them in their current context
            return true;
        }
        return false;
    }

    protected List<Command> getFlaggedCommandsWithParamsOrNull(ContextCommandFlag flag) {
        Command command;
        List<Command> commands = new ArrayList<Command>();
        List<ContextCommand> cntxtCmds = context.getFlaggedContextCommands(flag);

        if (cntxtCmds != null) {
            for (ContextCommand cntxtCmd : cntxtCmds) {
                try {
                    command = (Command) Class.forName(cntxtCmd.getCommandClassName()).newInstance();
                } catch (Exception e) {
                    LOGGER.error(
                            "Could not instantiate flagged command [" + cntxtCmd.getCommandClassName()
                                    + "] for context [" + context.getName() + "] and flag [" + flag.name() + "]",
                            e);
                    continue;
                }
                addParameters(command, cntxtCmd);
                commands.add(command);
            }
        }

        // Future CM: we could establish a dependency chain right here.

        return commands;
    }

    private void addParameters(Command cmd, ContextCommand cntxtCmd) {
        List<ContextCommandParameter> cntxtCmdParams = new ArrayList<ContextCommandParameter>(
                cntxtCmd.getParameters());
        List<String> parameters = new ArrayList<String>();

        Collections.sort(cntxtCmdParams, new SequenceComparator());

        for (ContextCommandParameter cntxtCmdParam : cntxtCmdParams) {
            parameters.add(cntxtCmdParam.getValue());
        }

        cmd.setCommandArguments(parameters);
    }

    protected void initializeAndExecuteCommands(List<Command> commands) {
        if (commands != null) {
            for (Command command : commands) {
                initializeAndExecuteCommand(command);
            }
        }
    }

    protected Future initializeAndExecuteCommand(Command command) {
        if (command != null) {
            LOGGER.debug("Executing command " + command.getClass().getName());
            command.setClient(client);
            return Executor.exec(command);
        }

        return null;
    }

    private boolean isMaxEntriesExceeded(Context newContext) {
        Integer contextCount;
        contextCount = contextEntryCounts.get(newContext.getId());

        return contextCount != null && context.getMaxEntries() > 0 && contextCount >= context.getMaxEntries();

    }

    private void incrementEntryCount(Context context) {
        Integer entryCount = contextEntryCounts.get(context.getId());
        contextEntryCounts.put(context.getId(), entryCount == null ? 1 : entryCount + 1);
    }

    public void loadAndSetFirstContext() {
        DetachedCriteria detachedCriteria = DetachedCriteria.forClass(Context.class);
        detachedCriteria.add(Restrictions.isNull(Context.PARENT_GROUP_PROPERTY));
        List<Context> contexts = (List<Context>) HibernateUtil.fetch(detachedCriteria);

        Context firstContext;
        if (contexts != null && !contexts.isEmpty()) {
            firstContext = contexts.get(0);
            firstContext.init();
            setContext(firstContext);
        } else {
            setContext(null);
        }
    }

    public void loadAndSetFirstChildContext() {
        ContextGroup childContextGroup = context.getChildGroup();
        List<Context> childContexts = new ArrayList<Context>(childContextGroup.getChildContexts());
        Context firstContext;
        if (!childContexts.isEmpty()) {
            firstContext = childContexts.get(0);
            firstContext.init();
            setContext(firstContext);
        } else {
            setContext(null);
        }
    }

    public void runCommand(String commandString) {
        Command command;

        if (commandString == null || commandString.trim().isEmpty()) {
            initializeAndExecuteCommand(getPromptCommand());
            return;
        }

        command = loadMatchingCommandOrDefault(commandString);

        if (command == null) {
            // Future CM: put something fun in here like random phrases "What?!" "Huh?" "Does not compute", etc.
            // "Hal reports that he's sorry, but he can't do that"
            client.sendTextLn("An error occurred trying to run \"" + commandString + "\"");
            initializeAndExecuteCommand(getPromptCommand());
            return;
        }

        initializeAndExecuteCommand(command);
    }

    private Command loadMatchingCommandOrDefault(String commandString) {
        ContextCommand contextCommand;
        Command command;
        List<String> commandTokens = tokenize(commandString);

        contextCommand = context.getContextCommandForAlias(commandTokens.get(0));

        if (contextCommand != null) {
            command = contextCommand.createCommandInstance();
            command.setCommandArguments(determineParameters(contextCommand.getParameters(), commandTokens));
        } else {
            command = getDefaultCommand();
            command.setCommandArguments(commandTokens);
        }

        return command;
    }

    private List<String> tokenize(String commandString) {
        StringTokenizer stringTokenizer = new StringTokenizer(commandString);
        List<String> words = new ArrayList<String>();

        while (stringTokenizer.hasMoreElements()) {
            words.add(stringTokenizer.nextToken());
        }

        return words;
    }

    private List<String> determineParameters(Set<ContextCommandParameter> preconfiguredParams,
            List<String> commandLineParams) {
        List<ContextCommandParameter> parameters = new ArrayList<ContextCommandParameter>(preconfiguredParams);
        if (preconfiguredParams != null && !preconfiguredParams.isEmpty()) {
            Collections.sort(parameters, new SequenceComparator());
            return getParameterValues(parameters);
        } else {
            return commandLineParams;
        }
    }

    private List<String> getParameterValues(List<ContextCommandParameter> preconfiguredParams) {
        List<String> parameterValues = new ArrayList<String>();
        if (preconfiguredParams != null) {
            for (ContextCommandParameter param : preconfiguredParams) {
                parameterValues.add(param.getValue());
            }
        }
        return parameterValues;
    }

}