package net.spy.pool;
import java.util.Date;
import net.spy.SpyObject;
/**
* PoolAble is the object container that is used to store objects in the
* pool.
*
* <p>
*
* The system property net.spy.pool.debug can be set to a file where the
* debugging output will go.
*/
public abstract class PoolAble extends SpyObject {
private static final int TOSTRING_LEN=128;
private int objectId=-1;
private boolean checkedOut=false;
private Object theObject=null;
private long maxAge=0;
private long startTime=0;
private String poolName=null;
private int checkouts=0;
private int checkins=0;
private int poolHash=0;
/**
* Minimum value returned from pruneStatus() if we may clean the object.
*/
public static final int MAY_CLEAN=1;
/**
* Minimum value returned from pruneStatus() if we must clean the object.
*/
public static final int MUST_CLEAN=2;
private boolean available=true;
/**
* Get a PoolAble representation for an object.
*/
public PoolAble(Object o, int h) {
super(); // thanks for asking.
this.theObject=o;
this.poolHash=h;
startTime=System.currentTimeMillis();
debug("New object");
}
/**
* Get a PoolAble representation for an object, including a given
* maximum lifetime the object can have.
*
* @param a the amount of time, in milliseconds, that the object
* will be valid. Objects will not be checked out if they are older
* than their maximum lifetime.
*/
public PoolAble(Object o, long a, int h) {
super(); // thanks for asking.
this.theObject=o;
this.maxAge=a;
this.poolHash=h;
startTime=System.currentTimeMillis();
debug("New object.");
}
// Get the debug name
private String debugName() {
return("PoolAble " + objectId + " for "
+ Integer.toHexString(poolHash));
}
/**
* Set the maximum age of this PoolAble.
*/
public synchronized void setMaxAge(long to) {
this.maxAge=to;
}
/**
* Find out of the PoolAble represents a usable object. Objects
* extending PoolAble should implement isAlive() methods for their
* particular needs.
* <p>
* Objects implementing isAlive() <i>should</i> turn off object
* availability if they determine the object no longer isAlive().
*
* @return true if the object will be usable
*/
public synchronized boolean isAlive() {
return(true);
}
/**
* Get the object we're pooling.
*
* @return the object.
*
* @exception PoolException if something bad happens (i.e. the object is
* not checked out)
*/
public synchronized Object getObject() throws PoolException {
if(!checkedOut) {
throw new PoolException("This PoolAble has not been checked out.");
}
return(theObject);
}
/**
* Internal version of getObject(). Returns regardless of whether the
* object has been checked out.
*/
protected Object intGetObject() {
return(theObject);
}
/**
* Set the internal object ID. This probably shouldn't be called
* outside of the pool container.
*
* @param id ObjectId of this object.
*/
public void setObjectID(int id) {
this.objectId=id;
}
/**
* Set the pool name this thing sits in. This is for debugging, but
* it's useful information, nonetheless.
*/
public void setPoolName(String to) {
this.poolName=to;
}
/**
* Get the object ID of this object.
*
* @return the object ID
*/
public int getObjectID() {
return(objectId);
}
/**
* Activate this PoolAble object.
*/
public void activate() {
debug("Activated.");
}
/**
* Check the object back in. The PoolAble will not be usable again
* until it's checked back out from the pooler.
* <p>
* checkIn also does some checks such as making sure the item is not
* too old, and that it is still alive.
*/
public synchronized void checkIn() {
checkedOut=false;
checkins++;
// At this point, set the availability based on whether this object
// is expired.
available=!isExpired();
// Also, make sure the thing's alive.
if(!isAlive()) {
available=false;
}
debug("Checked in.");
}
/**
* Mark this object as available.
*/
protected synchronized void setAvailable() {
available=true;
}
/**
* Mark this object as unavailable.
*/
protected synchronized void setUnavailable() {
available=false;
}
// Returns true if this should be invalidated based on the time
private boolean isExpired() {
boolean rv=true;
if(maxAge==0) {
rv=false;
} else {
long currentTime=System.currentTimeMillis();
if(currentTime-startTime < maxAge) {
rv=false;
}
}
return(rv);
}
/**
* Check the object out. Called from the PoolContainer
*/
public synchronized void checkOut() {
checkouts++;
checkedOut=true;
available=false;
debug("Checked out.");
}
/**
* Find out if the object is checked out.
*/
public synchronized boolean isCheckedOut() {
return(checkedOut);
}
/**
* Find out if the object is available for a requestor
*/
public synchronized boolean isAvailable() {
// If it currently believes it's available, but it's expired, make
// it unavailable.
if(available) {
if(isExpired()) {
available=false;
}
}
return(available);
}
/**
* Find out if an object is prunable
*
* @return 0 if not available, 1 if we may clean, greater than one if
* we must clean.
*/
public synchronized int pruneStatus() {
int ret=0;
// If it's not checked out, we can prune it.
if(!checkedOut) {
ret++;
// If it's not checked out, and it's not available, we *need*
// to prune it.
if(!isAvailable()) {
ret++;
}
// If it's not alive, we don't want it.
if(!isAlive()) {
ret++;
}
}
return(ret);
}
/**
* Discard the object. Anything that extends from this class needs to
* call super.discard() when it's done.
*/
public void discard() {
debug("Discard called.");
available=false;
theObject=null;
}
/**
* Debugging info.
*/
protected final void debug(String msg) {
if(getLogger().isDebugEnabled()) {
String classname=getClass().getName();
String objectClassname="n/a";
if(theObject!=null) {
objectClassname=theObject.getClass().getName();
}
String tmsg= "Poolable=" + classname + ", oid=" + objectId
+ " in " + poolName + ", object=" + objectClassname
+ ": " + msg;
getLogger().debug(tmsg);
}
}
/**
* Return a string representation of this object.
*
* @return a string representation of this object.
*/
@Override
public synchronized String toString() {
StringBuilder out=new StringBuilder(TOSTRING_LEN);
out.append(debugName());
if(isCheckedOut()) {
out.append(" is checked out");
} else {
out.append(" is not checked out");
}
out.append(" (o=" + checkouts + ", i=" + checkins + ")");
if(maxAge>0) {
out.append(" expires " + new Date(startTime + maxAge));
}
if(!isAvailable()) {
out.append(" (not available)");
}
return(out.toString());
}
}