Commit 0f0b2cb4 authored by Gaston Dombiak's avatar Gaston Dombiak Committed by gato

Refactoring work. JM-609

git-svn-id: http://svn.igniterealtime.org/svn/repos/wildfire/trunk@3636 b35dd754-fafc-0310-a699-88a17e54d16e
parent 99230e51
......@@ -11,8 +11,8 @@
package org.jivesoftware.wildfire.commands;
import org.dom4j.Element;
import org.xmpp.packet.JID;
import org.jivesoftware.wildfire.XMPPServer;
import org.xmpp.packet.JID;
import java.util.List;
......@@ -107,9 +107,10 @@ public abstract class AdHocCommand {
*
* @param data the gathered data through the command stages or <tt>null</tt> if the
* command does not have stages.
* @return a reported data or note element with the answer of the execution.
* @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);
public abstract void execute(SessionData data, Element command);
/**
* Adds to the command element the data form or notes required by the current stage. The
......@@ -154,9 +155,7 @@ public abstract class AdHocCommand {
* 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 or <tt>null</tt> if the
* command does not have stages or the requester is requesting the execution for the
* first time.
* @param data the gathered data through the command stages.
* @param command the command element to be sent to the command requester.
*/
public void addNextStageInformation(SessionData data, Element command) {
......@@ -171,12 +170,10 @@ public abstract class AdHocCommand {
}
/**
* Decrements the stage number by one and adds to the command the data form and allowed
* 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 or <tt>null</tt> if the
* command does not have stages or the requester is requesting the execution for the
* first time.
* @param data the gathered data through the command stages.
* @param command the command element to be sent to the command requester.
*/
public void addPreviousStageInformation(SessionData data, Element command) {
......
......@@ -2,7 +2,7 @@
* $Revision: 3023 $
* $Date: 2005-11-02 18:00:15 -0300 (Wed, 02 Nov 2005) $
*
* Copyright (C) 2005 Jive Software. All rights reserved.
* Copyright (C) 2006 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
......@@ -12,24 +12,16 @@ package org.jivesoftware.wildfire.commands;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.QName;
import org.jivesoftware.wildfire.IQHandlerInfo;
import org.jivesoftware.wildfire.XMPPServer;
import org.jivesoftware.wildfire.auth.UnauthorizedException;
import org.jivesoftware.wildfire.disco.*;
import org.jivesoftware.wildfire.forms.spi.XDataFormImpl;
import org.jivesoftware.wildfire.handler.IQHandler;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.StringUtils;
import org.xmpp.forms.DataForm;
import org.xmpp.forms.FormField;
import org.xmpp.packet.IQ;
import org.xmpp.packet.JID;
import org.xmpp.packet.PacketError;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
/**
* An AdHocCommandHandler is responsbile for providing discoverable information about the
......@@ -62,234 +54,18 @@ public class AdHocCommandHandler extends IQHandler
private IQDiscoInfoHandler infoHandler;
private IQDiscoItemsHandler itemsHandler;
/**
* Map that holds the offered commands by this server. Note: Key=commandCode, Value=command.
* commandCode matches the node attribute sent by command requesters.
* Manager that keeps the list of ad-hoc commands and processing command requests.
*/
private Map<String, AdHocCommand> commands = new ConcurrentHashMap<String, AdHocCommand>();
/**
* Map that holds the number of command sessions of each requester.
* Note: Key=requester full's JID, Value=number of sessions
*/
private Map<String, AtomicInteger> sessionsCounter = new ConcurrentHashMap<String, AtomicInteger>();
/**
* Map that holds the command sessions. Used mainly to quickly locate a SessionData.
* Note: Key=sessionID, Value=SessionData
*/
private Map<String, SessionData> sessions = new ConcurrentHashMap<String, SessionData>();
private AdHocCommandManager manager;
public AdHocCommandHandler() {
super("Ad-Hoc Commands Handler");
info = new IQHandlerInfo("command", NAMESPACE);
manager = new AdHocCommandManager();
}
public IQ handleIQ(IQ packet) throws UnauthorizedException {
IQ reply = IQ.createResultIQ(packet);
Element iqCommand = packet.getChildElement();
// Only packets of type SET can be processed
if (!IQ.Type.set.equals(packet.getType())) {
// Answer a bad_request error
reply.setChildElement(iqCommand.createCopy());
reply.setError(PacketError.Condition.bad_request);
return reply;
}
String sessionid = iqCommand.attributeValue("sessionid");
String commandCode = iqCommand.attributeValue("node");
String from = packet.getFrom().toString();
AdHocCommand command = commands.get(commandCode);
if (sessionid == null) {
// A new execution request has been received. Check that the command exists
if (command == null) {
// Requested command does not exist so return item_not_found error.
reply.setChildElement(iqCommand.createCopy());
reply.setError(PacketError.Condition.item_not_found);
}
else {
// Check that the requester has enough permission. Answer forbidden error if
// requester permissions are not enough to execute the requested command
if (!command.hasPermission(packet.getFrom())) {
reply.setChildElement(iqCommand.createCopy());
reply.setError(PacketError.Condition.forbidden);
return reply;
}
// Create new session ID
sessionid = StringUtils.randomString(15);
Element childElement = reply.setChildElement("command", NAMESPACE);
if (command.getMaxStages(null) == 0) {
// The command does not require any user interaction (returns results only)
// Execute the command and return the execution result which may be a
// data form (i.e. report data) or a note element
Element answer = command.execute(null);
childElement.addAttribute("sessionid", sessionid);
childElement.addAttribute("node", commandCode);
childElement.addAttribute("status", AdHocCommand.Status.completed.name());
// Add the execution result to the reply
childElement.add(answer);
}
else {
// The command requires user interactions (ie. has stages)
// Check that the user has not excedded the limit of allowed simultaneous
// command sessions.
AtomicInteger counter = sessionsCounter.get(from);
if (counter == null) {
synchronized (from.intern()) {
counter = sessionsCounter.get(from);
if (counter == null) {
counter = new AtomicInteger(0);
sessionsCounter.put(from, counter);
}
}
}
int limit = JiveGlobals.getIntProperty("xmpp.command.limit", 100);
if (counter.incrementAndGet() > limit) {
counter.decrementAndGet();
// Answer a not_allowed error since the user has exceeded limit. This
// checking prevents bad users from consuming all the system memory by not
// allowing them to create infinite simultaneous command sessions.
reply.setChildElement(iqCommand.createCopy());
reply.setError(PacketError.Condition.not_allowed);
return reply;
}
// Originate a new command session.
SessionData session = new SessionData(sessionid);
sessions.put(sessionid, session);
// Add to the child element the data form the user must complete and
// the allowed actions
command.addNextStageInformation(null, childElement);
}
}
}
else {
// An execution session already exists and the user has requested to perform a
// certain action.
String action = iqCommand.attributeValue("action");
SessionData session = sessions.get(sessionid);
// Check that a Session exists for the specified sessionID
if (session == null) {
// Answer a bad_request error (bad-sessionid)
reply.setChildElement(iqCommand.createCopy());
reply.setError(PacketError.Condition.bad_request);
return reply;
}
// Check if the Session data has expired (default is 10 minutes)
int timeout = JiveGlobals.getIntProperty("xmpp.command.timeout", 10 * 60 * 1000);
if (System.currentTimeMillis() - session.getCreationStamp() > timeout) {
// TODO Check all sessions that might have timed out (use another thread?)
// Remove the old session
removeSessionData(sessionid, from);
// Answer a not_allowed error (session-expired)
reply.setChildElement(iqCommand.createCopy());
reply.setError(PacketError.Condition.not_allowed);
return reply;
}
synchronized (sessionid.intern()) {
// Check if the user is requesting to cancel the command
if (AdHocCommand.Action.cancel.name().equals(action)) {
// User requested to cancel command execution so remove the session data
removeSessionData(sessionid, from);
// Generate a canceled confirmation response
Element childElement = reply.setChildElement("command", NAMESPACE);
childElement.addAttribute("sessionid", sessionid);
childElement.addAttribute("node", commandCode);
childElement.addAttribute("status", AdHocCommand.Status.canceled.name());
}
// If the user didn't specify an action then follow the default execute action
if (action == null || AdHocCommand.Action.execute.name().equals(action)) {
action = session.getExecuteAction().name();
}
// Check that the specified action was previously offered
if (!session.isValidAction(action)) {
// Answer a bad_request error (bad-action)
reply.setChildElement(iqCommand.createCopy());
reply.setError(PacketError.Condition.bad_request);
return reply;
}
else if (AdHocCommand.Action.prev.name().equals(action)) {
// Move to the previous stage and add to the child element the data form
// the user must complete and the allowed actions of the previous stage
Element childElement = reply.setChildElement("command", NAMESPACE);
childElement.addAttribute("sessionid", sessionid);
childElement.addAttribute("node", commandCode);
childElement.addAttribute("status", AdHocCommand.Status.executing.name());
command.addPreviousStageInformation(session, childElement);
}
else if (AdHocCommand.Action.next.name().equals(action)) {
// Store the completed form in the session data
saveCompletedForm(iqCommand, session);
// Move to the next stage and add to the child element the new data form
// the user must complete and the new allowed actions
Element childElement = reply.setChildElement("command", NAMESPACE);
childElement.addAttribute("sessionid", sessionid);
childElement.addAttribute("node", commandCode);
childElement.addAttribute("status", AdHocCommand.Status.executing.name());
command.addNextStageInformation(session, childElement);
}
else if (AdHocCommand.Action.complete.name().equals(action)) {
// Store the completed form in the session data
saveCompletedForm(iqCommand, session);
// Execute the command and return the execution result which may be a
// data form (i.e. report data) or a note element
Element answer = command.execute(session);
Element childElement = reply.setChildElement("command", NAMESPACE);
childElement.addAttribute("sessionid", sessionid);
childElement.addAttribute("node", commandCode);
childElement.addAttribute("status", AdHocCommand.Status.completed.name());
// Add the execution result to the reply
childElement.add(answer);
// Command has been executed so remove the session data
removeSessionData(sessionid, from);
}
}
}
return reply;
}
/**
* Stores in the SessionData the fields and their values as specified in the completed
* data form by the user.
*
* @param iqCommand the command element containing the data form element.
* @param session the SessionData for this command execution.
*/
private void saveCompletedForm(Element iqCommand, SessionData session) {
Element formElement = iqCommand.element(QName.get("x", "jabber:x:data"));
if (formElement != null) {
// Generate a Map with the variable names and variables values
Map<String, List<String>> data = new HashMap<String, List<String>>();
DataForm dataForm = new DataForm(formElement);
for (FormField field : dataForm.getFields()) {
data.put(field.getVariable(), field.getValues());
}
// Store the variables and their values in the session data
session.addStageForm(data);
}
}
/**
* Releases the data kept for the command execution whose id is sessionid. The number of
* commands executions currently being executed by the user (full JID) will be decreased.
*
* @param sessionid id of the session that identifies this command execution.
* @param from the full JID of the command requester.
*/
private void removeSessionData(String sessionid, String from) {
sessions.remove(sessionid);
if (sessionsCounter.get(from).decrementAndGet() <= 0) {
// Remove the AtomicInteger when no commands are being executed
sessionsCounter.remove(from);
}
return manager.process(packet);
}
public IQHandlerInfo getInfo() {
......@@ -325,7 +101,7 @@ public class AdHocCommandHandler extends IQHandler
}
else {
// Only include commands that the sender can execute
AdHocCommand command = commands.get(node);
AdHocCommand command = manager.getCommand(node);
return command != null && command.hasPermission(senderJID);
}
}
......@@ -337,7 +113,7 @@ public class AdHocCommandHandler extends IQHandler
}
else {
Element item;
for (AdHocCommand command : commands.values()) {
for (AdHocCommand command : manager.getCommands()) {
// Only include commands that the sender can invoke (i.e. has enough permissions)
if (command.hasPermission(senderJID)) {
item = DocumentHelper.createElement("item");
......@@ -372,7 +148,7 @@ public class AdHocCommandHandler extends IQHandler
infoHandler.removeServerNodeInfoProvider(NAMESPACE);
itemsHandler.removeServerNodeInfoProvider(NAMESPACE);
// Stop commands
for (AdHocCommand command : commands.values()) {
for (AdHocCommand command : manager.getCommands()) {
stopCommand(command);
}
}
......@@ -385,7 +161,7 @@ public class AdHocCommandHandler extends IQHandler
* @param command the new ad-hoc command to add.
*/
public void addCommand(AdHocCommand command) {
commands.put(command.getCode(), command);
manager.addCommand(command);
startCommand(command);
}
......@@ -396,7 +172,7 @@ public class AdHocCommandHandler extends IQHandler
* @param command the ad-hoc command to remove.
*/
public void removeCommand(AdHocCommand command) {
if (commands.remove(command.getCode()) != null) {
if (manager.removeCommand(command)) {
stopCommand(command);
}
}
......
......@@ -10,6 +10,8 @@
package org.jivesoftware.wildfire.commands;
import org.xmpp.packet.JID;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
......@@ -22,11 +24,12 @@ import java.util.Map;
*
* @author Gaston Dombiak
*/
class SessionData {
public class SessionData {
private long creationStamp;
private String id;
private JID owner;
/**
* Map that keeps the association of variables and values obtained in each stage.
......@@ -47,25 +50,35 @@ class SessionData {
*/
private int stage;
public SessionData(String sessionid) {
SessionData(String sessionid, JID owner) {
this.id = sessionid;
this.creationStamp = System.currentTimeMillis();
this.stage = 0;
this.stage = -1;
this.owner = owner;
}
public String getId() {
return id;
}
/**
* Returns the JID of the entity that is executing the command.
*
* @return the JID of the entity that is executing the command.
*/
public JID getOwner() {
return owner;
}
public long getCreationStamp() {
return creationStamp;
}
public AdHocCommand.Action getExecuteAction() {
AdHocCommand.Action getExecuteAction() {
return executeAction;
}
public void setExecuteAction(AdHocCommand.Action executeAction) {
void setExecuteAction(AdHocCommand.Action executeAction) {
this.executeAction = executeAction;
}
......@@ -74,7 +87,7 @@ class SessionData {
*
* @param allowedActions list of valid actions.
*/
public void setAllowedActions(List<AdHocCommand.Action> allowedActions) {
void setAllowedActions(List<AdHocCommand.Action> allowedActions) {
if (allowedActions == null) {
allowedActions = new ArrayList<AdHocCommand.Action>();
}
......@@ -88,7 +101,7 @@ class SessionData {
* @param actionName the name of the action to validate.
* @return true if the specified action is valid in the current stage.
*/
public boolean isValidAction(String actionName) {
boolean isValidAction(String actionName) {
for (AdHocCommand.Action action : allowedActions) {
if (actionName.equals(action.name())) {
return true;
......@@ -97,7 +110,7 @@ class SessionData {
return false;
}
public void addStageForm(Map<String, List<String>> data) {
void addStageForm(Map<String, List<String>> data) {
stagesData.put(stage, data);
}
......@@ -108,7 +121,9 @@ class SessionData {
*/
public Map<String, List<String>> getData() {
Map<String, List<String>> data = new HashMap<String, List<String>>();
data.putAll((Map<String, List<String>>) stagesData.values());
for (Map<String, List<String>> stageData : stagesData.values()) {
data.putAll(stageData);
}
return data;
}
......@@ -132,7 +147,7 @@ class SessionData {
*
* @param stage the current stage where the requester is located.
*/
public void setStage(int stage) {
void setStage(int stage) {
this.stage = stage;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment