r10000 - in helma/helma/trunk/src/helma: framework/core main objectmodel/db

[email protected] Mon, 30 Nov 2009 11:58:34 +0100 (CET)
Newsgroups gmane.comp.java.helma.cvs
Message-ID <20091130105834.4CE983D0E2@mia>
Author: hannes
Date: 2009-11-30 11:58:34 +0100 (Mon, 30 Nov 2009)
New Revision: 10000

Added:
   helma/helma/trunk/src/helma/objectmodel/db/DbConnection.java
Modified:
   helma/helma/trunk/src/helma/framework/core/Application.java
   helma/helma/trunk/src/helma/main/Server.java
   helma/helma/trunk/src/helma/objectmodel/db/DbSource.java
   helma/helma/trunk/src/helma/objectmodel/db/Transactor.java
Log:
Refactor db connection pooling: Use connection names instead of connection properties as has keys, introduce new DbConnection wrapper class and a serial-id flag in DbSource to validate connections.

Details at http://dev.helma.org/trac/helma/changeset/10000

Modified: helma/helma/trunk/src/helma/framework/core/Application.java
===================================================================
--- helma/helma/trunk/src/helma/framework/core/Application.java	2009-11-27 15:17:33 UTC (rev 9999)
+++ helma/helma/trunk/src/helma/framework/core/Application.java	2009-11-30 10:58:34 UTC (rev 10000)
@@ -1527,6 +1527,13 @@
     }
 
     /**
+     * Log a generic application debug message
+     */
+    public void logDebug(String msg) {
+        getEventLog().debug(msg);
+    }
+
+    /**
      * Log an application access
      */
     public void logAccess(String msg) {

Modified: helma/helma/trunk/src/helma/main/Server.java
===================================================================
--- helma/helma/trunk/src/helma/main/Server.java	2009-11-27 15:17:33 UTC (rev 9999)
+++ helma/helma/trunk/src/helma/main/Server.java	2009-11-30 10:58:34 UTC (rev 10000)
@@ -417,7 +417,6 @@
         dbProps = new ResourceProperties();
         dbProps.setIgnoreCase(false);
         dbProps.addResource(new FileResource(file));
-        DbSource.setDefaultProps(dbProps);
 
         // read apps.properties file
         String appsPropfile = sysProps.getProperty("appsPropFile");

Added: helma/helma/trunk/src/helma/objectmodel/db/DbConnection.java

Modified: helma/helma/trunk/src/helma/objectmodel/db/DbSource.java
===================================================================
--- helma/helma/trunk/src/helma/objectmodel/db/DbSource.java	2009-11-27 15:17:33 UTC (rev 9999)
+++ helma/helma/trunk/src/helma/objectmodel/db/DbSource.java	2009-11-30 10:58:34 UTC (rev 10000)
@@ -27,20 +27,18 @@
 import java.util.Hashtable;
 
 /**
- *  This class describes a releational data source (URL, driver, user and password).
+ *  This class describes a relational data source (URL, driver, user and password).
  */
 public class DbSource {
-    private static ResourceProperties defaultProps = null;
-    private Properties conProps;
     private String name;
-    private ResourceProperties props, subProps;
+    private int serialId = 0;
+    private ResourceProperties props;
     protected String url;
     private String driver;
+    private Properties conProps;
     private boolean isOracle, isMySQL, isPostgreSQL, isH2;
     private long lastRead = 0L;
     private Hashtable dbmappings = new Hashtable();
-    // compute hashcode statically because it's expensive and we need it often
-    private int hashcode;
     // thread local connection holder for non-transactor threads
     private ThreadLocal connection;
 
@@ -68,56 +66,47 @@
      */
     public synchronized Connection getConnection()
             throws ClassNotFoundException, SQLException {
-        Connection con;
+        DbConnection con;
         Transactor tx = Transactor.getInstance();
+        if (props.lastModified() != lastRead) {
+            init();
+        }
         if (tx != null) {
-            con = tx.getConnection(this);
+            con = tx.getDbConnection(name, serialId);
         } else {
-            con = getThreadLocalConnection();
+            con = getThreadLocalDbConnection();
         }
 
-        boolean fileUpdated = props.lastModified() > lastRead ||
-                (defaultProps != null && defaultProps.lastModified() > lastRead);
+        if (con == null) {
+            con = new DbConnection(DriverManager.getConnection(url, conProps), serialId);
 
-        if (con == null || con.isClosed() || fileUpdated) {
-            init();
-            con = DriverManager.getConnection(url, conProps);
-
             // If we wanted to use SQL transactions, we'd set autoCommit to
             // false here and make commit/rollback invocations in Transactor methods;
             // System.err.println ("Created new Connection to "+url);
             if (tx != null) {
-                tx.registerConnection(this, con);
+                tx.registerConnection(name, con);
             } else {
                 connection.set(con);
             }
         }
 
-        return con;
+        return con.getConnection();
     }
 
     /**
      * Used for connections not managed by a Helma transactor
      * @return a thread local tested connection, or null
      */
-    private Connection getThreadLocalConnection() {
+    private DbConnection getThreadLocalDbConnection() {
         if (connection == null) {
             connection = new ThreadLocal();
             return null;
         }
-        Connection con = (Connection) connection.get();
-        if (con != null) {
-            // test if connection is still ok
-            try {
-                Statement stmt = con.createStatement();
-                stmt.execute("SELECT 1");
-                stmt.close();
-            } catch (SQLException sx) {
-                try {
-                    con.close();
-                } catch (SQLException ignore) {/* nothing to do */}
-                return null;
-            }
+        DbConnection con = (DbConnection) connection.get();
+        if (con != null && !con.isValid(serialId)) {            
+            con.close();
+            connection.remove();
+            return null;
         }
         return con;
     }
@@ -142,13 +131,10 @@
      * @throws ClassNotFoundException if the JDBC driver couldn't be loaded
      */
     private synchronized void init() throws ClassNotFoundException {
-        lastRead = (defaultProps == null) ? props.lastModified()
-                                          : Math.max(props.lastModified(),
-                                                     defaultProps.lastModified());
+        lastRead = props.lastModified();
+        serialId ++;
         // refresh sub-properties for this DbSource
-        subProps = props.getSubProperties(name + '.');
-        // use properties hashcode for ourselves
-        hashcode = subProps.hashCode();
+        ResourceProperties subProps = props.getSubProperties(name + '.');
         // get JDBC URL and driver class name
         url = subProps.getProperty("url");
         driver = subProps.getProperty("driver");
@@ -213,15 +199,6 @@
     }
 
     /**
-     * Set the default (server-wide) properties
-     *
-     * @param props server default db.properties
-     */
-    public static void setDefaultProps(ResourceProperties props) {
-        defaultProps = props;
-    }
-
-    /**
      * Check if this DbSource represents an Oracle database
      *
      * @return true if we're using an oracle JDBC driver
@@ -278,17 +255,4 @@
         return (DbMapping) dbmappings.get(tablename.toUpperCase());
     }
 
-    /**
-     * Returns a hash code value for the object.
-     */
-    public int hashCode() {
-        return hashcode;
-    }
-
-    /**
-     * Indicates whether some other object is "equal to" this one.
-     */
-    public boolean equals(Object obj) {
-        return obj instanceof DbSource && subProps.equals(((DbSource) obj).subProps);
-    }
 }

Modified: helma/helma/trunk/src/helma/objectmodel/db/Transactor.java
===================================================================
--- helma/helma/trunk/src/helma/objectmodel/db/Transactor.java	2009-11-27 15:17:33 UTC (rev 9999)
+++ helma/helma/trunk/src/helma/objectmodel/db/Transactor.java	2009-11-30 10:58:34 UTC (rev 10000)
@@ -234,37 +234,38 @@
 
     /**
      * Register a db connection with this transactor thread.
-     * @param src the db source
+     * @param name the db source name
      * @param con the connection
      */
-    public void registerConnection(DbSource src, Connection con) {
-        sqlConnections.put(src, con);
+    public void registerConnection(String name, DbConnection con) {
+        DbConnection previous = (DbConnection) sqlConnections.put(name, con);
+        if (previous != null) {
+            nmgr.app.logEvent("Closing previous connection " + con);
+            previous.close();
+        }
         // we assume a freshly created connection is ok.
-        testedConnections.put(src, new Long(System.currentTimeMillis()));
+        testedConnections.put(name, new Long(System.currentTimeMillis()));
     }
 
     /**
      * Get a db connection that was previously registered with this transactor thread.
-     * @param src the db source
-     * @return the connection
+     * @param name the db source name
+     * @param serialId the current serial id of the db source definition, used for validation
+     * @return the connection, or null if no valid connection is available
      */
-    public Connection getConnection(DbSource src) {
-        Connection con = (Connection) sqlConnections.get(src);
-        Long tested = (Long) testedConnections.get(src);
+    public DbConnection getDbConnection(String name, int serialId) {
+        DbConnection con = (DbConnection) sqlConnections.get(name);
+        Long tested = (Long) testedConnections.get(name);
         long now = System.currentTimeMillis();
-        if (con != null && (tested == null || now - tested.longValue() > 60000)) {
-            // Check if the connection is still alive by executing a simple statement.
-            try {
-                Statement stmt = con.createStatement();
-                stmt.execute("SELECT 1");
-                stmt.close();
-                testedConnections.put(src, new Long(now));
-            } catch (SQLException sx) {
-                try {
-                    con.close();
-                } catch (SQLException ignore) {/* nothing to do */}
-                return null;
-            }
+        // Check if the connection is still valid
+        if (con != null
+                && (tested == null || now - tested.longValue() > 60000) 
+                && !con.isValid(serialId)) {
+            nmgr.app.logEvent("Closing cached connection " + con);
+            sqlConnections.remove(name);
+            testedConnections.remove(name);
+            con.close();
+            return null;
         }
         return con;
     }
@@ -529,12 +530,11 @@
         if (sqlConnections != null) {
             for (Iterator i = sqlConnections.values().iterator(); i.hasNext();) {
                 try {
-                    Connection con = (Connection) i.next();
-
+                    DbConnection con = (DbConnection) i.next();
                     con.close();
                     nmgr.app.logEvent("Closing DB connection: " + con);
-                } catch (Exception ignore) {
-                    // exception closing db connection, ignore
+                } catch (Exception x) {
+                    nmgr.app.logEvent("Error closing DB connection: " + x);
                 }
             }