craigmcc 01/06/02 16:13:40
Modified: src/share/org/apache/struts/action ActionServlet.java
src/share/org/apache/struts/util GenericDataSource.java
Log:
Port data source fix to HEAD branch.
Revision Changes Path
1.70 +15 -4
jakarta-struts/src/share/org/apache/struts/action/ActionServlet.java
Index: ActionServlet.java
===================================================================
RCS file:
/home/cvs/jakarta-struts/src/share/org/apache/struts/action/ActionServlet.java,v
retrieving revision 1.69
retrieving revision 1.70
diff -u -r1.69 -r1.70
--- ActionServlet.java 2001/05/18 17:11:37 1.69
+++ ActionServlet.java 2001/06/02 23:13:36 1.70
@@ -1,7 +1,7 @@
/*
- * $Header:
/home/cvs/jakarta-struts/src/share/org/apache/struts/action/ActionServlet.java,v 1.69
2001/05/18 17:11:37 mschachter Exp $
- * $Revision: 1.69 $
- * $Date: 2001/05/18 17:11:37 $
+ * $Header:
/home/cvs/jakarta-struts/src/share/org/apache/struts/action/ActionServlet.java,v 1.70
2001/06/02 23:13:36 craigmcc Exp $
+ * $Revision: 1.70 $
+ * $Date: 2001/06/02 23:13:36 $
*
* ====================================================================
*
@@ -88,6 +88,7 @@
import org.apache.struts.util.MessageResources;
import org.apache.struts.util.MessageResourcesFactory;
import org.apache.struts.util.RequestUtils;
+import org.apache.struts.util.ServletContextWriter;
import org.apache.struts.upload.MultipartRequestWrapper;
import org.xml.sax.AttributeList;
import org.xml.sax.SAXException;
@@ -229,7 +230,7 @@
* </ul>
*
* @author Craig R. McClanahan
- * @version $Revision: 1.69 $ $Date: 2001/05/18 17:11:37 $
+ * @version $Revision: 1.70 $ $Date: 2001/06/02 23:13:36 $
*/
public class ActionServlet
@@ -1054,11 +1055,21 @@
*/
protected void initDataSources() throws ServletException {
+ ServletContextWriter scw =
+ new ServletContextWriter(getServletContext());
+
synchronized (dataSources) {
Iterator keys = dataSources.keySet().iterator();
while (keys.hasNext()) {
String key = (String) keys.next();
DataSource dataSource = findDataSource(key);
+ try {
+ dataSource.setLogWriter(scw);
+ } catch (SQLException e) {
+ log(internal.getMessage("initDataSource", key), e);
+ throw new ServletException
+ (internal.getMessage("initDataSource", key), e);
+ }
if (dataSource instanceof GenericDataSource) {
if (debug >= 1)
log(internal.getMessage("dataSource.init", key));
1.6 +246 -27
jakarta-struts/src/share/org/apache/struts/util/GenericDataSource.java
Index: GenericDataSource.java
===================================================================
RCS file:
/home/cvs/jakarta-struts/src/share/org/apache/struts/util/GenericDataSource.java,v
retrieving revision 1.5
retrieving revision 1.6
diff -u -r1.5 -r1.6
--- GenericDataSource.java 2001/04/18 22:15:56 1.5
+++ GenericDataSource.java 2001/06/02 23:13:39 1.6
@@ -1,7 +1,7 @@
/*
- * $Header:
/home/cvs/jakarta-struts/src/share/org/apache/struts/util/GenericDataSource.java,v 1.5
2001/04/18 22:15:56 craigmcc Exp $
- * $Revision: 1.5 $
- * $Date: 2001/04/18 22:15:56 $
+ * $Header:
/home/cvs/jakarta-struts/src/share/org/apache/struts/util/GenericDataSource.java,v 1.6
2001/06/02 23:13:39 craigmcc Exp $
+ * $Revision: 1.6 $
+ * $Date: 2001/06/02 23:13:39 $
*
* ====================================================================
*
@@ -66,7 +66,9 @@
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.Driver;
+import java.sql.ResultSet;
import java.sql.SQLException;
+import java.sql.Statement;
import java.util.LinkedList;
import java.util.Properties;
import javax.sql.DataSource;
@@ -75,10 +77,8 @@
/**
* <p>Generic data source implementation of the <code>DataSource</code>
* interface. <b>WARNING</b> - This implementation does not know how to
- * provide connections with different username/password combinations. It
- * always returns connections based on the username and password configured
- * with <code>setUser()</code> and <code>setPassword()</code>,
- * respectively. Calling this version of the implementation using the
+ * provide connections with different username/password combinations.
+ * Calling this version of the implementation using the
* getConnection(username,password) signature will throw an exception.</p>
*
* <p>The following properties are supported by the standard
@@ -127,6 +127,26 @@
* the <code>user</code> property.</td>
* </tr>
* <tr>
+ * <td align="center">pingCommand</td>
+ * <td>A non-query SQL command that, if specified, will be executed before
+ * a connection is returned by a call to <code>getConnection()</code>.
+ * If any SQLException is thrown by the execution of this statement,
+ * it is assumed that this connection is stale and it will be discarded.
+ * Because this happens on every connection allocation, you should ensure
+ * that the statement executes very quickly.</td>
+ * </tr>
+ * <tr>
+ * <td align="center">pingQuery</td>
+ * <td>A query SQL command (i.e. a SELECT) that, if specified, will be
+ * executed before a connection is returned by a call to
+ * <code>getConnection()</code>. If any SQLException is thrown by the
+ * execution of this query (or by the subsequent processing of the
+ * entire returned <code>ResultSet</code>), it is assumed that this
+ * connection is stale and it will be discarded. Because this happens
+ * on every connection allocation, you should ensure that the
+ * statement executes very quickly.</td>
+ * </tr>
+ * <tr>
* <td align="center">readOnly</td>
* <td>Set to <code>true</code> if you want the connections returned to you
* by calling <code>getConnection()</code> to be configured for read only
@@ -158,7 +178,7 @@
*
* @author Craig R. McClanahan
* @author Ted Husted
- * @version $Revision: 1.5 $ $Date: 2001/04/18 22:15:56 $
+ * @version $Revision: 1.6 $ $Date: 2001/06/02 23:13:39 $
*/
public class GenericDataSource implements DataSource {
@@ -206,13 +226,6 @@
protected PrintWriter logWriter = null;
- /**
- * The connection properties for use in establishing connections.
- */
- protected Properties properties = new Properties();
-
-
-
// ------------------------------------------------------------- Properties
@@ -252,6 +265,20 @@
/**
+ * The debugging detail level for this data source.
+ */
+ protected int debug = 0;
+
+ public int getDebug() {
+ return (this.debug);
+ }
+
+ public void setDebug(int debug) {
+ this.debug = debug;
+ }
+
+
+ /**
* The description of this data source.
*/
protected String description = null;
@@ -322,7 +349,42 @@
}
+
+ /**
+ * The non-query SQL command used to ping an allocated connection.
+ */
+ protected String pingCommand = null;
+
+ public String getPingCommand() {
+ return (this.pingCommand);
+ }
+
+ public void setPingCommand(String pingCommand) {
+ this.pingCommand = pingCommand;
+ }
+
+
+ /**
+ * The query SQL command used to ping an allocated connection.
+ */
+ protected String pingQuery = null;
+
+ public String getPingQuery() {
+ return (this.pingQuery);
+ }
+
+ public void setPingQuery(String pingQuery) {
+ this.pingQuery = pingQuery;
+ }
+
+
/**
+ * The connection properties for use in establishing connections.
+ */
+ protected Properties properties = new Properties();
+
+
+ /**
* The default read-only state for newly created connections.
*/
protected boolean readOnly = false;
@@ -387,6 +449,8 @@
public Connection getConnection() throws SQLException {
int seconds = 0;
+ if (debug >= 2)
+ log(" getConnection()");
// Validate the opened status of this data source
if (closed)
@@ -397,31 +461,68 @@
while (true) {
// Have we timed out yet?
+ if (debug >= 3)
+ log(" Check for timeout, activeCount=" + activeCount +
+ ", useCount=" + useCount);
if ((loginTimeout > 0) && (seconds >= loginTimeout))
break;
// Return an existing connection from the pool if there is one
synchronized (connections) {
if (!connections.isEmpty()) {
- useCount++;
- GenericConnection connection = (GenericConnection)
connections.removeFirst();
- // unclose the connection's wrapper
+
+ // Allocate the first available connection
+ GenericConnection connection =
+ (GenericConnection) connections.removeFirst();
+ if (debug >= 3)
+ log(" Found available connection");
+
+ // Make sure this connection is not stale
connection.setClosed(false);
+ try {
+ ping(connection);
+ } catch (SQLException e) {
+ if (debug >= 3)
+ log(" Connection stale, releasing");
+ try {
+ connection.getConnection().close();
+ } catch (SQLException f) {
+ ;
+ }
+ activeCount--;
+ continue;
+ }
+
+ // unclose the connection's wrapper and return it
+ useCount++;
+ if (debug >= 3)
+ log(" Return allocated connection, activeCount=" +
+ activeCount + ", useCount=" + useCount);
return(connection);
- // return ((Connection) connections.removeFirst()); DEBUG
+
}
}
// Create a new connection if we are not yet at the maximum
if (activeCount < maxCount) {
- Connection conn = createConnection();
- if (conn != null) {
+ Connection connection = createConnection();
+ if (connection != null) {
+ try {
+ ping(connection);
+ } catch (SQLException e) {
+ throw e;
+ }
useCount++;
- return (conn);
+ if (debug >= 3)
+ log(" Return new connection, activeCount=" +
+ activeCount + ", useCount=" + useCount);
+ return (connection);
}
}
// Wait for an existing connection to be returned
+ if (debug >= 3)
+ log(" Sleep until next test");
try {
Thread.sleep(1000);
seconds++;
@@ -432,6 +533,8 @@
}
// We have timed out awaiting an available connection
+ if (debug >= 3)
+ log(" Timeout awaiting connection");
throw new SQLException
("getConnection: Timeout awaiting connection");
@@ -440,16 +543,14 @@
/**
* Attempt to establish a database connection. <b>WARNING</b> - The
- * specified username and password are ignored by this implementation.
+ * specified username and password are not supported by this
+ * implementation.
*
* @param username Database username for this connection
* @param password Database password for this connection
*
* @exception SQLException if a database access error occurs
*/
-
-
-
public Connection getConnection(String username, String password)
throws SQLException {
@@ -523,6 +624,8 @@
if (closed)
throw new SQLException("close: Data Source already closed");
+ if (debug >= 1)
+ log(" close()");
// Shut down all active connections
while (activeCount > 0) {
@@ -548,13 +651,15 @@
// Have we already been opened?
if (driver != null)
return;
+ if (debug >= 1)
+ log(" open()");
// Instantiate our database driver
try {
Class clazz = Class.forName(driverClass);
driver = (Driver) clazz.newInstance();
} catch (Throwable t) {
- throw new SQLException("createConnection: " + t);
+ throw new SQLException("open: " + t);
}
// Create the initial minimum number of required connections
@@ -621,15 +726,125 @@
protected synchronized Connection createConnection() throws SQLException {
if (activeCount < maxCount) {
+ if (debug >= 3)
+ log(" createConnection()");
Connection conn = driver.connect(url, properties);
activeCount++;
return (new GenericConnection(this, conn, autoCommit, readOnly));
}
+ if (debug >= 3)
+ log(" createConnection() returning null");
return (null);
}
+ /**
+ * Log the specified message to our log writer, if we have one.
+ *
+ * @param message The message to be logged
+ */
+ protected void log(String message) {
+
+ if (logWriter != null) {
+ logWriter.print("GenericDataSource[");
+ logWriter.print(description);
+ logWriter.print("]: ");
+ logWriter.println(message);
+ }
+
+ }
+
+
+ /**
+ * Log the specified message and exception to our log writer, if we
+ * have one.
+ *
+ * @param message The message to be logged
+ * @param throwable The exception to be logged
+ */
+ protected void log(String message, Throwable throwable) {
+
+ if (logWriter != null) {
+ logWriter.print("GenericDataSource[");
+ logWriter.print(description);
+ logWriter.print("]: ");
+ logWriter.println(message);
+ throwable.printStackTrace(logWriter);
+ }
+
+ }
+
+
+ /**
+ * Perform any configured <code>pingCommand</code> and/or
+ * <code>pingQuery</code> on the specified connection, returning any
+ * SQLException that is encountered along the way.
+ *
+ * @param conn The connection to be pinged
+ */
+ protected void ping(Connection conn) throws SQLException {
+
+ if (pingCommand != null) {
+
+ if (debug >= 4)
+ log(" ping(" + pingCommand + ")");
+
+ Statement stmt = conn.createStatement();
+ try {
+ stmt.execute(pingCommand);
+ stmt.close();
+ } catch (SQLException e) {
+ if (debug >= 5)
+ log(" ping() failed: " + e);
+ try {
+ if (stmt != null)
+ stmt.close();
+ } catch (SQLException f) {
+ ;
+ }
+ throw e;
+ }
+
+ }
+
+ if (pingQuery != null) {
+
+ if (debug >= 4)
+ log(" ping(" + pingQuery + ")");
+
+ ResultSet rs = null;
+ Statement stmt = conn.createStatement();
+ try {
+ rs = stmt.executeQuery(pingCommand);
+ while (rs.next()) {
+ ;
+ }
+ rs.close();
+ stmt.close();
+ } catch (SQLException e) {
+ if (debug >= 5)
+ log(" ping() failed: " + e);
+ try {
+ if (rs != null)
+ rs.close();
+ } catch (SQLException f) {
+ ;
+ }
+ try {
+ if (stmt != null)
+ stmt.close();
+ } catch (SQLException f) {
+ ;
+ }
+ throw e;
+ }
+
+ }
+
+ }
+
+
// -------------------------------------------------------- Package Methods
@@ -639,6 +854,10 @@
* @param conn The connection being returned
*/
void returnConnection(GenericConnection conn) {
+
+ if (debug >= 2)
+ log(" releaseConnection(), activeCount=" + activeCount +
+ ", useCount=" + (useCount - 1));
synchronized (connections) {
connections.addLast(conn);