// Copyright (c) 2001 Dustin Sallings <dustin@spy.net>
package net.spy.util;
import java.text.MessageFormat;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.TimerTask;
import net.spy.log.Logger;
import net.spy.log.LoggerFactory;
/**
* A TTL object is used to express an intent for a process to finish within
* a certain amount of time.
*
* <p>
* A TTL object must be registered with a {@link TTLMonitor} before it
* will report. The default implementation of report logs via
* {@link Logger}.
* </p>
*/
public class TTL extends TimerTask {
private static final int DEFAULT_REPORT_INTERVAL=300000;
private static final int DEFAULT_N_REPORTS=10;
private final long ttl;
private final Expired e;
private long startTime;
private Object extraInfo;
private boolean isClosed=false;
private boolean fired=false;
private int nReports=0;
private int reportInterval=DEFAULT_REPORT_INTERVAL;
private int maxNReports=DEFAULT_N_REPORTS;
private transient Logger logger=null;
/**
* Get an instance of TTL.
* @param theTTL Number of milliseconds until the TTL fires
*/
public TTL(long theTTL) {
this(theTTL, null);
}
/**
* Get an instance of TTL with the given ttl and extra object.
* @param theTTL Number of milliseconds until the TTL fires
* @param info Extra info that will be toString()ed in the log
*/
public TTL(long theTTL, Object info) {
ttl=theTTL;
extraInfo=info;
reset();
e=new Expired();
}
/**
* Get the logger for this TTL.
*/
protected Logger getLogger() {
if(logger==null) {
logger=LoggerFactory.getLogger(getClass());
}
return(logger);
}
/**
* Resets the counter by setting the time that the TTL started to
* <i>right now</i>.
*/
public void reset() {
this.startTime=System.currentTimeMillis();
}
/**
* String me.
*/
@Override
public String toString() {
return("TTL: " + ttl + "@" + System.identityHashCode(this));
}
/**
* Get the number of milliseconds this TTL object is expected to be in
* use.
*/
public long getTTL() {
return(ttl);
}
/**
* Set the minimum interval at which doReport() should be called when
* TTLMonitor sees this object as expired.
*/
public void setReportInterval(int ms) {
this.reportInterval=ms;
}
/**
* Get the report interval.
*/
public int getReportInterval() {
return(reportInterval);
}
/**
* Set the maximum number of reports this TTL should issue before
* automatically closing.
*/
public void setMaxReports(int to) {
maxNReports=to;
}
/**
* True if the TTL object has reported.
*/
public synchronized boolean hasReported() {
return(nReports>0);
}
/**
* Provide extra information for the TTL report.
*/
public void setExtraInfo(Object o) {
this.extraInfo=o;
}
/**
* Get the extra info provided for the TTL report.
*/
public Object getExtraInfo() {
return(extraInfo);
}
/**
* Calling this method states that we are no longer interested in the
* progress of this TTL.
*/
public synchronized void close() {
isClosed=true;
cancel();
}
/**
* Return true if this TTL is no longer interesting.
*/
public synchronized boolean isClosed() {
return(isClosed);
}
/**
* Ask a TTL if it's expired.
*
* @return true if the TTL is expired
*/
public synchronized boolean isExpired() {
return(fired);
}
/**
* Report a TTL expiration with the given format.
*
* <ul>
* <li> 0 - Time the TTL object has been open.</li>
* <li> 1 - Time the TTL was expected to be running.</li>
* <li> 2 - Extra info passed in (may be null).</li>
* </ul>
*
* @param msg message format string to print when the TTL expires
*/
protected void reportWithFormat(String msg) {
long now=System.currentTimeMillis();
Object[] args={new Long(now-startTime), new Long(ttl), extraInfo};
MessageFormat mf=new MessageFormat(msg);
String toLog=mf.format(args);
getLogger().warn(toLog, e);
}
/**
* Get the message format string from the named bundle.
*
* @param bundleName the name of the bundle from which to get the messages
* @param msgNoArg the key in the bundle to use when there's no extra info
* @param msgWithArg the key in the bundle to use when there's extra info
*
* @return the format string
*/
protected String getMessageFromBundle(String bundleName,
String msgNoArg, String msgWithArg) {
String rv=null;
ResourceBundle rb=null;
try {
rb=ResourceBundle.getBundle(bundleName);
} catch(MissingResourceException ex) {
rv="ResourceBundle not found while reporting TTL expiration: "
+ bundleName + ". (Expected {1}ms, been {0}ms).";
}
if(rb!=null) {
try {
if(extraInfo==null) {
rv=rb.getString(msgNoArg);
} else {
rv=rb.getString(msgWithArg);
}
} catch(MissingResourceException ex) {
rv="Resource not found while reporting TTL expiration: "
+ ex.getKey() + ". (Expected {1}ms, been {0}ms).";
}
}
return (rv);
}
/**
* Called when an object's TTL has expired without closing.
*/
protected void doReport() {
// Get the message.
String msg=getMessageFromBundle("net.spy.util.messages",
"ttl.msg", "ttl.msg.witharg");
reportWithFormat(msg);
}
/**
* Request a report of the TTL.
*
* This is called whenever the TTLMonitor owning this object notices
* the object's TTL has expired.
*/
@Override
public final synchronized void run() {
fired=true;
// Call the actual report routine
doReport();
// If we've reported enough times, give up.
if(nReports++ >= maxNReports) {
close();
}
}
// This does not extend Exception because it exists primarily for the
// message it prints.
private static class Expired extends Exception {
public Expired() {
super("Timer has expired.");
}
}
}