package org.ifsoft.openlink.commands; import org.slf4j.*; import org.slf4j.Logger; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Vector; import java.util.concurrent.ConcurrentHashMap; import org.dom4j.Element; import org.jivesoftware.openfire.admin.AdminManager; import org.jivesoftware.util.Log; import org.xmpp.packet.IQ; import org.xmpp.packet.JID; import org.xmpp.packet.Message; import org.xmpp.packet.IQ.Type; import org.ifsoft.openlink.component.*; /** * An Openlink command is a stateless object responsbile for executing the * provided service. Each subclass will only have one instance that will be * shared across all users sessions. Therefore, it is important to not keep any * information related to executions as permanent data (i.e. as instance or * static variables). Each command has a <tt>code</tt> that should be unique * within a given JID. * <p> * * Commands may have zero or more stages. Each stage is usually used for * gathering information required for the command execution. Users are able to * move forward or backward across the different stages. Commands may not be * cancelled while they are beig executed. However, users may request the * "cancel" action when submiting a stage response indicating that the command * execution should be aborted. Thus, releasing any collected information. * Commands that require user interaction (i.e. have more than one stage) will * have to provide the data forms the user must complete in each stage and the * allowed actions the user might perform during each stage (e.g. go to the * previous stage or go to the next stage). * */ public abstract class OpenlinkCommand { private static final Logger Log = LoggerFactory.getLogger(OpenlinkCommand.class); private String label = getDefaultLabel(); protected OpenlinkComponent traderLyncComponent; protected OpenlinkCommandManager manager = new OpenlinkCommandManager(); public static Map<String, OpenlinkFeatures> featureMap = null; public Map<String, OpenlinkFeatures> getFeatureMap() { if (featureMap == null) { featureMap = new ConcurrentHashMap<String, OpenlinkFeatures>(); } return featureMap; } public OpenlinkCommand(OpenlinkComponent traderLyncComponent) { this.traderLyncComponent = traderLyncComponent; } public OpenlinkComponent getOpenlinkComponent() { return traderLyncComponent; } public String getLabel() { return label; } public void setLabel(String label) { this.label = label; } public boolean validPermissions(SessionData data, String endUser, Element newCommand) { String userAgent = data.getOwner().getNode(); if (!AdminManager.getInstance().isUserAdmin(userAgent, false) && !userAgent.equals(endUser)) { Element note = newCommand.addElement("note"); note.addAttribute("type", "error"); note.setText("Access denied"); return false; } else return true; } /** * Returns true if the requester is allowed to execute this command. By * default only admins are allowed to execute commands. Subclasses may * redefine this method with any specific logic. * <p> * * Note: The bare JID of the requester will be compared with the bare JID of * the admins. * * @param requester * the JID of the user requesting to execute this command. * @return true if the requester is allowed to execute this command. */ public boolean hasPermission(JID requester) { return true; //(AdminManager.getInstance().isUserAdmin(requester.getNode(), false) || ! getOpenlinkComponent().getOpenlinkProfiles(requester).isEmpty()); } /** * Returns the unique identifier for this command for the containing JID. * The code will be used as the node in the disco#items or the node when * executing the command. * * @return the unique identifier for this command for the containing JID. */ public abstract String getCode(); /** * Returns the default label used for describing this commmand. This * information is usually used when returning commands as disco#items. * Admins can later use {@link #setLabel(String)} to set a new label and * reset to the default value at any time. * * @return the default label used for describing this commmand. */ public abstract String getDefaultLabel(); /** * Returns the max number of stages for this command. The number of stages * may vary according to the collected data in previous stages. Therefore, a * SessionData object is passed as a parameter. When the max number of * stages has been reached then the command is ready to be executed. * * @param data * the gathered data through the command stages or <tt>null</tt> * if the command does not have stages or the requester is * requesting the execution for the first time. * @return the max number of stages for this command. */ public abstract int getMaxStages(SessionData data); /** * Executes the command with the specified session data. * * @param data * the gathered data through the command stages or <tt>null</tt> * if the command does not have stages. * @param command * the command element to be sent to the command requester with a * reported data result or note element with the answer of the * execution. */ public abstract Element execute(SessionData data, Element newCommand, Element oldCommand); /** * Adds to the command element the data form or notes required by the * current stage. The current stage is specified in the SessionData. This * method will never be invoked for commands that have no stages. * * @param data * the gathered data through the command stages or <tt>null</tt> * if the command does not have stages or the requester is * requesting the execution for the first time. * @param command * the command element to be sent to the command requester. */ protected abstract boolean addStageInformation(SessionData data, Element newCommand, Element oldCommand); /** * Returns a collection with the allowed actions based on the current stage * as defined in the SessionData. Possible actions are: <tt>prev</tt>, * <tt>next</tt> and <tt>complete</tt>. This method will never be * invoked for commands that have no stages. * * @param data * the gathered data through the command stages or <tt>null</tt> * if the command does not have stages or the requester is * requesting the execution for the first time. * @return a collection with the allowed actions based on the current stage * as defined in the SessionData. */ protected abstract List<Action> getActions(SessionData data); /** * Returns which of the actions available for the current stage is * considered the equivalent to "execute". When the requester sends his * reply, if no action was defined in the command then the action will be * assumed "execute" thus assuming the action returned by this method. This * method will never be invoked for commands that have no stages. * * @param data * the gathered data through the command stages or <tt>null</tt> * if the command does not have stages or the requester is * requesting the execution for the first time. * @return which of the actions available for the current stage is * considered the equivalent to "execute". */ protected abstract Action getExecuteAction(SessionData data); /** * Increments the stage number by one and adds to the command element the * new data form and new allowed actions that the user might perform. * * @param data * the gathered data through the command stages. * @param command * the command element to be sent to the command requester. */ public boolean addNextStageInformation(SessionData data, Element newCommand, Element oldCommand) { // Increment the stage number to the next stage data.setStage(data.getStage() + 1); // Return the data form of the current stage to the command requester. // The // requester will need to specify the action to follow (e.g. execute, // prev, // cancel, etc.) and complete the form is going "forward" boolean proceed = addStageInformation(data, newCommand, oldCommand); if (proceed) addStageActions(data, newCommand, oldCommand); return proceed; } /** * Decrements the stage number by one and adds to the command the data form * and allowed actions that the user might perform of the previous stage. * * @param data * the gathered data through the command stages. * @param command * the command element to be sent to the command requester. */ public boolean addPreviousStageInformation(SessionData data, Element newCommand, Element oldCommand) { // Decrement the stage number to the previous stage data.setStage(data.getStage() - 1); // Return the data form of the current stage to the command requester. // The // requester will need to specify the action to follow (e.g. execute, // prev, // cancel, etc.) and complete the form is going "forward" boolean proceed = addStageInformation(data, newCommand, oldCommand); if (proceed) addStageActions(data, newCommand, oldCommand); return proceed; } /** * Adds the allowed actions to follow from the current stage. Possible * actions are: <tt>prev</tt>, <tt>next</tt> and <tt>complete</tt>. * * @param data * the gathered data through the command stages or <tt>null</tt> * if the command does not have stages or the requester is * requesting the execution for the first time. * @param command * the command element to be sent to the command requester. */ protected void addStageActions(SessionData data, Element newCommand, Element oldCommand) { // Add allowed actions to the response Element actions = newCommand.addElement("actions"); List<Action> validActions = getActions(data); for (OpenlinkCommand.Action action : validActions) { actions.addElement(action.name()); } Action executeAction = getExecuteAction(data); // Add default execute action to the response actions.addAttribute("execute", executeAction.name()); // Store the allowed actions that the user can follow from this stage data.setAllowedActions(validActions); // Store the default execute action to follow if the user does not // specify an // action in his command data.setExecuteAction(executeAction); } public enum Status { /** * The command is being executed. */ executing, /** * The command has completed. The command session has ended. */ completed, /** * The command has been canceled. The command session has ended. */ canceled } public enum Action { /** * The command should be executed or continue to be executed. This is * the default value. */ execute, /** * The command should be canceled. */ cancel, /** * The command should be digress to the previous stage of execution. */ prev, /** * The command should progress to the next stage of execution. */ next, /** * The command should be completed (if possible). */ complete } }