docs: private IQ processAdHocCommand(AdHocCommandData requestData) throws NoResponseException, NotConnectedException, InterruptedException {
          // Creates the response with the corresponding data
          AdHocCommandData response = new AdHocCommandData();
          response.setTo(requestData.getFrom());
          response.setStanzaId(requestData.getStanzaId());
          response.setNode(requestData.getNode());
          response.setId(requestData.getTo());
  
          String sessionId = requestData.getSessionID();
          String commandNode = requestData.getNode();
  
          if (sessionId == null) {
              // A new execution request has been received. Check that the
              // command exists
              if (!commands.containsKey(commandNode)) {
                  // Requested command does not exist so return
                  // item_not_found error.
                  return respondError(response, StanzaError.Condition.item_not_found);
              }
  
              // Create new session ID
              sessionId = StringUtils.randomString(15);
  
              try {
                  // Create a new instance of the command with the
                  // corresponding sessionid
                  LocalCommand command;
                  try {
                      command = newInstanceOfCmd(commandNode, sessionId);
                  }
                  catch (InstantiationException | IllegalAccessException | IllegalArgumentException
                                  | InvocationTargetException | NoSuchMethodException | SecurityException e) {
                      StanzaError.Builder xmppError = StanzaError.getBuilder().setCondition(StanzaError.Condition.internal_server_error).setDescriptiveEnText(e.getMessage());
                      return respondError(response, xmppError);
                  }
  
                  response.setType(IQ.Type.result);
                  command.setData(response);
  
                  // Check that the requester has enough permission.
                  // Answer forbidden error if requester permissions are not
                  // enough to execute the requested command
                  if (!command.hasPermission(requestData.getFrom())) {
                      return respondError(response, StanzaError.Condition.forbidden);
                  }
  
                  Action action = requestData.getAction();
  
                  // If the action is unknown then respond an error.
                  if (action != null && action.equals(Action.unknown)) {
                      return respondError(response, StanzaError.Condition.bad_request,
                              AdHocCommand.SpecificErrorCondition.malformedAction);
                  }
  
                  // If the action is not execute, then it is an invalid action.
                  if (action != null && !action.equals(Action.execute)) {
                      return respondError(response, StanzaError.Condition.bad_request,
                              AdHocCommand.SpecificErrorCondition.badAction);
                  }
  
                  // Increase the state number, so the command knows in witch
                  // stage it is
                  command.incrementStage();
                  // Executes the command
                  command.execute();
  
                  if (command.isLastStage()) {
                      // If there is only one stage then the command is completed
                      response.setStatus(Status.completed);
                  }
                  else {
                      // Else it is still executing, and is registered to be
                      // available for the next call
                      response.setStatus(Status.executing);
                      executingCommands.put(sessionId, command);
                      // See if the session reaping thread is started. If not, start it.
                      if (sessionsSweeper == null) {
                          sessionsSweeper = new Thread(new Runnable() {
                              @Override
                              public void run() {
                                  while (true) {
                                      for (String sessionId : executingCommands.keySet()) {
                                          LocalCommand command = executingCommands.get(sessionId);
                                          // Since the command could be removed in the meanwhile
                                          // of getting the key and getting the value - by a
                                          // processed packet. We must check if it still in the
                                          // map.
                                          if (command != null) {
                                              long creationStamp = command.getCreationDate();
                                              // Check if the Session data has expired (default is
                                              // 10 minutes)
                                              // To remove it from the session list it waits for
                                              // the double of the of time out time. This is to
                                              // let
                                              // the requester know why his execution request is
                                              // not accepted. If the session is removed just
                                              // after the time out, then whe the user request to
                                              // continue the execution he will received an
                                              // invalid session error and not a time out error.
                                              if (System.currentTimeMillis() - creationStamp > SESSION_TIMEOUT * 1000 * 2) {
                                                  // Remove the expired session
                                                  executingCommands.remove(sessionId);
                                              }
                                          }
                                      }
                                      try {
                                          Thread.sleep(1000);
                                      }
                                      catch (InterruptedException ie) {
                                          // Ignore.
                                      }
                                  }
                              }
  
                          });
                          sessionsSweeper.setDaemon(true);
                          sessionsSweeper.start();
                      }
                  }
  
                  // Sends the response packet
                  return response;
  
              }
              catch (XMPPErrorException e) {
                  // If there is an exception caused by the next, complete,
                  // prev or cancel method, then that error is returned to the
                  // requester.
                  StanzaError error = e.getStanzaError();
  
                  // If the error type is cancel, then the execution is
                  // canceled therefore the status must show that, and the
                  // command be removed from the executing list.
                  if (StanzaError.Type.CANCEL.equals(error.getType())) {
                      response.setStatus(Status.canceled);
                      executingCommands.remove(sessionId);
                  }
                  return respondError(response, StanzaError.getBuilder(error));
              }
          }
          else {
              LocalCommand command = executingCommands.get(sessionId);
  
              // Check that a command exists for the specified sessionID
              // This also handles if the command was removed in the meanwhile
              // of getting the key and the value of the map.
              if (command == null) {
                  return respondError(response, StanzaError.Condition.bad_request,
                          AdHocCommand.SpecificErrorCondition.badSessionid);
              }
  
              // Check if the Session data has expired (default is 10 minutes)
              long creationStamp = command.getCreationDate();
              if (System.currentTimeMillis() - creationStamp > SESSION_TIMEOUT * 1000) {
                  // Remove the expired session
                  executingCommands.remove(sessionId);
  
                  // Answer a not_allowed error (session-expired)
                  return respondError(response, StanzaError.Condition.not_allowed,
                          AdHocCommand.SpecificErrorCondition.sessionExpired);
              }
  
              /*
               * Since the requester could send two requests for the same
               * executing command i.e. the same session id, all the execution of
               * the action must be synchronized to avoid inconsistencies.
               */
              synchronized (command) {
                  Action action = requestData.getAction();
  
                  // If the action is unknown the respond an error
                  if (action != null && action.equals(Action.unknown)) {
                      return respondError(response, StanzaError.Condition.bad_request,
                              AdHocCommand.SpecificErrorCondition.malformedAction);
                  }
  
                  // If the user didn't specify an action or specify the execute
                  // action then follow the actual default execute action
                  if (action == null || Action.execute.equals(action)) {
                      action = command.getExecuteAction();
                  }
  
                  // Check that the specified action was previously
                  // offered
                  if (!command.isValidAction(action)) {
                      return respondError(response, StanzaError.Condition.bad_request,
                              AdHocCommand.SpecificErrorCondition.badAction);
                  }
  
                  try {
                      // TODO: Check that all the required fields of the form are
                      // TODO: filled, if not throw an exception. This will simplify the
                      // TODO: construction of new commands
  
                      // Since all errors were passed, the response is now a
                      // result
                      response.setType(IQ.Type.result);
  
                      // Set the new data to the command.
                      command.setData(response);
  
                      if (Action.next.equals(action)) {
                          command.incrementStage();
                          command.next(new Form(requestData.getForm()));
                          if (command.isLastStage()) {
                              // If it is the last stage then the command is
                              // completed
                              response.setStatus(Status.completed);
                          }
                          else {
                              // Otherwise it is still executing
                              response.setStatus(Status.executing);
                          }
                      }
                      else if (Action.complete.equals(action)) {
                          command.incrementStage();
                          command.complete(new Form(requestData.getForm()));
                          response.setStatus(Status.completed);
                          // Remove the completed session
                          executingCommands.remove(sessionId);
                      }
                      else if (Action.prev.equals(action)) {
                          command.decrementStage();
                          command.prev();
                      }
                      else if (Action.cancel.equals(action)) {
                          command.cancel();
                          response.setStatus(Status.canceled);
                          // Remove the canceled session
                          executingCommands.remove(sessionId);
                      }
  
                      return response;
                  }
                  catch (XMPPErrorException e) {
                      // If there is an exception caused by the next, complete,
                      // prev or cancel method, then that error is returned to the
                      // requester.
                      StanzaError error = e.getStanzaError();
  
                      // If the error type is cancel, then the execution is
                      // canceled therefore the status must show that, and the
                      // command be removed from the executing list.
                      if (StanzaError.Type.CANCEL.equals(error.getType())) {
                          response.setStatus(Status.canceled);
                          executingCommands.remove(sessionId);
                      }
                      return respondError(response, StanzaError.getBuilder(error));
                  }
              }
          }
      }