/**
   * Digest the password using the specified algorithm and convert the result to a corresponding
   * hexadecimal string. If exception, the plain credentials string is returned.
   *
   * @param credentials Password or other credentials to use in authenticating this username
   */
  protected String digest(String credentials) {

    // If no MessageDigest instance is specified, return unchanged
    if (hasMessageDigest() == false) return (credentials);

    // Digest the user credentials and return as hexadecimal
    synchronized (this) {
      try {
        md.reset();

        byte[] bytes = null;
        try {
          bytes = credentials.getBytes(getDigestCharset());
        } catch (UnsupportedEncodingException uee) {
          log.error("Illegal digestEncoding: " + getDigestEncoding(), uee);
          throw new IllegalArgumentException(uee.getMessage());
        }
        md.update(bytes);

        return (HexUtils.toHexString(md.digest()));
      } catch (Exception e) {
        log.error(sm.getString("realmBase.digest"), e);
        return (credentials);
      }
    }
  }
  /**
   * Digest password using the algorithm specified and convert the result to a corresponding hex
   * string. If exception, the plain credentials string is returned
   *
   * @param credentials Password or other credentials to use in authenticating this username
   * @param algorithm Algorithm used to do the digest
   * @param encoding Character encoding of the string to digest
   */
  public static final String Digest(String credentials, String algorithm, String encoding) {

    try {
      // Obtain a new message digest with "digest" encryption
      MessageDigest md = (MessageDigest) MessageDigest.getInstance(algorithm).clone();

      // encode the credentials
      // Should use the digestEncoding, but that's not a static field
      if (encoding == null) {
        md.update(credentials.getBytes());
      } else {
        md.update(credentials.getBytes(encoding));
      }

      // Digest the credentials and return as hexadecimal
      return (HexUtils.toHexString(md.digest()));
    } catch (Exception ex) {
      log.error(ex);
      return credentials;
    }
  }
  /**
   * Writes a message to file. If (msg.getMessageNumber() == msg.getTotalNrOfMsgs()) the output
   * stream will be closed after writing.
   *
   * @param msg FileMessage - message containing data to be written
   * @throws IllegalArgumentException - if the factory is opened for read or closed
   * @throws IOException - if a file write error occurs
   * @return returns true if the file is complete and outputstream is closed, false otherwise.
   */
  public boolean writeMessage(FileMessageRemoteInterface msg)
      throws IllegalArgumentException, IOException, RemoteException, RemoteException {
    if (!openForWrite)
      throw new IllegalArgumentException("Can't write message, this factory is reading.");
    if (log.isDebugEnabled())
      log.debug(
          "Message "
              + msg
              + " data "
              + HexUtils.toHexString(msg.getData())
              + " data length "
              + msg.getDataLength()
              + " out "
              + out);

    if (msg.getMessageNumber() <= lastMessageProcessed.get()) {
      // Duplicate of message already processed
      log.warn(
          "Receive Message again -- Sender ActTimeout too short [ name: "
              + msg.getContextName()
              + " war: "
              + msg.getFileName()
              + " data: "
              + HexUtils.toHexString(msg.getData())
              + " data length: "
              + msg.getDataLength()
              + " ]");
      return false;
    }

    FileMessageRemoteInterface previous = msgBuffer.put(Long.valueOf(msg.getMessageNumber()), msg);
    if (previous != null) {
      // Duplicate of message not yet processed
      log.warn(
          "Receive Message again -- Sender ActTimeout too short [ name: "
              + msg.getContextName()
              + " war: "
              + msg.getFileName()
              + " data: "
              + HexUtils.toHexString(msg.getData())
              + " data length: "
              + msg.getDataLength()
              + " ]");
      return false;
    }

    FileMessageRemoteInterface next = null;
    synchronized (this) {
      if (!isWriting) {
        next = msgBuffer.get(Long.valueOf(lastMessageProcessed.get() + 1));
        if (next != null) {
          isWriting = true;
        } else {
          return false;
        }
      } else {
        return false;
      }
    }

    while (next != null) {
      out.write(next.getData(), 0, next.getDataLength());
      lastMessageProcessed.incrementAndGet();
      out.flush();
      if (next.getMessageNumber() == next.getTotalNrOfMsgs()) {
        out.close();
        cleanup();
        return true;
      }
      synchronized (this) {
        next = msgBuffer.get(Long.valueOf(lastMessageProcessed.get() + 1));
        if (next == null) {
          isWriting = false;
        }
      }
    }

    return false;
  }