Designing RMI Applications

Using a Lock Expiry Thread

import java.rmi.*;
import java.rmi.server.*;
/*
 Has timer-based lock management on server-side
 */

public class Account3_Impl extends UnicastRemoteObject implements Account3 {
  private static final int TIMER_DURATION = 120000; // Two minutes
  private static final int THREAD_SLEEP_TIME = 10000; // 10 seconds

  private Money _balance;
  private String _currentClient;
  private int _timeLeftUntilLockIsReleased;

  public Account3_Impl(Money startingBalance)
    throws RemoteException {
    _balance = startingBalance;
    _timeLeftUntilLockIsReleased = 0;
    new Thread(new CountDownTimer()).start();
  }

  public synchronized Money getBalance()
    throws RemoteException, LockedAccountException {
    checkAccess();
    return _balance;
  }

  public synchronized void makeDeposit(Money amount)
    throws RemoteException, LockedAccountException, NegativeAmountException {
    checkAccess();
    checkForNegativeAmount(amount);
    _balance.add(amount);
    return;
  }

  public synchronized void makeWithdrawal(Money amount)
    throws RemoteException, OverdraftException, LockedAccountException, NegativeAmountException {
    checkAccess();
    checkForNegativeAmount(amount);
    checkForOverdraft(amount);
    _balance.subtract(amount);
    return;
  }

  private void checkAccess() throws LockedAccountException {
    String clientHost = wrapperAroundGetClientHost();

    if (null == _currentClient) {
      _currentClient = clientHost;
    } else {
      if (!_currentClient.equals(clientHost)) {
        throw new LockedAccountException();
      }
    }
    resetCounter();
    return;
  }

  private void resetCounter() {
    _timeLeftUntilLockIsReleased = TIMER_DURATION;
  }

  private void releaseLock() {
    if (null != _currentClient) {
      _currentClient = null;
    }
  }

  private String wrapperAroundGetClientHost() {
    String clientHost = null;

    try {
      clientHost = getClientHost();
    } catch (ServerNotActiveException ignored) {
    }
    return clientHost;
  }

  private void checkForNegativeAmount(Money amount)
    throws NegativeAmountException {
    int cents = amount.getCents();

    if (0 > cents) {
      throw new NegativeAmountException();
    }
  }

  private void checkForOverdraft(Money amount)
    throws OverdraftException {
    if (amount.greaterThan(_balance)) {
      throw new OverdraftException(false);
    }
    return;
  }

  /** The expire thread */
  private class CountDownTimer implements Runnable {
    public void run() {
      while (true) {
        try {
          Thread.sleep(THREAD_SLEEP_TIME);
        } catch (Exception ignored) {
        }
        synchronized (Account3_Impl.this) {
          if (_timeLeftUntilLockIsReleased > 0) {
            _timeLeftUntilLockIsReleased -= THREAD_SLEEP_TIME;
          } else {
            releaseLock();
          }
        }
      }
    }
  }
} 


José M. Vidal .

35 of 49