/**
   * Returns the Clearspace username of the user by id.
   *
   * @param id ID to retrieve Username of.
   * @return The username of the user in Clearspace.
   * @throws org.jivesoftware.openfire.user.UserNotFoundException If the user was not found.
   */
  protected String getUsernameByID(Long id) throws UserNotFoundException {
    // Checks if it is in the cache
    if (usernameCache.containsKey(id)) {
      return usernameCache.get(id);
    }

    // Gets the user's ID from Clearspace
    try {
      String path = ClearspaceUserProvider.USER_URL_PREFIX + "usersByID/" + id;
      Element element =
          executeRequest(org.jivesoftware.openfire.clearspace.ClearspaceManager.HttpType.GET, path);

      String username =
          WSUtils.getElementText(
              element.selectSingleNode("return"), "username"); // TODO: is this right?

      // Escape the username so that it can be used as a JID.
      username = JID.escapeNode(username);

      usernameCache.put(id, username);

      return username;
    } catch (UserNotFoundException unfe) {
      // It is a supported exception, throw it again
      throw unfe;
    } catch (Exception e) {
      // It is not a supported exception, wrap it into a UserNotFoundException
      throw new UserNotFoundException("Unexpected error", e);
    }
  }
  /**
   * If CS throws an exception it handled and transalated to a Openfire exception if possible. This
   * is done using <code>exceptionMap</code> that has a mapping from CS to OF. If no mapping is
   * found then it tries to instantiete the original exception. If this fails it throws a <code>
   * Exception</code> with the message of the CS exception.
   *
   * @param response the response from CS to check if it is an exception message.
   * @throws Exception if the response is an exception message.
   */
  private void checkFault(Element response) throws Exception {
    Node node = response.selectSingleNode("ns1:faultstring");
    if (node != null) {
      String exceptionText = node.getText();

      // Text accepted samples:
      // 'java.lang.Exception: Exception message'
      // 'java.lang.Exception'

      // Get the exception class and message if any
      int index = exceptionText.indexOf(":");
      String className;
      String message;
      // If there is no message, save the class only
      if (index == -1) {
        className = exceptionText;
        message = null;
      } else {
        // Else save both
        className = exceptionText.substring(0, index);
        message = exceptionText.substring(index + 2);
      }

      // Map the exception to a Openfire one, if possible
      if (exceptionMap.containsKey(className)) {
        className = exceptionMap.get(className);
      }

      // Tries to create an instance with the message
      Exception exception;
      try {
        Class exceptionClass = Class.forName(className);
        if (message == null) {
          exception = (Exception) exceptionClass.newInstance();
        } else {
          Constructor constructor = exceptionClass.getConstructor(String.class);
          exception = (Exception) constructor.newInstance(message);
        }
      } catch (Exception e) {
        // failed to create an specific exception, creating a standard one.
        exception = new Exception(exceptionText);
      }

      throw exception;
    }
  }
  /**
   * Returns the Clearspace user id the user by username.
   *
   * @param username Username to retrieve ID of.
   * @return The ID number of the user in Clearspace.
   * @throws org.jivesoftware.openfire.user.UserNotFoundException If the user was not found.
   */
  protected long getUserID(String username) throws UserNotFoundException {
    // Gets the part before of @ of the username param
    if (username.contains("@")) {
      // User's id are only for local users
      if (!XMPPServer.getInstance().isLocal(new JID(username))) {
        throw new UserNotFoundException("Cannot load user of remote server: " + username);
      }
      username = username.substring(0, username.lastIndexOf("@"));
    }

    // Checks if it is in the cache
    if (userIDCache.containsKey(username)) {
      return userIDCache.get(username);
    }

    // Un-escape username.
    String unescapedUsername = JID.unescapeNode(username);
    // Encode potentially non-ASCII characters
    unescapedUsername = URLUTF8Encoder.encode(unescapedUsername);
    // Gets the user's ID from Clearspace
    try {
      String path = ClearspaceUserProvider.USER_URL_PREFIX + "users/" + unescapedUsername;
      Element element =
          executeRequest(org.jivesoftware.openfire.clearspace.ClearspaceManager.HttpType.GET, path);

      Long id = Long.valueOf(WSUtils.getElementText(element.selectSingleNode("return"), "ID"));

      userIDCache.put(username, id);

      return id;
    } catch (UserNotFoundException unfe) {
      // It is a supported exception, throw it again
      throw unfe;
    } catch (Exception e) {
      // It is not a supported exception, wrap it into a UserNotFoundException
      throw new UserNotFoundException("Unexpected error", e);
    }
  }
  /**
   * Returns the Clearspace group id of the group.
   *
   * @param groupname Name of the group to retrieve ID of.
   * @return The ID number of the group in Clearspace.
   * @throws org.jivesoftware.openfire.group.GroupNotFoundException If the group was not found.
   */
  protected long getGroupID(String groupname) throws GroupNotFoundException {
    if (groupIDCache.containsKey(groupname)) {
      return groupIDCache.get(groupname);
    }
    try {
      // Encode potentially non-ASCII characters
      groupname = URLUTF8Encoder.encode(groupname);
      String path = ClearspaceGroupProvider.URL_PREFIX + "groups/" + groupname;
      Element element =
          executeRequest(org.jivesoftware.openfire.clearspace.ClearspaceManager.HttpType.GET, path);

      Long id = Long.valueOf(WSUtils.getElementText(element.selectSingleNode("return"), "ID"));
      // Saves it into the cache
      groupIDCache.put(groupname, id);

      return id;
    } catch (GroupNotFoundException gnfe) {
      // It is a supported exception, throw it again
      throw gnfe;
    } catch (Exception e) {
      // It is not a supported exception, wrap it into a GroupNotFoundException
      throw new GroupNotFoundException("Unexpected error", e);
    }
  }