<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3"/>
<classpathentry kind="var" path="TOMCAT_LIBS_BASE/tomcat6-deps/dbcp/tomcat-dbcp.jar" sourcepath="/TOMCAT_LIBS_BASE/tomcat6-deps/dbcp/src/java"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+ <classpathentry kind="lib" path="/development/tomcat/trunk/trunk/modules/jdbc-pool/includes/h2/bin/h2-1.1.115.jar"/>
<classpathentry kind="output" path="bin"/>
</classpath>
<li>Asynchronous connection retrieval - you can queue your request for a connection and receive a Future<Connection> back.</li>
<li>Better idle connection handling. Instead of closing connections directly, it can still pool connections and sizes the idle pool with a smarter algorithm.</li>
<li>You can decide at what moment connections are considered abandoned, is it when the pool is full, or directly at a timeout
- by specifying a threshold.
+ by specifying a pool usage threshold.
</li>
<li>The abandon connection timer will reset upon a statement/query activity. Allowing a connections that is in use for a long time to not timeout.
This is achieved using the ResetAbandonedTimer
</li>
<li>Close connections after they have been connected for a certain time. Age based close upon return to the pool.
- </li>
+ </li>
+ <li>Get JMX notifications and log entries when connections are suspected for being abandoned. This is similar to
+ the <code>removeAbandonedTimeout</code> but it doesn't take any action, only reports the information.
+ This is achieved using the <code>suspectTimeout</code> attribute.</li>
</ol>
</p>
The default value is <code>true</code>.
</p>
</attribute>
+ <attribute name="suspectTimeout" required="false">
+ <p>(int) Timeout value in seconds. Default value is <code>0</code>.<br/>
+ Similar to to the <code>removeAbandonedTimeout</code> value but instead of treating the connection
+ as abandoned, and potentially closing the connection, this simply logs the warning if
+ <code>logAbandoned</code> is set to true. If this value is equal or less than 0, no suspect
+ checking will be performed. Suspect checking only takes place if the timeout value is larger than 0 and
+ the connection was not abandoned or if abandon check is disabled. If a connection is suspect a WARN message gets
+ logged and a JMX notification gets sent once.
+ </p>
+ </attribute>
</attributes>
</subsection>
</section>
con.unlock();
}
}
+
+ /**
+ * thread safe way to abandon a connection
+ * signals a connection to be abandoned.
+ * this will disconnect the connection, and log the stack trace if logAbanded=true
+ * @param con PooledConnection
+ */
+ protected void suspect(PooledConnection con) {
+ if (con == null)
+ return;
+ if (con.isSuspect())
+ return;
+ try {
+ con.lock();
+ String trace = con.getStackTrace();
+ if (getPoolProperties().isLogAbandoned()) {
+ log.warn("Connection has been marked suspect, possibly abandoned " + con + "["+(System.currentTimeMillis()-con.getTimestamp())+" ms.]:" + trace);
+ }
+ if (jmxPool!=null) {
+ jmxPool.notify(org.apache.tomcat.jdbc.pool.jmx.ConnectionPool.SUSPECT_ABANDONED_NOTIFICATION, trace);
+ }
+ con.setSuspect(true);
+ } finally {
+ con.unlock();
+ }
+ }
/**
* thread safe way to release a connection
public void checkAbandoned() {
try {
if (busy.size()==0) return;
- if (!shouldAbandon()) return;
Iterator<PooledConnection> locked = busy.iterator();
+ int sto = getPoolProperties().getSuspectTimeout();
while (locked.hasNext()) {
PooledConnection con = locked.next();
boolean setToNull = false;
continue;
long time = con.getTimestamp();
long now = System.currentTimeMillis();
- if ((now - time) > con.getAbandonTimeout()) {
+ if (shouldAbandon() && (now - time) > con.getAbandonTimeout()) {
busy.remove(con);
abandon(con);
setToNull = true;
+ } else if (sto > 0 && (now - time) > (sto*1000)) {
+ suspect(con);
} else {
//do nothing
} //end if
}
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int getSuspectTimeout() {
+ return getPoolProperties().getSuspectTimeout();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setSuspectTimeout(int seconds) {
+ getPoolProperties().setSuspectTimeout(seconds);
+ }
+
//===============================================================================
// Expose JMX attributes through Tomcat's dynamic reflection
//===============================================================================
* @param useLock set to true if a lock should be used on connection operations
*/
public void setUseLock(boolean useLock);
+
+ /**
+ * Similar to {@link #setRemoveAbandonedTimeout(int)} but instead of treating the connection
+ * as abandoned, and potentially closing the connection, this simply logs the warning if
+ * {@link #isLogAbandoned()} returns true. If this value is equal or less than 0, no suspect
+ * checking will be performed. Suspect checking only takes place if the timeout value is larger than 0 and
+ * the connection was not abandoned or if abandon check is disabled. If a connection is suspect a WARN message gets
+ * logged and a JMX notification gets sent once.
+ * @param seconds - the amount of time in seconds that has to pass before a connection is marked suspect.
+ */
+ public void setSuspectTimeout(int seconds);
+
+ /**
+ * Returns the time in seconds to pass before a connection is marked an abanoned suspect.
+ * Any value lesser than or equal to 0 means the check is disabled.
+ * @return Returns the time in seconds to pass before a connection is marked an abanoned suspect.
+ */
+ public int getSuspectTimeout();
+
}
\ No newline at end of file
protected long maxAge = 0;
protected boolean useLock = false;
private InterceptorDefinition[] interceptors = null;
+ protected int suspectTimeout = 0;
+
/**
* {@inheritDoc}
return defaultReadOnly;
}
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int getSuspectTimeout() {
+ return this.suspectTimeout;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setSuspectTimeout(int seconds) {
+ this.suspectTimeout = seconds;
+ }
+
/**
* {@inheritDoc}
*/
@Override
public boolean isPoolSweeperEnabled() {
- boolean result = getTimeBetweenEvictionRunsMillis()>0;
- result = result && (isRemoveAbandoned() && getRemoveAbandonedTimeout()>0);
- result = result || (isTestWhileIdle() && getValidationQuery()!=null);
+ boolean timer = getTimeBetweenEvictionRunsMillis()>0;
+ boolean result = timer && (isRemoveAbandoned() && getRemoveAbandonedTimeout()>0);
+ result = result || (timer && getSuspectTimeout()>0);
+ result = result || (timer && isTestWhileIdle() && getValidationQuery()!=null);
return result;
}
private AtomicBoolean released = new AtomicBoolean(false);
+ private volatile boolean suspect = false;
+
/**
* Constructor
* @param prop - pool properties
*/
public void setTimestamp(long timestamp) {
this.timestamp = timestamp;
+ setSuspect(false);
+ }
+
+
+
+
+ public boolean isSuspect() {
+ return suspect;
+ }
+
+ public void setSuspect(boolean suspect) {
+ this.suspect = suspect;
}
/**
* Invoked when a query execution, a call to execute/executeQuery or executeBatch failed.
* @param query the query that was executed and failed
* @param args the arguments to the execution
- * @param name the name of the method used to execute {@link AbstractCreateStatementInterceptor#executes}
+ * @param name the name of the method used to execute {@link AbstractCreateStatementInterceptor#isExecute(Method, boolean)}
* @param start the time the query execution started
* @param t the exception that happened
* @return - the SQL that was executed or the string "batch" if it was a batch execution
* Invoked when a query execution, a call to execute/executeQuery or executeBatch succeeded and was within the timing threshold
* @param query the query that was executed and failed
* @param args the arguments to the execution
- * @param name the name of the method used to execute {@link AbstractCreateStatementInterceptor#executes}
+ * @param name the name of the method used to execute {@link AbstractCreateStatementInterceptor#isExecute(Method, boolean)}
* @param start the time the query execution started
* @param delta the time the execution took
* @return - the SQL that was executed or the string "batch" if it was a batch execution
* Invoked when a query execution, a call to execute/executeQuery or executeBatch succeeded and was exceeded the timing threshold
* @param query the query that was executed and failed
* @param args the arguments to the execution
- * @param name the name of the method used to execute {@link AbstractCreateStatementInterceptor#executes}
+ * @param name the name of the method used to execute {@link AbstractCreateStatementInterceptor#isExecute(Method, boolean)}
* @param start the time the query execution started
* @param delta the time the execution took
* @return - the SQL that was executed or the string "batch" if it was a batch execution
public static final String NOTIFY_ABANDON = "CONNECTION ABANDONED";
public static final String SLOW_QUERY_NOTIFICATION = "SLOW QUERY";
public static final String FAILED_QUERY_NOTIFICATION = "FAILED QUERY";
+ public static final String SUSPECT_ABANDONED_NOTIFICATION = "SUSPECT CONNETION ABANDONED";
}
public static MBeanNotificationInfo[] getDefaultNotificationInfo() {
- String[] types = new String[] {NOTIFY_INIT, NOTIFY_CONNECT, NOTIFY_ABANDON, SLOW_QUERY_NOTIFICATION, FAILED_QUERY_NOTIFICATION};
+ String[] types = new String[] {NOTIFY_INIT, NOTIFY_CONNECT, NOTIFY_ABANDON, SLOW_QUERY_NOTIFICATION, FAILED_QUERY_NOTIFICATION, SUSPECT_ABANDONED_NOTIFICATION};
String name = Notification.class.getName();
String description = "A connection pool error condition was met.";
MBeanNotificationInfo info = new MBeanNotificationInfo(types, name, description);
// TODO Auto-generated method stub
}
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int getSuspectTimeout() {
+ return getPoolProperties().getSuspectTimeout();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setSuspectTimeout(int seconds) {
+ //no op
+ }
}