/**
   * Refresh locks held by the authorization <code>authID</code>.
   *
   * <p>(remember that the lock may have expired)
   *
   * @param authID Authorization identifing Lock to refresh
   * @param transaction Transaction with authorization for lockID
   * @return <code>true</code> if lock was found and refreshed
   * @throws IOException If transaction not authorized to refresh authID
   * @throws IllegalArgumentException If authID or transaction not provided
   */
  public synchronized boolean refresh(String authID, Transaction transaction) throws IOException {
    if (authID == null) {
      throw new IllegalArgumentException("lockID required");
    }

    if ((transaction == null) || (transaction == Transaction.AUTO_COMMIT)) {
      throw new IllegalArgumentException(
          "Tansaction required (with authorization for " + authID + ")");
    }

    Lock lock;
    boolean refresh = false;

    for (Iterator i = allLocks().iterator(); i.hasNext(); ) {
      lock = (Lock) i.next();

      if (lock.isExpired()) {
        i.remove();
      } else if (lock.isMatch(authID)) {
        if (lock.isAuthorized(transaction)) {
          lock.refresh();
          refresh = true;
        } else {
          throw new IOException("Not authorized to refresh " + lock);
        }
      }
    }

    return refresh;
  }
  /**
   * Release locks held by the authorization <code>authID</code>.
   *
   * <p>(remember that the lock may have expired)
   *
   * @param authID Authorization identifing Lock to release
   * @param transaction Transaction with authorization for lockID
   * @return <code>true</code> if lock was found and released
   * @throws IOException If transaction not authorized to release authID
   * @throws IllegalArgumentException If authID or transaction not provided
   */
  public boolean release(String authID, Transaction transaction) throws IOException {
    // LOGGER.info("release called on lock: " + authID + ", trans: "
    //  + transaction);

    if (authID == null) {
      throw new IllegalArgumentException("lockID required");
    }

    if ((transaction == null) || (transaction == Transaction.AUTO_COMMIT)) {
      throw new IllegalArgumentException(
          "Tansaction required (with authorization for " + authID + ")");
    }

    Lock lock;
    boolean release = false;

    // This could be done more efficiently, and perhaps cleaner,
    // but these maps within a map are just nasty.  The previous way of
    // calling iterator.remove() didn't actually remove anything, as it
    // was only iterating through the values of a map, which I believe
    // java just copies, so it's immutable.  Or perhaps we just moved
    // through too many iterator layers...
    for (Iterator i = lockTables.values().iterator(); i.hasNext(); ) {
      Map fidMap = (Map) i.next();
      Set unLockedFids = new HashSet();

      for (Iterator j = fidMap.keySet().iterator(); j.hasNext(); ) {
        String fid = (String) j.next();
        lock = (Lock) fidMap.get(fid);
        // LOGGER.info("checking lock " + lock + ", is match "
        //    + lock.isMatch(authID));

        if (lock.isExpired()) {
          unLockedFids.add(fid);

          // fidMap.remove(fid); concurrent modification error.
        } else if (lock.isMatch(authID)) {
          // LOGGER.info("matches, is authorized: "
          //    + lock.isAuthorized(transaction));

          if (lock.isAuthorized(transaction)) {
            unLockedFids.add(fid);

            // fidMap.remove(fid);
            release = true;
          } else {
            throw new IOException("Not authorized to release " + lock);
          }
        }
      }

      for (Iterator k = unLockedFids.iterator(); k.hasNext(); ) {
        fidMap.remove(k.next());
      }
    }

    return release;
  }
  /**
   * Checks mutability of featureID for this transaction.
   *
   * <p>Two behaviors are defined by FeatureLocking:
   *
   * <ul>
   *   <li>TransactionLock (Blocking): lock held by a Transaction<br>
   *       Authorization is granted to the Transaction holding the Lock. Conflict will result in a
   *       block until the Transaction holding the lock completes. (This behavior is equivalent to a
   *       Database row-lock, or a java synchronized statement)
   *   <li>FeatureLock (Error): lock held by a FeatureLock<br>
   *       Authorization is based on the set of Authorization IDs held by the provided Transaction.
   *       Conflict will result in an error. (This behavior is equivalent to the WFS locking
   *       specification)
   * </ul>
   *
   * <p>Right now we are just going to error out with an exception
   *
   * @param typeName Feature type to check against
   * @param featureID FeatureID to check
   * @param transaction Provides Authorization
   * @throws FeatureLockException If transaction does not have sufficient authroization
   */
  public void assertAccess(String typeName, String featureID, Transaction transaction)
      throws FeatureLockException {
    Lock lock = getLock(typeName, featureID);

    // LOGGER.info("asserting access on lock for " + typeName + ", fid: "
    //  + featureID + ", transaction: " + transaction + ", lock " + lock);

    if ((lock != null) && !lock.isAuthorized(transaction)) {
      throw new FeatureLockException(
          "Transaction does not have authorization for " + typeName + ":" + featureID);
    }
  }