/*
* LeaseCleaner.java May 2004
*
* Copyright (C) 2004, Niall Gallagher <niallg@users.sf.net>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
package org.simpleframework.util.lease;
import static java.util.concurrent.TimeUnit.NANOSECONDS;
import org.simpleframework.util.thread.Daemon;
/**
* The <code>LeaseCleaner</code> provides a means of providing callbacks to
* clean a leased resource once the contract duration has expired. This will
* acquire contracts from the queue and invoke the <code>Cleaner</code>
* notification method. This will wait until the current clean operation has
* completed before it attempts to clean the next contract.
*
* @author Niall Gallagher
*/
class LeaseCleaner<T> extends Daemon {
/**
* This is used to queue contracts that are to be cleaned.
*/
private ContractQueue<T> queue;
/**
* This is the cleaner that is invoked to clean contracts.
*/
private Cleaner<T> cleaner;
/**
* This is used to determine if the invoker should stop.
*/
private volatile boolean dead;
/**
* Constructor for the <code>LeaseCleaner</code> object. This can be used to
* issue, update, and expire leases. When a lease expires notification is
* sent to the <code>Cleaner</code> object provided. This allows an
* implementation independent means to clean up once a specific lease has
* expired.
*
* @param cleaner
* this will receive expiration notifications
*/
public LeaseCleaner(Cleaner<T> cleaner) {
this.queue = new ContractQueue<T>();
this.cleaner = cleaner;
this.start();
}
/**
* This revokes a contract that has previously been issued. This is used
* when the contract duration has changed so that it can be reissued again
* with a new duration. This returns true if the contract was still active
* and false if it did not exist.
*
* @param contract
* this is the contract that contains details
*/
public boolean revoke(Contract<T> contract) throws LeaseException {
if (this.dead) throw new LeaseException("Lease can not be revoked");
return this.queue.remove(contract);
}
/**
* This method will establish a contract for a given resource. If the
* contract duration expires before it is renewed then a notification is
* sent, to the issued <code>Cleaner</code> implementation, to signify that
* the resource has expired.
*
* @param contract
* this is the contract that contains details
*/
public boolean issue(Contract<T> contract) throws LeaseException {
if (this.dead) throw new LeaseException("Lease can not be issued");
return this.queue.offer(contract);
}
/**
* This acquires expired lease contracts from the queue once the expiry
* duration has passed. This will deliver notification to the
* <code>Cleaner</code> object once the contract has been taken from the
* queue. This allows the cleaner to clean up any resources associated with
* the lease before the next expiration.
*/
@Override
public void run() {
while (!this.dead) {
try {
this.clean();
} catch (Throwable e) {
continue;
}
}
this.purge();
}
/**
* This method is used to take the lease from the queue and give it to the
* cleaner for expiry. This effectively waits until the next contract expiry
* has passed, once it has passed the key for that contract is given to the
* cleaner to clean up resources.
*/
private void clean() throws Exception {
Contract<T> next = this.queue.take();
T key = next.getKey();
if (key != null) {
this.cleaner.clean(key);
}
}
/**
* Here all of the existing contracts are purged when the invoker is closed.
* This ensures that each leased resource has a chance to clean up after the
* lease manager has been closed. All of the contracts are given a zero
* delay and cleaned immediately such that once this method has finished the
* queue will be empty.
*/
private void purge() {
for (Contract<T> next : this.queue) {
T key = next.getKey();
try {
next.setDelay(0L, NANOSECONDS);
this.cleaner.clean(key);
} catch (Throwable e) {
continue;
}
}
}
/**
* Here we shutdown the lease maintainer so that the thread will die.
* Shutting down the maintainer is done by interrupting the thread and
* setting the dead flag to true. Once this is invoked then the thread will
* no longer be running for this object.
*/
public void close() {
this.dead = true;
this.interrupt();
}
}