/**
   * Returns the result of applying XOR on this attribute's address, using the specified transaction
   * ID when converting IPv6 addresses.
   *
   * @param transactionID the transaction ID to use in case this attribute is encapsulating an IPv6
   *     address.
   * @return the XOR-ed address.
   */
  public TransportAddress getAddress(byte[] transactionID) {
    byte[] xorMask = new byte[16];

    System.arraycopy(Message.MAGIC_COOKIE, 0, xorMask, 0, 4);
    System.arraycopy(transactionID, 0, xorMask, 4, 12);

    return applyXor(xorMask);
  }
  /**
   * Applies a XOR mask to the specified address and then sets it as the value transported by this
   * attribute.
   *
   * @param address the address that we should xor and then record in this attribute.
   * @param transactionID the transaction identifier that we should use when creating the XOR mask.
   */
  public void setAddress(TransportAddress address, byte[] transactionID) {
    byte[] xorMask = new byte[16];

    System.arraycopy(Message.MAGIC_COOKIE, 0, xorMask, 0, 4);
    System.arraycopy(transactionID, 0, xorMask, 4, 12);

    TransportAddress xorAddress = applyXor(address, xorMask);

    super.setAddress(xorAddress);
  }
  /**
   * Runs in {@link #sendKeepAliveMessageThread} and sends STUN keep-alive <tt>Message</tt>s to the
   * STUN server associated with the <tt>StunCandidateHarvester</tt> of this instance.
   *
   * @return <tt>true</tt> if the method is to be invoked again; otherwise, <tt>false</tt>
   */
  private boolean runInSendKeepAliveMessageThread() {
    synchronized (sendKeepAliveMessageSyncRoot) {
      // Since we're going to #wait, make sure we're not canceled yet.
      if (sendKeepAliveMessageThread != Thread.currentThread()) return false;
      if (sendKeepAliveMessageInterval == SEND_KEEP_ALIVE_MESSAGE_INTERVAL_NOT_SPECIFIED) {
        return false;
      }

      // Determine the amount of milliseconds that we'll have to #wait.
      long timeout;

      if (sendKeepAliveMessageTime == -1) {
        /*
         * If we're just starting, don't just go and send a new STUN
         * keep-alive message but rather wait for the whole interval.
         */
        timeout = sendKeepAliveMessageInterval;
      } else {
        timeout =
            sendKeepAliveMessageTime + sendKeepAliveMessageInterval - System.currentTimeMillis();
      }
      // At long last, #wait if necessary.
      if (timeout > 0) {
        try {
          sendKeepAliveMessageSyncRoot.wait(timeout);
        } catch (InterruptedException iex) {
        }
        /*
         * Apart from being the time to send the STUN keep-alive
         * message, it could be that we've experienced a spurious
         * wake-up or that we've been canceled.
         */
        return true;
      }
    }

    sendKeepAliveMessageTime = System.currentTimeMillis();
    try {
      sendKeepAliveMessage();
    } catch (StunException sex) {
      logger.log(Level.INFO, "Failed to send STUN keep-alive message.", sex);
    }
    return true;
  }