/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 com.sun.jini.test.share;
import net.jini.core.lease.Lease;
import net.jini.core.lease.LeaseDeniedException;
import net.jini.core.lease.UnknownLeaseException;
import com.sun.jini.qa.harness.QAConfig;
/**
* Base class for owners that don't cause failures. Does basic management
* of the expiration and interaction with the test. Relies on
* abstract methods to validate extension and create/register leases.
* <p>
* Renews the lease when asked. Ensures that the lease has not expired
* and that each time it is renewed it has been asked for the correct
* duration.
*/
abstract public class TrackingOwner extends LeaseOwner {
/** String to be returned by <code>didPass</code> */
private String rslt = null;
/** Max time we are will to grant on a renewal request */
final private long maxExtension;
/** Desired expiration renewal service should be renewing towards */
final protected long desiredExpiration;
/** The max value we expect for each renewal request */
final protected long desiredRenewal;
/** Max acceptable variation from desiredExpiration for a renewal request */
final protected long slop;
/** Current expiration time of lease */
protected long expiration;
/** Time of last renewal */
private long lastRenewal;
/** Object to notify if we fail */
private Object notifyOnFailure;
/** Print stream to log messages to */
final protected QAConfig config;
/** In the context of a batch renew call the current time */
protected long now;
/**
* Simple constructor
* @param initialExpiration Initial expiration time for lease.
* @param maxExtension Maximum time this owner will be willing to extend
* the lease
* @param desiredExpiration Every lease a renewal service is renewing
* has a "membership expiration" associated with it. The
* renewal service should all-ways pick a renewal duration
* that when added to the current time equals this membership
* expiration time.
* @param slop Allowable variance from desired expiration when making a
* renewal request.
* @param desiredRenewal
* Expect value of the renewDuration parameter
* @param notifyOnFailure
* Object to notify if there is an early failure
* @param config QAConfig object
*/
public TrackingOwner(long initialExpiration, long maxExtension,
long desiredExpiration, long slop,
long desiredRenewal, Object notifyOnFailure,
QAConfig config)
{
this.desiredRenewal = desiredRenewal;
this.maxExtension = maxExtension;
this.desiredExpiration = desiredExpiration;
this.slop = slop;
expiration = initialExpiration;
this.notifyOnFailure = notifyOnFailure;
this.config = config;
}
/**
* Return true if the lease has not expired
*/
private boolean ensureCurrent(long now) {
return now < expiration;
}
/**
* Return null if no error has been detected, and a descriptive
* string otherwise. Checks rslt field to see if an error has
* been detected (subclasses should use
* <code>setRsltIfNeeded</code> to flag an error. Default
* implementation also makes sure that the lease expired close to
* the desired expiration, or that the lease's desired expiration
* has not been reached and the lease has not yet expired.
*/
public synchronized String didPass() {
// Has an error already been flaged?
if (rslt != null)
return rslt;
// Make sure the lease has not expired (unless it should have)
final long now = System.currentTimeMillis();
final long diff = Math.abs(desiredExpiration - expiration);
if (!ensureCurrent(now) && diff > slop)
rslt = "Lease expired";
return rslt;
}
/**
* Flag an error
*/
protected synchronized void setRsltIfNeeded(String newResult) {
if (rslt == null) {
rslt = newResult;
}
}
/**
* Return the contets of the rslt field
*/
protected synchronized String getRslt() {
return rslt;
}
/**
* Return true if the passed extension is valid for this lease.
* Called from batchRenew() so the value of the <code>now</code>
* field is valid. If the extension is invalid should return
* false and set rslt
*/
protected abstract boolean isValidExtension(long extension);
private synchronized long batchRenewSync(long extension)
throws UnknownLeaseException, LeaseDeniedException
{
now = System.currentTimeMillis();
lastRenewal = now;
// Has the lease expired?
if (!ensureCurrent(now)) {
setRsltIfNeeded("Renwal service let lease expire, " +
(now - expiration) + " ms late");
throw new UnknownLeaseException("Lease expired");
}
// Have the asked for the right renewal?
if (!isValidExtension(extension))
throw new LeaseDeniedException("Did not ask for right extension");
// Seem to ask for the right extension
// Calculate the renewal grant __DURATION__
long grant;
if (extension == Lease.ANY) {
grant = maxExtension;
} else {
grant = Math.min(maxExtension, extension);
}
expiration = now + grant;
if (expiration < 0) {
// Overflow
expiration = Long.MAX_VALUE;
grant = Long.MAX_VALUE - now;
}
return grant;
}
// Inherit java doc from super type
public long batchRenew(long extension)
throws UnknownLeaseException, LeaseDeniedException
{
try {
return batchRenewSync(extension);
} finally {
// If after all this rslt is non null we have to notify
// notifyOnFailure
if (rslt != null && notifyOnFailure != null) {
synchronized (notifyOnFailure) {
notifyOnFailure.notifyAll();
}
}
}
}
/**
* Return the lease desired duration we are shooting for
*/
long getDesiredDuration() {
if (desiredExpiration == Lease.FOREVER)
return Lease.FOREVER;
else
return desiredExpiration - System.currentTimeMillis();
}
/**
* Return true if the lease associated with this owner should
* be registered using the two arg form of <code>renewFor</code>
*/
abstract boolean isTwoArg();
/**
* Return the desired renewal request
*/
long getDesiredRenewal() {
return desiredRenewal;
}
// Inherit java doc from super type
public Object renew(long extension)
throws UnknownLeaseException, LeaseDeniedException
{
return new Long(batchRenew(extension));
}
// Inherit java doc from super type
public void batchCancel() {
setRsltIfNeeded("Renewal Service canceld lease!");
}
// Inherit java doc from super type
public Throwable cancel() {
batchCancel();
return null;
}
/**
* Returns the expiration time of the lease
*/
public synchronized long getExpiration() {
return expiration;
}
/**
* Returns the time of the last renewal call
*/
public synchronized long getLastRenewTime() {
return lastRenewal;
}
}