You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by th...@apache.org on 2007/09/14 18:04:20 UTC

svn commit: r575734 - in /jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle: ./ util/

Author: thomasm
Date: Fri Sep 14 09:04:19 2007
New Revision: 575734

URL: http://svn.apache.org/viewvc?rev=575734&view=rev
Log:
JCR-940: BundleDbPersistenceManager auto re-connect

Added:
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/ConnectionRecoveryManager.java   (with props)
Modified:
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/BundleDbPersistenceManager.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/DerbyPersistenceManager.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/H2PersistenceManager.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/Oracle9PersistenceManager.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/OraclePersistenceManager.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/PostgreSQLPersistenceManager.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/DbNameIndex.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/NGKDbNameIndex.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/PostgreSQLNameIndex.java

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/BundleDbPersistenceManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/BundleDbPersistenceManager.java?rev=575734&r1=575733&r2=575734&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/BundleDbPersistenceManager.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/BundleDbPersistenceManager.java Fri Sep 14 09:04:19 2007
@@ -25,6 +25,7 @@
 import org.apache.jackrabbit.core.state.NodeReferencesId;
 import org.apache.jackrabbit.core.state.NodeReferences;
 import org.apache.jackrabbit.core.persistence.PMContext;
+import org.apache.jackrabbit.core.persistence.bundle.util.ConnectionRecoveryManager;
 import org.apache.jackrabbit.core.persistence.bundle.util.DbNameIndex;
 import org.apache.jackrabbit.core.persistence.bundle.util.NodePropBundle;
 import org.apache.jackrabbit.core.persistence.bundle.util.BundleBinding;
@@ -53,15 +54,14 @@
 import java.sql.Blob;
 import java.sql.Connection;
 import java.sql.DatabaseMetaData;
-import java.sql.DriverManager;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.sql.Statement;
-import java.sql.Driver;
-import java.util.Iterator;
-import java.util.Collection;
 import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
 
 import javax.jcr.RepositoryException;
 
@@ -82,6 +82,7 @@
  * <li>&lt;param name="{@link #setSchema(String) schema}" value=""/>
  * <li>&lt;param name="{@link #setSchemaObjectPrefix(String) schemaObjectPrefix}" value=""/>
  * <li>&lt;param name="{@link #setErrorHandling(String) errorHandling}" value=""/>
+ * <li>&lt;param name="{@link #setBlockOnConnectionLoss(String) blockOnConnectionLoss}" value="false"/>
  * </ul>
  */
 public class BundleDbPersistenceManager extends AbstractBundlePersistenceManager {
@@ -135,21 +136,25 @@
     /** inidicates if uses (filesystem) blob store */
     protected boolean externalBLOBs;
 
+    /** indicates whether to block if the database connection is lost */
+    protected boolean blockOnConnectionLoss = false;
+
+    /**
+     * The class that manages statement execution and recovery from connection loss.
+     */
+    protected ConnectionRecoveryManager connectionManager;
 
-    /** jdbc conection */
-    protected Connection con;
+    // SQL statements for bundle management
+    protected String bundleInsertSQL;
+    protected String bundleUpdateSQL;
+    protected String bundleSelectSQL;
+    protected String bundleDeleteSQL;
 
-    // shared prepared statements for bundle management
-    protected PreparedStatement bundleInsert;
-    protected PreparedStatement bundleUpdate;
-    protected PreparedStatement bundleSelect;
-    protected PreparedStatement bundleDelete;
-
-    // shared prepared statements for NodeReference management
-    protected PreparedStatement nodeReferenceInsert;
-    protected PreparedStatement nodeReferenceUpdate;
-    protected PreparedStatement nodeReferenceSelect;
-    protected PreparedStatement nodeReferenceDelete;
+    // SQL statements for NodeReference management
+    protected String nodeReferenceInsertSQL;
+    protected String nodeReferenceUpdateSQL;
+    protected String nodeReferenceSelectSQL;
+    protected String nodeReferenceDeleteSQL;
 
     /** file system where BLOB data is stored */
     protected CloseableBLOBStore blobStore;
@@ -371,6 +376,14 @@
         return errorHandling.toString();
     }
 
+    public void setBlockOnConnectionLoss(String block) {
+        this.blockOnConnectionLoss = Boolean.valueOf(block).booleanValue();
+    }
+
+    public String getBlockOnConnectionLoss() {
+        return Boolean.toString(blockOnConnectionLoss);
+    }
+
     /**
      * Returns <code>true</code> if the blobs are stored in the DB.
      * @return <code>true</code> if the blobs are stored in the DB.
@@ -404,7 +417,7 @@
                 throw new RepositoryException(msg);
             }
             BufferedReader reader = new BufferedReader(new InputStreamReader(in));
-            Statement stmt = con.createStatement();
+            Statement stmt = connectionManager.getConnection().createStatement();
             try {
                 String sql = reader.readLine();
                 while (sql != null) {
@@ -452,7 +465,7 @@
      * @throws SQLException if an SQL erro occurs.
      */
     protected boolean checkTablesExist() throws SQLException {
-        DatabaseMetaData metaData = con.getMetaData();
+        DatabaseMetaData metaData = connectionManager.getConnection().getMetaData();
         String tableName = schemaObjectPrefix + "BUNDLE";
         if (metaData.storesLowerCaseIdentifiers()) {
             tableName = tableName.toLowerCase();
@@ -486,36 +499,40 @@
      *
      * Basically wrapps a JDBC transaction around super.store().
      */
-    public synchronized void store(ChangeLog changeLog)
-            throws ItemStateException {
-
+    public synchronized void store(ChangeLog changeLog) throws ItemStateException {
+        Connection con = null;
         try {
-            con.setAutoCommit(false);
-            super.store(changeLog);
-        } catch (SQLException e) {
-            String msg = "setting autocommit failed.";
-            log.error(msg, e);
-            throw new ItemStateException(msg, e);
-        } catch (ItemStateException e) {
-            // storing the changes failed, rollback changes
+            boolean tryAgain = true;
+            do {
+                try {
+                    con = connectionManager.getConnection();
+                    connectionManager.setAutoReconnect(false);
+                    con.setAutoCommit(false);
+                    super.store(changeLog);
+                    con.commit();
+                    con.setAutoCommit(true);
+                } catch (SQLException e) {
+                    if (tryAgain) {
+                        tryAgain = false;
+                        continue;
+                    }
+                    throw e;
+                }
+            } while(false);
+        } catch (Throwable th) {
             try {
-                con.rollback();
-            } catch (SQLException e1) {
-                String msg = "rollback of change log failed";
-                log.error(msg, e1);
+                if (con != null) {
+                    con.rollback();
+                }
+            } catch (SQLException e) {
+                logException("rollback failed", e);
             }
-            // re-throw original exception
-            throw e;
-        }
-
-        // storing the changes succeeded, now commit the changes
-        try {
-            con.commit();
-            con.setAutoCommit(true);
-        } catch (SQLException e) {
-            String msg = "committing change log failed";
-            log.error(msg, e);
-            throw new ItemStateException(msg, e);
+            if (th instanceof SQLException || th.getCause() instanceof SQLException) {
+                connectionManager.close();
+            }
+            throw new ItemStateException(th.getMessage());
+        } finally {
+            connectionManager.setAutoReconnect(true);
         }
     }
 
@@ -530,23 +547,8 @@
 
         this.name = context.getHomeDir().getName();
 
-        // setup jdbc connection
-        // Note: Explicit creation of new instance of the driver is required
-        // in order to re-register the driver in the DriverManager after a
-        // repository shutdown.
-        Driver drv = (Driver) Class.forName(driver).newInstance();
-        log.info("JDBC driver created: {}", drv);
-        con = DriverManager.getConnection(url, user, password);
-        
-        DatabaseMetaData meta = con.getMetaData();
-        try {
-            log.info("Database: " + meta.getDatabaseProductName() + " / " + meta.getDatabaseProductVersion());
-            log.info("Driver: " + meta.getDriverName() + " / " + meta.getDriverVersion());
-        } catch (SQLException e) {
-            log.warn("Can not retrieve database and driver name / version", e);
-        }
-        
-        con.setAutoCommit(true);
+        connectionManager = new ConnectionRecoveryManager(blockOnConnectionLoss,
+                getDriver(), getUrl(), getUser(), getPassword());
 
         // make sure schemaObjectPrefix consists of legal name characters only
         prepareSchemaObjectPrefix();
@@ -557,28 +559,8 @@
         // create correct blob store
         blobStore = createBlobStore();
 
-        // prepare statements
-        if (getStorageModel() == SM_BINARY_KEYS) {
-            bundleInsert = con.prepareStatement("insert into " + schemaObjectPrefix + "BUNDLE (BUNDLE_DATA, NODE_ID) values (?, ?)");
-            bundleUpdate = con.prepareStatement("update " + schemaObjectPrefix + "BUNDLE set BUNDLE_DATA = ? where NODE_ID = ?");
-            bundleSelect = con.prepareStatement("select BUNDLE_DATA from " + schemaObjectPrefix + "BUNDLE where NODE_ID = ?");
-            bundleDelete = con.prepareStatement("delete from " + schemaObjectPrefix + "BUNDLE where NODE_ID = ?");
-
-            nodeReferenceInsert = con.prepareStatement("insert into " + schemaObjectPrefix + "REFS (REFS_DATA, NODE_ID) values (?, ?)");
-            nodeReferenceUpdate = con.prepareStatement("update " + schemaObjectPrefix + "REFS set REFS_DATA = ? where NODE_ID = ?");
-            nodeReferenceSelect = con.prepareStatement("select REFS_DATA from " + schemaObjectPrefix + "REFS where NODE_ID = ?");
-            nodeReferenceDelete = con.prepareStatement("delete from " + schemaObjectPrefix + "REFS where NODE_ID = ?");
-        } else {
-            bundleInsert = con.prepareStatement("insert into " + schemaObjectPrefix + "BUNDLE (BUNDLE_DATA, NODE_ID_HI, NODE_ID_LO) values (?, ?, ?)");
-            bundleUpdate = con.prepareStatement("update " + schemaObjectPrefix + "BUNDLE set BUNDLE_DATA = ? where NODE_ID_HI = ? and NODE_ID_LO = ?");
-            bundleSelect = con.prepareStatement("select BUNDLE_DATA from " + schemaObjectPrefix + "BUNDLE where NODE_ID_HI = ? and NODE_ID_LO = ?");
-            bundleDelete = con.prepareStatement("delete from " + schemaObjectPrefix + "BUNDLE where NODE_ID_HI = ? and NODE_ID_LO = ?");
-
-            nodeReferenceInsert = con.prepareStatement("insert into " + schemaObjectPrefix + "REFS (REFS_DATA, NODE_ID_HI, NODE_ID_LO) values (?, ?, ?)");
-            nodeReferenceUpdate = con.prepareStatement("update " + schemaObjectPrefix + "REFS set REFS_DATA = ? where NODE_ID_HI = ? and NODE_ID_LO = ?");
-            nodeReferenceSelect = con.prepareStatement("select REFS_DATA from " + schemaObjectPrefix + "REFS where NODE_ID_HI = ? and NODE_ID_LO = ?");
-            nodeReferenceDelete = con.prepareStatement("delete from " + schemaObjectPrefix + "REFS where NODE_ID_HI = ? and NODE_ID_LO = ?");
-        }
+        buildSQLStatements();
+
         // load namespaces
         binding = new BundleBinding(errorHandling, blobStore, getNsIndex(), getNameIndex(), context.getDataStore());
         binding.setMinBlobSize(minBlobSize);
@@ -631,7 +613,7 @@
      * @throws SQLException if an SQL error occurs.
      */
     protected DbNameIndex createDbNameIndex() throws SQLException {
-        return new DbNameIndex(con, schemaObjectPrefix);
+        return new DbNameIndex(connectionManager, schemaObjectPrefix);
     }
 
     /**
@@ -685,20 +667,16 @@
         log.info("{}: checking workspace consistency...", name);
 
         Collection modifications = new ArrayList();
-        PreparedStatement stmt = null;
         ResultSet rs = null;
         DataInputStream din = null;
         try {
+            String sql;
             if (getStorageModel() == SM_BINARY_KEYS) {
-                stmt = con.prepareStatement(
-                        "select NODE_ID, BUNDLE_DATA from "
-                        + schemaObjectPrefix + "BUNDLE");
+                sql = "select NODE_ID, BUNDLE_DATA from " + schemaObjectPrefix + "BUNDLE";
             } else {
-                stmt = con.prepareStatement(
-                        "select NODE_ID_HI, NODE_ID_LO, BUNDLE_DATA from "
-                        + schemaObjectPrefix + "BUNDLE");
+                sql = "select NODE_ID_HI, NODE_ID_LO, BUNDLE_DATA from " + schemaObjectPrefix + "BUNDLE";
             }
-            stmt.execute();
+            Statement stmt = connectionManager.executeStmt(sql, new Object[0]);
             rs = stmt.getResultSet();
             while (rs.next()) {
                 NodeId id;
@@ -769,7 +747,6 @@
         } finally {
             closeStream(din);
             closeResultSet(rs);
-            closeStatement(stmt);
         }
 
         if (consistencyFix && !modifications.isEmpty()) {
@@ -799,7 +776,7 @@
      * @throws Exception if an error occurs
      */
     protected void prepareSchemaObjectPrefix() throws Exception {
-        DatabaseMetaData metaData = con.getMetaData();
+        DatabaseMetaData metaData = connectionManager.getConnection().getMetaData();
         String legalChars = metaData.getExtraNameCharacters();
         legalChars += "ABCDEFGHIJKLMNOPQRSTUVWXZY0123456789_";
 
@@ -829,24 +806,10 @@
         }
 
         try {
-            // close shared prepared statements
-            closeStatement(bundleInsert);
-            closeStatement(bundleUpdate);
-            closeStatement(bundleSelect);
-            closeStatement(bundleDelete);
-
-            closeStatement(nodeReferenceInsert);
-            closeStatement(nodeReferenceUpdate);
-            closeStatement(nodeReferenceSelect);
-            closeStatement(nodeReferenceDelete);
-
             if (nameIndex instanceof DbNameIndex) {
                 ((DbNameIndex) nameIndex).close();
             }
-
-            // close jdbc connection
-            con.close();
-
+            connectionManager.close();
             // close blob store
             blobStore.close();
             blobStore = null;
@@ -877,16 +840,63 @@
     }
 
     /**
+     * Constructs a parameter list for a PreparedStatement
+     * for the given UUID.
+     *
+     * @param uuid the uuid
+     * @return a list of Objects
+     */
+    protected Object[] getKey(UUID uuid) {
+        if (getStorageModel() == SM_BINARY_KEYS) {
+            return new Object[]{uuid.getRawBytes()};
+        } else {
+            return new Object[]{new Long(uuid.getMostSignificantBits()),
+                    new Long(uuid.getLeastSignificantBits())};
+        }
+    }
+
+    /**
+     * Creates a parameter array for an SQL statement that needs
+     * (i) a UUID, and (2) another parameter.
+     *
+     * @param uuid the UUID
+     * @param p the other parameter
+     * @param before whether the other parameter should be before the uuid parameter
+     * @return an Object array that represents the parameters
+     */
+    protected Object[] createParams(UUID uuid, Object p, boolean before) {
+
+        // Create the key
+        List key = new ArrayList();
+        if (getStorageModel() == SM_BINARY_KEYS) {
+            key.add(uuid.getRawBytes());
+        } else {
+            key.add(new Long(uuid.getMostSignificantBits()));
+            key.add(new Long(uuid.getLeastSignificantBits()));
+        }
+
+        // Create the parameters
+        List params = new ArrayList();
+        if (before) {
+            params.add(p);
+            params.addAll(key);
+        } else {
+            params.addAll(key);
+            params.add(p);
+        }
+
+        return params.toArray();
+    }
+
+    /**
      * {@inheritDoc}
      */
     protected synchronized NodePropBundle loadBundle(NodeId id)
             throws ItemStateException {
-        PreparedStatement stmt = bundleSelect;
         ResultSet rs = null;
         InputStream in = null;
         try {
-            setKey(stmt, id.getUUID(), 1);
-            stmt.execute();
+            Statement stmt = connectionManager.executeStmt(bundleSelectSQL, getKey(id.getUUID()));
             rs = stmt.getResultSet();
             if (!rs.next()) {
                 return null;
@@ -911,7 +921,6 @@
         } finally {
             closeStream(in);
             closeResultSet(rs);
-            resetStatement(stmt);
         }
     }
 
@@ -919,13 +928,10 @@
      * {@inheritDoc}
      */
     protected synchronized boolean existsBundle(NodeId id) throws ItemStateException {
-        PreparedStatement stmt = bundleSelect;
         ResultSet rs = null;
         try {
-            setKey(stmt, id.getUUID(), 1);
-            stmt.execute();
+            Statement stmt = connectionManager.executeStmt(bundleSelectSQL, getKey(id.getUUID()));
             rs = stmt.getResultSet();
-
             // a bundle exists, if the result has at least one entry
             return rs.next();
         } catch (Exception e) {
@@ -934,7 +940,6 @@
             throw new ItemStateException(msg, e);
         } finally {
             closeResultSet(rs);
-            resetStatement(stmt);
         }
     }
 
@@ -942,27 +947,19 @@
      * {@inheritDoc}
      */
     protected synchronized void storeBundle(NodePropBundle bundle) throws ItemStateException {
-        PreparedStatement stmt = null;
         try {
             ByteArrayOutputStream out = new ByteArrayOutputStream(INITIAL_BUFFER_SIZE);
             DataOutputStream dout = new DataOutputStream(out);
             binding.writeBundle(dout, bundle);
             dout.close();
 
-            if (bundle.isNew()) {
-                stmt = bundleInsert;
-            } else {
-                stmt = bundleUpdate;
-            }
-            stmt.setBytes(1, out.toByteArray());
-            setKey(stmt, bundle.getId().getUUID(), 2);
-            stmt.execute();
+            String sql = bundle.isNew() ? bundleInsertSQL : bundleUpdateSQL;
+            Object[] params = createParams(bundle.getId().getUUID(), out.toByteArray(), true);
+            connectionManager.executeStmt(sql, params);
         } catch (Exception e) {
             String msg = "failed to write bundle: " + bundle.getId();
             log.error(msg, e);
             throw new ItemStateException(msg, e);
-        } finally {
-            resetStatement(stmt);
         }
     }
 
@@ -970,10 +967,8 @@
      * {@inheritDoc}
      */
     protected synchronized void destroyBundle(NodePropBundle bundle) throws ItemStateException {
-        PreparedStatement stmt = bundleDelete;
         try {
-            setKey(stmt, bundle.getId().getUUID(), 1);
-            stmt.execute();
+            connectionManager.executeStmt(bundleDeleteSQL, getKey(bundle.getId().getUUID()));
             // also delete all
             bundle.removeAllProperties();
         } catch (Exception e) {
@@ -983,8 +978,6 @@
             String msg = "failed to delete bundle: " + bundle.getId();
             log.error(msg, e);
             throw new ItemStateException(msg, e);
-        } finally {
-            resetStatement(stmt);
         }
     }
 
@@ -997,12 +990,11 @@
             throw new IllegalStateException("not initialized");
         }
 
-        PreparedStatement stmt = nodeReferenceSelect;
         ResultSet rs = null;
         InputStream in = null;
         try {
-            setKey(stmt, targetId.getTargetId().getUUID(), 1);
-            stmt.execute();
+            Statement stmt = connectionManager.executeStmt(
+                    nodeReferenceSelectSQL, getKey(targetId.getTargetId().getUUID()));
             rs = stmt.getResultSet();
             if (!rs.next()) {
                 throw new NoSuchItemStateException(targetId.toString());
@@ -1023,7 +1015,6 @@
         } finally {
             closeStream(in);
             closeResultSet(rs);
-            resetStatement(stmt);
         }
     }
 
@@ -1041,34 +1032,25 @@
             throw new IllegalStateException("not initialized");
         }
 
-        PreparedStatement stmt = null;
-        try {
-            // check if insert or update
-            if (exists(refs.getId())) {
-                stmt = nodeReferenceUpdate;
-            } else {
-                stmt = nodeReferenceInsert;
-            }
+        // check if insert or update
+        boolean update = exists(refs.getId());
+        String sql = (update) ? nodeReferenceUpdateSQL : nodeReferenceInsertSQL;
 
-            ByteArrayOutputStream out = new ByteArrayOutputStream(INITIAL_BUFFER_SIZE);
+        try {
+            ByteArrayOutputStream out =
+                    new ByteArrayOutputStream(INITIAL_BUFFER_SIZE);
             // serialize references
             Serializer.serialize(refs, out);
 
-            // we are synchronized on this instance, therefore we do not
-            // not have to additionally synchronize on the preparedStatement
-
-            stmt.setBytes(1, out.toByteArray());
-            setKey(stmt, refs.getTargetId().getUUID(), 2);
-            stmt.execute();
+            Object[] params = createParams(refs.getTargetId().getUUID(), out.toByteArray(), true);
+            connectionManager.executeStmt(sql, params);
 
             // there's no need to close a ByteArrayOutputStream
             //out.close();
         } catch (Exception e) {
-            String msg = "failed to write property state: " + refs.getTargetId();
+            String msg = "failed to write node references: " + refs.getId();
             log.error(msg, e);
             throw new ItemStateException(msg, e);
-        } finally {
-            resetStatement(stmt);
         }
     }
 
@@ -1080,10 +1062,9 @@
             throw new IllegalStateException("not initialized");
         }
 
-        PreparedStatement stmt = nodeReferenceDelete;
         try {
-            setKey(stmt, refs.getTargetId().getUUID(), 1);
-            stmt.execute();
+            connectionManager.executeStmt(nodeReferenceDeleteSQL,
+                    getKey(refs.getTargetId().getUUID()));
         } catch (Exception e) {
             if (e instanceof NoSuchItemStateException) {
                 throw (NoSuchItemStateException) e;
@@ -1091,8 +1072,6 @@
             String msg = "failed to delete references: " + refs.getTargetId();
             log.error(msg, e);
             throw new ItemStateException(msg, e);
-        } finally {
-            resetStatement(stmt);
         }
     }
 
@@ -1104,22 +1083,21 @@
             throw new IllegalStateException("not initialized");
         }
 
-        PreparedStatement stmt = nodeReferenceSelect;
         ResultSet rs = null;
         try {
-            setKey(stmt, targetId.getTargetId().getUUID(), 1);
-            stmt.execute();
+            Statement stmt = connectionManager.executeStmt(nodeReferenceSelectSQL,
+                    getKey(targetId.getTargetId().getUUID()));
             rs = stmt.getResultSet();
 
-            // a reference exists, if the result has at least one entry
+            // a reference exists if the result has at least one entry
             return rs.next();
         } catch (Exception e) {
-            String msg = "failed to check existence of node references: " + targetId;
+            String msg = "failed to check existence of node references: "
+                + targetId;
             log.error(msg, e);
             throw new ItemStateException(msg, e);
         } finally {
             closeResultSet(rs);
-            resetStatement(stmt);
         }
     }
 
@@ -1206,6 +1184,34 @@
     }
 
     /**
+     * Initializes the SQL strings.
+     */
+    protected void buildSQLStatements() {
+        // prepare statements
+        if (getStorageModel() == SM_BINARY_KEYS) {
+            bundleInsertSQL = "insert into " + schemaObjectPrefix + "BUNDLE (BUNDLE_DATA, NODE_ID) values (?, ?)";
+            bundleUpdateSQL = "update " + schemaObjectPrefix + "BUNDLE set BUNDLE_DATA = ? where NODE_ID = ?";
+            bundleSelectSQL = "select BUNDLE_DATA from " + schemaObjectPrefix + "BUNDLE where NODE_ID = ?";
+            bundleDeleteSQL = "delete from " + schemaObjectPrefix + "BUNDLE where NODE_ID = ?";
+
+            nodeReferenceInsertSQL = "insert into " + schemaObjectPrefix + "REFS (REFS_DATA, NODE_ID) values (?, ?)";
+            nodeReferenceUpdateSQL = "update " + schemaObjectPrefix + "REFS set REFS_DATA = ? where NODE_ID = ?";
+            nodeReferenceSelectSQL = "select REFS_DATA from " + schemaObjectPrefix + "REFS where NODE_ID = ?";
+            nodeReferenceDeleteSQL = "delete from " + schemaObjectPrefix + "REFS where NODE_ID = ?";
+        } else {
+            bundleInsertSQL = "insert into " + schemaObjectPrefix + "BUNDLE (BUNDLE_DATA, NODE_ID_HI, NODE_ID_LO) values (?, ?, ?)";
+            bundleUpdateSQL = "update " + schemaObjectPrefix + "BUNDLE set BUNDLE_DATA = ? where NODE_ID_HI = ? and NODE_ID_LO = ?";
+            bundleSelectSQL = "select BUNDLE_DATA from " + schemaObjectPrefix + "BUNDLE where NODE_ID_HI = ? and NODE_ID_LO = ?";
+            bundleDeleteSQL = "delete from " + schemaObjectPrefix + "BUNDLE where NODE_ID_HI = ? and NODE_ID_LO = ?";
+
+            nodeReferenceInsertSQL = "insert into " + schemaObjectPrefix + "REFS (REFS_DATA, NODE_ID_HI, NODE_ID_LO) values (?, ?, ?)";
+            nodeReferenceUpdateSQL = "update " + schemaObjectPrefix + "REFS set REFS_DATA = ? where NODE_ID_HI = ? and NODE_ID_LO = ?";
+            nodeReferenceSelectSQL = "select REFS_DATA from " + schemaObjectPrefix + "REFS where NODE_ID_HI = ? and NODE_ID_LO = ?";
+            nodeReferenceDeleteSQL = "delete from " + schemaObjectPrefix + "REFS where NODE_ID_HI = ? and NODE_ID_LO = ?";
+        }
+    }
+
+    /**
      * Helper interface for closeable stores
      */
     protected static interface CloseableBLOBStore extends BLOBStore {
@@ -1244,28 +1250,18 @@
      */
     protected class DbBlobStore implements CloseableBLOBStore {
 
-        protected PreparedStatement blobInsert;
-        protected PreparedStatement blobUpdate;
-        protected PreparedStatement blobSelect;
-        protected PreparedStatement blobSelectExist;
-        protected PreparedStatement blobDelete;
+        protected String blobInsertSQL;
+        protected String blobUpdateSQL;
+        protected String blobSelectSQL;
+        protected String blobSelectExistSQL;
+        protected String blobDeleteSQL;
 
         public DbBlobStore() throws SQLException {
-            blobInsert =
-                    con.prepareStatement("insert into "
-                    + schemaObjectPrefix + "BINVAL (BINVAL_DATA, BINVAL_ID) values (?, ?)");
-            blobUpdate =
-                    con.prepareStatement("update "
-                    + schemaObjectPrefix + "BINVAL set BINVAL_DATA = ? where BINVAL_ID = ?");
-            blobSelect =
-                    con.prepareStatement("select BINVAL_DATA from "
-                    + schemaObjectPrefix + "BINVAL where BINVAL_ID = ?");
-            blobSelectExist =
-                    con.prepareStatement("select 1 from "
-                    + schemaObjectPrefix + "BINVAL where BINVAL_ID = ?");
-            blobDelete =
-                    con.prepareStatement("delete from "
-                    + schemaObjectPrefix + "BINVAL where BINVAL_ID = ?");
+            blobInsertSQL = "insert into " + schemaObjectPrefix + "BINVAL (BINVAL_DATA, BINVAL_ID) values (?, ?)";
+            blobUpdateSQL = "update " + schemaObjectPrefix + "BINVAL set BINVAL_DATA = ? where BINVAL_ID = ?";
+            blobSelectSQL = "select BINVAL_DATA from " + schemaObjectPrefix + "BINVAL where BINVAL_ID = ?";
+            blobSelectExistSQL = "select 1 from " + schemaObjectPrefix + "BINVAL where BINVAL_ID = ?";
+            blobDeleteSQL = "delete from " + schemaObjectPrefix + "BINVAL where BINVAL_ID = ?";
         }
 
         /**
@@ -1287,39 +1283,31 @@
          * {@inheritDoc}
          */
         public InputStream get(String blobId) throws Exception {
-            PreparedStatement stmt = blobSelect;
-            synchronized (stmt) {
-                try {
-                    stmt.setString(1, blobId);
-                    stmt.execute();
-                    final ResultSet rs = stmt.getResultSet();
-                    if (!rs.next()) {
-                        closeResultSet(rs);
-                        throw new Exception("no such BLOB: " + blobId);
-                    }
-                    InputStream in = rs.getBinaryStream(1);
-                    if (in == null) {
-                        // some databases treat zero-length values as NULL;
-                        // return empty InputStream in such a case
-                        closeResultSet(rs);
-                        return new ByteArrayInputStream(new byte[0]);
-                    }
+            Statement stmt = connectionManager.executeStmt(blobSelectSQL, new Object[]{blobId});
+            final ResultSet rs = stmt.getResultSet();
+            if (!rs.next()) {
+                closeResultSet(rs);
+                throw new Exception("no such BLOB: " + blobId);
+            }
+            InputStream in = rs.getBinaryStream(1);
+            if (in == null) {
+                // some databases treat zero-length values as NULL;
+                // return empty InputStream in such a case
+                closeResultSet(rs);
+                return new ByteArrayInputStream(new byte[0]);
+            }
 
-                    /**
-                     * return an InputStream wrapper in order to
-                     * close the ResultSet when the stream is closed
-                     */
-                    return new FilterInputStream(in) {
-                        public void close() throws IOException {
-                            in.close();
-                            // now it's safe to close ResultSet
-                            closeResultSet(rs);
-                        }
-                    };
-                } finally {
-                    resetStatement(stmt);
+            /**
+             * return an InputStream wrapper in order to
+             * close the ResultSet when the stream is closed
+             */
+            return new FilterInputStream(in) {
+                public void close() throws IOException {
+                    in.close();
+                    // now it's safe to close ResultSet
+                    closeResultSet(rs);
                 }
-            }
+            };
         }
 
         /**
@@ -1327,45 +1315,28 @@
          */
         public synchronized void put(String blobId, InputStream in, long size)
                 throws Exception {
-            PreparedStatement stmt = blobSelectExist;
-            try {
-                stmt.setString(1, blobId);
-                stmt.execute();
-                ResultSet rs = stmt.getResultSet();
-                // a BLOB exists if the result has at least one entry
-                boolean exists = rs.next();
-                resetStatement(stmt);
-                closeResultSet(rs);
+            Statement stmt = connectionManager.executeStmt(blobSelectExistSQL, new Object[]{blobId});
+            ResultSet rs = stmt.getResultSet();
+            // a BLOB exists if the result has at least one entry
+            boolean exists = rs.next();
+            closeResultSet(rs);
 
-                stmt = (exists) ? blobUpdate : blobInsert;
-                stmt.setBinaryStream(1, in, (int) size);
-                stmt.setString(2, blobId);
-                stmt.executeUpdate();
-            } finally {
-                resetStatement(stmt);
-            }
+            String sql = (exists) ? blobUpdateSQL : blobInsertSQL;
+            Object[] params = new Object[]{new ConnectionRecoveryManager.StreamWrapper(in, size), blobId};
+            connectionManager.executeStmt(sql, params);
         }
 
         /**
          * {@inheritDoc}
          */
         public synchronized boolean remove(String blobId) throws Exception {
-            PreparedStatement stmt = blobDelete;
-            try {
-                stmt.setString(1, blobId);
-                return stmt.executeUpdate() == 1;
-            } finally {
-                resetStatement(stmt);
-            }
+            Statement stmt = connectionManager.executeStmt(blobDeleteSQL, new Object[]{blobId});
+            return stmt.getUpdateCount() == 1;
         }
 
         public void close() {
-            closeStatement(blobInsert);
-            closeStatement(blobUpdate);
-            closeStatement(blobSelect);
-            closeStatement(blobSelectExist);
-            closeStatement(blobDelete);
+            // closing the database resources of this blobstore is left to the
+            // owning BundleDbPersistenceManager
         }
     }
-
 }

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/DerbyPersistenceManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/DerbyPersistenceManager.java?rev=575734&r1=575733&r2=575734&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/DerbyPersistenceManager.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/DerbyPersistenceManager.java Fri Sep 14 09:04:19 2007
@@ -19,9 +19,7 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.apache.jackrabbit.core.persistence.PMContext;
-import org.apache.jackrabbit.core.persistence.db.DatabasePersistenceManager;
 
-import java.sql.Connection;
 import java.sql.DriverManager;
 import java.sql.SQLException;
 import java.sql.Statement;
@@ -58,7 +56,7 @@
 
     /** name of the embedded driver */
     public static final String DERBY_EMBEDDED_DRIVER = "org.apache.derby.jdbc.EmbeddedDriver";
-    
+
     /** the default logger */
     private static Logger log = LoggerFactory.getLogger(DerbyPersistenceManager.class);
 
@@ -271,7 +269,7 @@
     protected void checkSchema() throws SQLException, RepositoryException {
         // set properties
         if (DERBY_EMBEDDED_DRIVER.equals(getDriver())) {
-            Statement stmt = con.createStatement();
+            Statement stmt = connectionManager.getConnection().createStatement();
             try {
                 stmt.execute("CALL SYSCS_UTIL.SYSCS_SET_DATABASE_PROPERTY " +
                         "('derby.storage.initialPages', '" + derbyStorageInitialPages + "')");
@@ -317,7 +315,7 @@
         }
 
         // prepare connection url for issuing shutdown command
-        String url = con.getMetaData().getURL();
+        String url = connectionManager.getConnection().getMetaData().getURL();
         int pos = url.lastIndexOf(';');
         if (pos != -1) {
             // strip any attributes from connection url
@@ -329,7 +327,7 @@
         // otherwise Derby would mysteriously complain about some pending uncommitted
         // changes which can't possibly be true.
         // @todo further investigate
-        con.setAutoCommit(true);
+        connectionManager.getConnection().setAutoCommit(true);
 
         // now it's safe to shutdown the embedded Derby database
         try {
@@ -342,4 +340,4 @@
         super.close();
     }
 
-}
\ No newline at end of file
+}

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/H2PersistenceManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/H2PersistenceManager.java?rev=575734&r1=575733&r2=575734&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/H2PersistenceManager.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/H2PersistenceManager.java Fri Sep 14 09:04:19 2007
@@ -17,7 +17,6 @@
 package org.apache.jackrabbit.core.persistence.bundle;
 
 import org.apache.jackrabbit.core.persistence.PMContext;
-import org.apache.jackrabbit.core.persistence.PersistenceManager;
 
 import java.sql.Statement;
 import java.sql.SQLException;
@@ -103,7 +102,7 @@
      * {@inheritDoc}
      */
     protected void checkSchema() throws SQLException, RepositoryException {
-        Statement stmt = con.createStatement();
+        Statement stmt = connectionManager.getConnection().createStatement();
         try {
             stmt.execute("SET LOCK_TIMEOUT " + lockTimeout);
         } finally {
@@ -121,7 +120,7 @@
         }
         if (getUrl().startsWith("jdbc:h2:file:")) {
             // have to explicitly shutdown in-proc h2
-            Statement stmt = con.createStatement();
+            Statement stmt = connectionManager.getConnection().createStatement();
             stmt.execute("shutdown");
             stmt.close();
         }
@@ -129,4 +128,4 @@
         super.close();
     }
 
-}
\ No newline at end of file
+}

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/Oracle9PersistenceManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/Oracle9PersistenceManager.java?rev=575734&r1=575733&r2=575734&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/Oracle9PersistenceManager.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/Oracle9PersistenceManager.java Fri Sep 14 09:04:19 2007
@@ -33,9 +33,9 @@
 import java.lang.reflect.Method;
 import java.sql.Blob;
 import java.sql.Connection;
-import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
+import java.sql.Statement;
 
 /**
  * <code>OracleLegacyPersistenceManager</code> provides support for Oracle jdbc
@@ -68,8 +68,8 @@
     private static Logger log = LoggerFactory.getLogger(Oracle9PersistenceManager.class);
 
     private Class blobClass;
-    private Integer DURATION_SESSION_CONSTANT;
-    private Integer MODE_READWRITE_CONSTANT;
+    private Integer duractionSessionConstant;
+    private Integer modeReadWriteConstant;
 
     public Oracle9PersistenceManager() {
     }
@@ -92,10 +92,10 @@
 
         // use the Connection object for using the exact same
         // class loader that the Oracle driver was loaded with
-        blobClass = con.getClass().getClassLoader().loadClass("oracle.sql.BLOB");
-        DURATION_SESSION_CONSTANT =
+        blobClass = connectionManager.getConnection().getClass().getClassLoader().loadClass("oracle.sql.BLOB");
+        duractionSessionConstant =
                 new Integer(blobClass.getField("DURATION_SESSION").getInt(null));
-        MODE_READWRITE_CONSTANT =
+        modeReadWriteConstant =
                 new Integer(blobClass.getField("MODE_READWRITE").getInt(null));
     }
 
@@ -111,7 +111,6 @@
      */
     protected synchronized void storeBundle(NodePropBundle bundle)
             throws ItemStateException {
-        PreparedStatement stmt = null;
         Blob blob = null;
         try {
             ByteArrayOutputStream out = new ByteArrayOutputStream(INITIAL_BUFFER_SIZE);
@@ -119,21 +118,15 @@
             binding.writeBundle(dout, bundle);
             dout.close();
 
-            if (bundle.isNew()) {
-                stmt = bundleInsert;
-            } else {
-                stmt = bundleUpdate;
-            }
+            String sql = bundle.isNew() ? bundleInsertSQL : bundleUpdateSQL;
             blob = createTemporaryBlob(new ByteArrayInputStream(out.toByteArray()));
-            stmt.setBlob(1, blob);
-            stmt.setBytes(2, bundle.getId().getUUID().getRawBytes());
-            stmt.execute();
+            Object[] params = createParams(bundle.getId().getUUID(), blob, true);
+            connectionManager.executeStmt(sql, params);
         } catch (Exception e) {
             String msg = "failed to write bundle: " + bundle.getId();
             log.error(msg, e);
             throw new ItemStateException(msg, e);
         } finally {
-            resetStatement(stmt);
             if (blob != null) {
                 try {
                     freeTemporaryBlob(blob);
@@ -152,15 +145,11 @@
             throw new IllegalStateException("not initialized");
         }
 
-        PreparedStatement stmt = null;
         Blob blob = null;
         try {
             // check if insert or update
-            if (exists(refs.getId())) {
-                stmt = nodeReferenceUpdate;
-            } else {
-                stmt = nodeReferenceInsert;
-            }
+            boolean update = exists(refs.getId());
+            String sql = (update) ? nodeReferenceUpdateSQL : nodeReferenceInsertSQL;
 
             ByteArrayOutputStream out = new ByteArrayOutputStream(INITIAL_BUFFER_SIZE);
             // serialize references
@@ -170,9 +159,8 @@
             // not have to additionally synchronize on the preparedStatement
 
             blob = createTemporaryBlob(new ByteArrayInputStream(out.toByteArray()));
-            stmt.setBlob(1, blob);
-            stmt.setBytes(2, refs.getTargetId().getUUID().getRawBytes());
-            stmt.execute();
+            Object[] params = createParams(refs.getTargetId().getUUID(), blob, true);
+            connectionManager.executeStmt(sql, params);
 
             // there's no need to close a ByteArrayOutputStream
             //out.close();
@@ -181,7 +169,6 @@
             log.error(msg, e);
             throw new ItemStateException(msg, e);
         } finally {
-            resetStatement(stmt);
             if (blob != null) {
                 try {
                     freeTemporaryBlob(blob);
@@ -210,9 +197,9 @@
         Method createTemporary = blobClass.getMethod("createTemporary",
                 new Class[]{Connection.class, Boolean.TYPE, Integer.TYPE});
         Object blob = createTemporary.invoke(null,
-                new Object[]{con, Boolean.FALSE, DURATION_SESSION_CONSTANT});
+                new Object[]{connectionManager.getConnection(), Boolean.FALSE, duractionSessionConstant});
         Method open = blobClass.getMethod("open", new Class[]{Integer.TYPE});
-        open.invoke(blob, new Object[]{MODE_READWRITE_CONSTANT});
+        open.invoke(blob, new Object[]{modeReadWriteConstant});
         Method getBinaryOutputStream = blobClass.getMethod("getBinaryOutputStream", new Class[0]);
         OutputStream out = (OutputStream) getBinaryOutputStream.invoke(blob, null);
         try {
@@ -253,29 +240,23 @@
          */
         public synchronized void put(String blobId, InputStream in, long size)
                 throws Exception {
-            PreparedStatement stmt = blobSelectExist;
+
             Blob blob = null;
             try {
-                stmt.setString(1, blobId);
-                stmt.execute();
+                Statement stmt = connectionManager.executeStmt(blobSelectExistSQL, new Object[]{blobId});
                 ResultSet rs = stmt.getResultSet();
                 // a BLOB exists if the result has at least one entry
                 boolean exists = rs.next();
-                resetStatement(stmt);
                 closeResultSet(rs);
 
-                stmt = (exists) ? blobUpdate : blobInsert;
-
+                String sql = (exists) ? blobUpdateSQL : blobInsertSQL;
                 blob = createTemporaryBlob(in);
-                stmt.setBlob(1, blob);
-                stmt.setString(2, blobId);
-                stmt.executeUpdate();
+                connectionManager.executeStmt(sql, new Object[]{blob, blobId});
             } finally {
-                resetStatement(stmt);
                 if (blob != null) {
                     try {
                         freeTemporaryBlob(blob);
-                    } catch (Exception e1) {
+                    } catch (Exception e) {
                     }
                 }
             }

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/OraclePersistenceManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/OraclePersistenceManager.java?rev=575734&r1=575733&r2=575734&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/OraclePersistenceManager.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/OraclePersistenceManager.java Fri Sep 14 09:04:19 2007
@@ -20,8 +20,8 @@
 import java.sql.SQLException;
 
 import org.apache.jackrabbit.core.persistence.PMContext;
-import org.apache.jackrabbit.core.persistence.bundle.util.NGKDbNameIndex;
 import org.apache.jackrabbit.core.persistence.bundle.util.DbNameIndex;
+import org.apache.jackrabbit.core.persistence.bundle.util.NGKDbNameIndex;
 import org.apache.jackrabbit.util.Text;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -111,10 +111,10 @@
             setSchemaObjectPrefix(context.getHomeDir().getName() + "_");
         }
         super.init(context);
-        
+
         // check driver version
         try {
-            DatabaseMetaData metaData = con.getMetaData();
+            DatabaseMetaData metaData = connectionManager.getConnection().getMetaData();
             if (metaData.getDriverMajorVersion() < 10) {
                 // Oracle drivers prior to version 10 only support
                 // writing BLOBs up to 32k in size...
@@ -133,7 +133,7 @@
      * @throws SQLException if an SQL error occurs.
      */
     protected DbNameIndex createDbNameIndex() throws SQLException {
-        return new NGKDbNameIndex(con, schemaObjectPrefix);
+        return new NGKDbNameIndex(connectionManager, schemaObjectPrefix);
     }
 
     /**
@@ -168,7 +168,7 @@
      * @inheritDoc
      */
     protected void prepareSchemaObjectPrefix() throws Exception {
-        DatabaseMetaData metaData = con.getMetaData();
+        DatabaseMetaData metaData = connectionManager.getConnection().getMetaData();
         String legalChars = metaData.getExtraNameCharacters();
         legalChars += "ABCDEFGHIJKLMNOPQRSTUVWXZY0123456789_";
 

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/PostgreSQLPersistenceManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/PostgreSQLPersistenceManager.java?rev=575734&r1=575733&r2=575734&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/PostgreSQLPersistenceManager.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/PostgreSQLPersistenceManager.java Fri Sep 14 09:04:19 2007
@@ -28,9 +28,9 @@
 
 import java.io.DataInputStream;
 import java.io.InputStream;
-import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
+import java.sql.Statement;
 
 /**
  * Extends the {@link BundleDbPersistenceManager} by PostgreSQL specific code.
@@ -54,7 +54,7 @@
     /**
      * Logger instance.
      */
-    private static final Logger log =
+    private static Logger log =
         LoggerFactory.getLogger(PostgreSQLPersistenceManager.class);
 
     /**
@@ -77,7 +77,7 @@
      * @throws java.sql.SQLException if an SQL error occurs.
      */
     protected DbNameIndex createDbNameIndex() throws SQLException {
-        return new PostgreSQLNameIndex(con, schemaObjectPrefix);
+        return new PostgreSQLNameIndex(connectionManager, schemaObjectPrefix);
     }
 
     /**
@@ -90,10 +90,9 @@
 
     protected synchronized NodePropBundle loadBundle(NodeId id)
             throws ItemStateException {
-        PreparedStatement stmt = bundleSelect;
         try {
-            setKey(stmt, id.getUUID(), 1);
-            ResultSet rs = stmt.executeQuery();
+            Statement stmt = connectionManager.executeStmt(bundleSelectSQL, getKey(id.getUUID()));
+            ResultSet rs = stmt.getResultSet();
             try {
                 if (rs.next()) {
                     InputStream input = rs.getBinaryStream(1);
@@ -116,8 +115,6 @@
             String msg = "failed to read bundle: " + id + ": " + e;
             log.error(msg);
             throw new ItemStateException(msg, e);
-        } finally {
-            resetStatement(stmt);
         }
     }
 

Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/ConnectionRecoveryManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/ConnectionRecoveryManager.java?rev=575734&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/ConnectionRecoveryManager.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/ConnectionRecoveryManager.java Fri Sep 14 09:04:19 2007
@@ -0,0 +1,412 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.core.persistence.bundle.util;
+
+import java.io.InputStream;
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.HashMap;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This class provides methods to get a database connection and to execute SQL statements.
+ * It also contains reconnection logic. If the connection has been closed with the
+ * {@link #close()} method, then  a call to any public method except for
+ * {@link #setAutoReconnect(boolean)} will try to reestablish the connection, but
+ * only if the <code>autoReconnect</code> equals <code>true</code>.
+ * <p />
+ * The reconnection attempt
+ * can either be blocking or non-blocking, which is configured during construction.
+ * In the latter case a fixed number of reconnection attempts is made. When the
+ * reconnection failed an SQLException is thrown.
+ * <p />
+ * The methods of this class that execute SQL statements automatically call
+ * {@link #close()} when they encounter an SQLException.
+ *
+ */
+public class ConnectionRecoveryManager {
+
+    /**
+     * The default logger.
+     */
+    private static Logger log = LoggerFactory.getLogger(ConnectionRecoveryManager.class);
+
+    /**
+     * The database driver.
+     */
+    private final String driver;
+
+    /**
+     * The database URL.
+     */
+    private final String url;
+
+    /**
+     * The database user.
+     */
+    private final String user;
+
+    /**
+     * The database password.
+     */
+    private final String password;
+
+    /**
+     * The database connection that is managed by this {@link ConnectionRecoveryManager}.
+     */
+    private Connection connection;
+
+    /**
+     * An internal flag governing whether an automatic reconnect should be
+     * attempted after a SQLException had been encountered in
+     * {@link #executeStmt(String, Object[])}.
+     */
+    private boolean autoReconnect = true;
+
+    /**
+     * Indicates whether the reconnection function should block
+     * until the connection is up again.
+     */
+    private final boolean block;
+
+    /**
+     * Time to sleep in ms before a reconnect is attempted.
+     */
+    private static final int SLEEP_BEFORE_RECONNECT = 500;
+
+    /**
+     * Number of reconnection attempts per method call. Only
+     * used if <code>block == false</code>.
+     */
+    private static final int TRIALS = 20;
+
+    /**
+     * The map of prepared statements (key: SQL stmt, value: prepared stmt).
+     */
+    private HashMap preparedStatements = new HashMap();
+
+    /**
+     * Indicates whether the managed connection is open or closed.
+     */
+    private boolean isClosed;
+
+    /**
+     * Creates a {@link ConnectionRecoveryManager} and establishes
+     * a database Connection using the driver, user, password and url
+     * arguments.
+     * <p />
+     * By default, the connection is in auto-commit mode, and this
+     * manager tries to reconnect if the connection is lost.
+     *
+     * @param block whether this class should block until the connection can be recovered
+     * @param driver the driver to use for the connection
+     * @param url the url to use for the connection
+     * @param user the user to use for the connection
+     * @param password the password to use for the connection
+     */
+    public ConnectionRecoveryManager(boolean block, String driver, String url, String user, String password) {
+        this.block = block;
+        this.driver = driver;
+        this.url = url;
+        this.user = user;
+        this.password = password;
+        try {
+            setupConnection();
+            isClosed = false;
+        } catch (SQLException e) {
+            logException("could not setup connection", e);
+            close();
+        }
+    }
+
+    /**
+     * Gets the database connection that is managed. If the
+     * connection has been closed, and autoReconnect==true
+     * then an attempt is made to reestablish the connection.
+     *
+     * @return the database connection that is managed
+     * @throws SQLException on error
+     */
+    public synchronized Connection getConnection() throws SQLException {
+        if (isClosed) {
+            if (autoReconnect) {
+                reestablishConnection();
+            } else {
+                throw new SQLException("connection has been closed and autoReconnect == false");
+            }
+        }
+        return connection;
+    }
+
+    /**
+     * Starts a transaction. I.e., the auto-commit is set to false,
+     * and the manager does not try to reconnect if the connection
+     * is lost. This method call should be followed by a call to
+     * <code>endTransaction</code>.
+     *
+     * @throws SQLException on error
+     */
+    public synchronized void setAutoReconnect(boolean autoReconnect) {
+        this.autoReconnect = autoReconnect;
+    }
+
+    /**
+     * Executes the given SQL query.
+     *
+     * @param sql query to execute
+     * @return a <code>ResultSet</code> object
+     * @throws SQLException if an error occurs
+     */
+    public synchronized ResultSet executeQuery(String sql) throws SQLException {
+        PreparedStatement stmt = null;
+        try {
+            stmt = (PreparedStatement) preparedStatements.get(sql);
+            if (stmt == null) {
+                stmt = getConnection().prepareStatement(sql);
+                preparedStatements.put(sql, stmt);
+            }
+            return stmt.executeQuery();
+        } catch (SQLException e) {
+            logException("could not execute statement", e);
+            close();
+            throw e;
+        } finally {
+            resetStatement(stmt);
+        }
+    }
+
+    /**
+     * Executes the given SQL statement with the specified parameters.
+     *
+     * @param sql statement to execute
+     * @param params parameters to set
+     * @return the <code>Statement</code> object that had been executed
+     * @throws SQLException if an error occurs
+     */
+    public synchronized Statement executeStmt(String sql, Object[] params) throws SQLException {
+        try {
+            PreparedStatement stmt = (PreparedStatement) preparedStatements.get(sql);
+            if (stmt == null) {
+                stmt = getConnection().prepareStatement(sql);
+                preparedStatements.put(sql, stmt);
+            }
+            return executeStmtInternal(params, stmt);
+        } catch (SQLException e) {
+            logException("could not execute statement", e);
+            close();
+            throw e;
+        }
+    }
+
+    /**
+     * Executes the given SQL statement with the specified parameters.
+     *
+     * @param sql statement to execute
+     * @param params parameters to set
+     * @param autoGeneratedKeys the constant that tells the driver to make auto generated keys available
+     * @return the <code>Statement</code> object that had been executed
+     * @throws SQLException if an error occurs
+     */
+    public synchronized Statement executeStmt(String sql, Object[] params, int autoGeneratedKeys) throws SQLException {
+        try {
+            PreparedStatement stmt = (PreparedStatement) preparedStatements.get(sql + "_" + autoGeneratedKeys);
+            if (stmt == null) {
+                stmt = getConnection().prepareStatement(sql, autoGeneratedKeys);
+                preparedStatements.put(sql + "_" + autoGeneratedKeys, stmt);
+            }
+            return executeStmtInternal(params, stmt);
+        } catch (SQLException e) {
+            logException("could not execute statement", e);
+            close();
+            throw e;
+        }
+    }
+
+    /**
+     * Closes all resources held by this {@link ConnectionRecoveryManager}.
+     * An ongoing transaction is discarded.
+     */
+    public synchronized void close() {
+        preparedStatements.clear();
+        try {
+            if (connection != null) {
+                if (!connection.getAutoCommit()) {
+                    connection.rollback();
+                }
+                connection.close();
+            }
+        } catch (SQLException e) {
+            logException("failed to close connection", e);
+        }
+        connection = null;
+        isClosed = true;
+    }
+
+    /**
+     * Creates the database connection.
+     *
+     * @throws SQLException on error
+     */
+    private void setupConnection() throws SQLException {
+        try {
+            Class.forName(driver).newInstance();
+        } catch (Exception e) {
+            throw new SQLException("could not load driver: " + e.getMessage());
+        }
+        connection = DriverManager.getConnection(url, user, password);
+        connection.setAutoCommit(true);
+        try {
+            DatabaseMetaData meta = connection.getMetaData();
+            log.info("Database: " + meta.getDatabaseProductName() + " / " + meta.getDatabaseProductVersion());
+            log.info("Driver: " + meta.getDriverName() + " / " + meta.getDriverVersion());
+        } catch (SQLException e) {
+            log.warn("Can not retrieve database and driver name / version", e);
+        }
+    }
+
+    /**
+     * @param params the parameters for the <code>stmt</code> parameter
+     * @param stmt the statement to execute
+     * @return the executed Statement
+     * @throws SQLException on error
+     */
+    private Statement executeStmtInternal(Object[] params, PreparedStatement stmt) throws SQLException {
+        for (int i = 0; i < params.length; i++) {
+            if (params[i] instanceof StreamWrapper) {
+                StreamWrapper wrapper = (StreamWrapper) params[i];
+                stmt.setBinaryStream(i + 1, wrapper.stream, (int) wrapper.size);
+            } else {
+                stmt.setObject(i + 1, params[i]);
+            }
+        }
+        stmt.execute();
+        resetStatement(stmt);
+        return stmt;
+    }
+
+    /**
+     * Re-establishes the database connection.
+     *
+     * @throws SQLException if reconnecting failed
+     */
+    private void reestablishConnection() throws SQLException {
+
+        long trials = TRIALS;
+        SQLException exception = null;
+
+        // Close the connection (might already have been done)
+        close();
+
+        if (block) {
+            log.warn("blocking until database connection is up again...");
+        }
+
+        // Try to reconnect
+        while (trials-- >= 0 || block) {
+
+            // Reset the last caught exception
+            exception = null;
+
+            // Sleep for a while to give database a chance
+            // to restart before a reconnect is attempted.
+            try {
+                Thread.sleep(SLEEP_BEFORE_RECONNECT);
+            } catch (InterruptedException ignore) {
+            }
+
+            // now try to re-establish connection
+            try {
+                setupConnection();
+                isClosed = false;
+                break;
+            } catch (SQLException e) {
+                exception = e;
+                close();
+            }
+        }
+
+        // Rethrow last caught exception (if this is not null, then
+        // we know that reconnecting failed and close has been called.
+        if (exception != null) {
+            throw exception;
+        } else if (block) {
+            log.warn("database connection is up again!");
+        }
+    }
+
+    /**
+     * Resets the given <code>PreparedStatement</code> by clearing the
+     * parameters and warnings contained.
+     *
+     * @param stmt The <code>PreparedStatement</code> to reset. If
+     *             <code>null</code> this method does nothing.
+     */
+    private void resetStatement(PreparedStatement stmt) {
+        if (stmt != null) {
+            try {
+                stmt.clearParameters();
+                stmt.clearWarnings();
+            } catch (SQLException se) {
+                logException("Failed resetting PreparedStatement", se);
+            }
+        }
+    }
+
+    /**
+     * Logs an sql exception.
+     *
+     * @param message the message
+     * @param se the exception
+     */
+    private void logException(String message, SQLException se) {
+        message = message == null ? "" : message;
+        log.error(message + ", reason: " + se.getMessage() + ", state/code: "
+                + se.getSQLState() + "/" + se.getErrorCode());
+        log.debug("   dump:", se);
+    }
+
+    /**
+     * A wrapper for a binary stream that includes the
+     * size of the stream.
+     *
+     */
+    public static class StreamWrapper {
+
+        private final InputStream stream;
+        private final long size;
+
+        /**
+         * Creates a wrapper for the given InputStream that can
+         * savely be passed as a parameter to the <code>executeStmt</code>
+         * methods in the {@link ConnectionRecoveryManager} class.
+         *
+         * @param in the InputStream to wrap
+         * @param size the size of the input stream
+         */
+        public StreamWrapper(InputStream in, long size) {
+            this.stream = in;
+            this.size = size;
+        }
+    }
+}

Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/ConnectionRecoveryManager.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/DbNameIndex.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/DbNameIndex.java?rev=575734&r1=575733&r2=575734&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/DbNameIndex.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/DbNameIndex.java Fri Sep 14 09:04:19 2007
@@ -18,8 +18,6 @@
 
 import java.util.HashMap;
 
-import java.sql.Connection;
-import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.sql.Statement;
@@ -42,14 +40,19 @@
      */
     static final String CVS_ID = "$URL$ $Rev$ $Date$";
 
-    // name index statements
-    protected PreparedStatement nameSelect;
-    protected PreparedStatement indexSelect;
-    protected PreparedStatement nameInsert;
+    /**
+     * The class that manages statement execution and recovery from connection loss.
+     */
+    protected ConnectionRecoveryManager connectionManager;
 
+    // name index statements
+    protected String nameSelectSQL;
+    protected String indexSelectSQL;
+    protected String nameInsertSQL;
+    
     // caches
     private final HashMap string2Index = new HashMap();
-    private final HashMap index2String= new HashMap();
+    private final HashMap index2String = new HashMap();
 
     /**
      * Creates a new index that is stored in a db.
@@ -57,9 +60,10 @@
      * @param schemaObjectPrefix the prefix for table names
      * @throws SQLException if the statements cannot be prepared.
      */
-    public DbNameIndex(Connection con, String schemaObjectPrefix)
+    public DbNameIndex(ConnectionRecoveryManager conMgr, String schemaObjectPrefix)
             throws SQLException {
-        init(con, schemaObjectPrefix);
+        connectionManager = conMgr;
+        init(schemaObjectPrefix);
     }
 
     /**
@@ -69,20 +73,19 @@
      * @param schemaObjectPrefix the prefix for table names
      * @throws SQLException if the statements cannot be prepared.
      */
-    protected void init(Connection con, String schemaObjectPrefix)
+    protected void init(String schemaObjectPrefix)
             throws SQLException {
-        nameSelect = con.prepareStatement("select NAME from " + schemaObjectPrefix + "NAMES where ID = ?");
-        indexSelect = con.prepareStatement("select ID from " + schemaObjectPrefix + "NAMES where NAME = ?");
-        nameInsert = con.prepareStatement("insert into " + schemaObjectPrefix + "NAMES (NAME) values (?)", Statement.RETURN_GENERATED_KEYS);
+        nameSelectSQL = "select NAME from " + schemaObjectPrefix + "NAMES where ID = ?";
+        indexSelectSQL = "select ID from " + schemaObjectPrefix + "NAMES where NAME = ?";
+        nameInsertSQL = "insert into " + schemaObjectPrefix + "NAMES (NAME) values (?)";
     }
 
     /**
      * Closes this index and releases it's resources.
      */
     public void close() {
-        closeStatement(nameSelect);
-        closeStatement(indexSelect);
-        closeStatement(nameInsert);
+        // closing the database resources is done by the owning
+        // BundleDbPersistenceManager that created this index
     }
 
     /**
@@ -135,11 +138,9 @@
      */
     protected int insertString(String string) {
         // assert index does not exist
-        PreparedStatement stmt = nameInsert;
         ResultSet rs = null;
         try {
-            stmt.setString(1, string);
-            stmt.executeUpdate();
+            Statement stmt = connectionManager.executeStmt(nameInsertSQL, new Object[]{string}, Statement.RETURN_GENERATED_KEYS);
             rs = stmt.getGeneratedKeys();
             if (!rs.next()) {
                 return -1;
@@ -150,7 +151,6 @@
             throw new IllegalStateException("Unable to insert index: " + e);
         } finally {
             closeResultSet(rs);
-            resetStatement(stmt);
         }
     }
 
@@ -160,11 +160,9 @@
      * @return the index or -1 if not found.
      */
     protected int getIndex(String string) {
-        PreparedStatement stmt = indexSelect;
         ResultSet rs = null;
         try {
-            stmt.setString(1, string);
-            stmt.execute();
+            Statement stmt = connectionManager.executeStmt(indexSelectSQL, new Object[]{string});
             rs = stmt.getResultSet();
             if (!rs.next()) {
                 return -1;
@@ -175,7 +173,6 @@
             throw new IllegalStateException("Unable to read index: " + e);
         } finally {
             closeResultSet(rs);
-            resetStatement(stmt);
         }
     }
 
@@ -185,11 +182,9 @@
      * @return the string or <code>null</code> if not found.
      */
     protected String getString(int index) {
-        PreparedStatement stmt = nameSelect;
         ResultSet rs = null;
         try {
-            stmt.setInt(1, index);
-            stmt.execute();
+            Statement stmt = connectionManager.executeStmt(nameSelectSQL, new Object[]{new Integer(index)});
             rs = stmt.getResultSet();
             if (!rs.next()) {
                 return null;
@@ -200,38 +195,6 @@
             throw new IllegalStateException("Unable to read name: " + e);
         } finally {
             closeResultSet(rs);
-            resetStatement(stmt);
-        }
-    }
-
-    /**
-     * closes the statement
-     * @param stmt the statement
-     */
-    protected void closeStatement(PreparedStatement stmt) {
-        if (stmt != null) {
-            try {
-                stmt.close();
-            } catch (SQLException se) {
-                // ignore
-            }
-        }
-    }
-    /**
-     * Resets the given <code>PreparedStatement</code> by clearing the
-     * parameters and warnings contained.
-     *
-     * @param stmt The <code>PreparedStatement</code> to reset. If
-     *             <code>null</code> this method does nothing.
-     */
-    protected void resetStatement(PreparedStatement stmt) {
-        if (stmt != null) {
-            try {
-                stmt.clearParameters();
-                stmt.clearWarnings();
-            } catch (SQLException se) {
-                // ignore
-            }
         }
     }
 
@@ -248,4 +211,4 @@
             }
         }
     }
-}
\ No newline at end of file
+}

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/NGKDbNameIndex.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/NGKDbNameIndex.java?rev=575734&r1=575733&r2=575734&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/NGKDbNameIndex.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/NGKDbNameIndex.java Fri Sep 14 09:04:19 2007
@@ -16,11 +16,8 @@
  */
 package org.apache.jackrabbit.core.persistence.bundle.util;
 
-import java.sql.PreparedStatement;
 import java.sql.SQLException;
-import java.sql.Connection;
 import java.sql.ResultSet;
-import java.sql.Statement;
 
 /**
  * Same as {@link DbNameIndex} but does not make use of the
@@ -36,23 +33,23 @@
 
     /**
      * Creates a new index that is stored in a db.
-     * @param con the jdbc connection
+     * @param con the ConnectionRecoveryManager
      * @param schemaObjectPrefix the prefix for table names
      * @throws SQLException if the statements cannot be prepared.
      */
-    public NGKDbNameIndex(Connection con, String schemaObjectPrefix)
+    public NGKDbNameIndex(ConnectionRecoveryManager conMgr, String schemaObjectPrefix)
             throws SQLException {
-        super(con, schemaObjectPrefix);
+        super(conMgr, schemaObjectPrefix);
     }
 
     /**
      * {@inheritDoc}
      */
-    protected void init(Connection con, String schemaObjectPrefix)
+    protected void init(String schemaObjectPrefix)
             throws SQLException {
-        nameSelect = con.prepareStatement("select NAME from " + schemaObjectPrefix + "NAMES where ID = ?");
-        indexSelect = con.prepareStatement("select ID from " + schemaObjectPrefix + "NAMES where NAME = ?");
-        nameInsert = con.prepareStatement("insert into " + schemaObjectPrefix + "NAMES (NAME) values (?)");
+        nameSelectSQL = "select NAME from " + schemaObjectPrefix + "NAMES where ID = ?";
+        indexSelectSQL = "select ID from " + schemaObjectPrefix + "NAMES where NAME = ?";
+        nameInsertSQL = "insert into " + schemaObjectPrefix + "NAMES (NAME) values (?)";
     }
 
     /**
@@ -66,17 +63,14 @@
      */
     protected int insertString(String string) {
         // assert index does not exist
-        PreparedStatement stmt = nameInsert;
         ResultSet rs = null;
         try {
-            stmt.setString(1, string);
-            stmt.executeUpdate();
+            connectionManager.executeStmt(nameInsertSQL, new Object[]{string});
             return getIndex(string);
         } catch (Exception e) {
             throw new IllegalStateException("Unable to insert index: " + e);
         } finally {
             closeResultSet(rs);
-            resetStatement(stmt);
         }
     }
-}
\ No newline at end of file
+}

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/PostgreSQLNameIndex.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/PostgreSQLNameIndex.java?rev=575734&r1=575733&r2=575734&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/PostgreSQLNameIndex.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/PostgreSQLNameIndex.java Fri Sep 14 09:04:19 2007
@@ -16,8 +16,6 @@
  */
 package org.apache.jackrabbit.core.persistence.bundle.util;
 
-import java.sql.Connection;
-import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
 
@@ -28,11 +26,11 @@
  */
 public class PostgreSQLNameIndex extends DbNameIndex {
 
-    protected PreparedStatement generatedKeySelect;
+    protected String generatedKeySelectSQL;
 
-    public PostgreSQLNameIndex(Connection con, String schemaObjectPrefix)
+    public PostgreSQLNameIndex(ConnectionRecoveryManager conMgr, String schemaObjectPrefix)
             throws SQLException {
-        super(con, schemaObjectPrefix);
+        super(conMgr, schemaObjectPrefix);
     }
 
     /**
@@ -42,16 +40,12 @@
      * @param schemaObjectPrefix the prefix for table names
      * @throws SQLException if the statements cannot be prepared.
      */
-    protected void init(Connection con, String schemaObjectPrefix)
+    protected void init(String schemaObjectPrefix)
             throws SQLException {
-        nameSelect = con.prepareStatement(
-                "select NAME from " + schemaObjectPrefix + "NAMES where ID = ?");
-        indexSelect = con.prepareStatement(
-                "select ID from " + schemaObjectPrefix + "NAMES where NAME = ?");
-        nameInsert = con.prepareStatement(
-                "insert into " + schemaObjectPrefix + "NAMES (NAME) values (?)");
-        generatedKeySelect = con.prepareStatement(
-                "select currval('" + schemaObjectPrefix + "NAMES_ID_SEQ')");
+        nameSelectSQL = "select NAME from " + schemaObjectPrefix + "NAMES where ID = ?";
+        indexSelectSQL = "select ID from " + schemaObjectPrefix + "NAMES where NAME = ?";
+        nameInsertSQL = "insert into " + schemaObjectPrefix + "NAMES (NAME) values (?)";
+        generatedKeySelectSQL = "select currval('" + schemaObjectPrefix + "NAMES_ID_SEQ')";
     }
 
     /**
@@ -65,15 +59,11 @@
      */
     protected int insertString(String string) {
         // assert index does not exist
-        PreparedStatement stmt = nameInsert;
         try {
-            stmt.setString(1, string);
-            stmt.executeUpdate();
+            connectionManager.executeStmt(nameInsertSQL, new Object[]{string});
             return getGeneratedKey();
         } catch (Exception e) {
             throw new IllegalStateException("Unable to insert index: " + e);
-        } finally {
-            resetStatement(stmt);
         }
     }
 
@@ -82,9 +72,8 @@
      * @return the index.
      */
     protected int getGeneratedKey() {
-        PreparedStatement stmt = generatedKeySelect;
         try {
-            ResultSet rs = stmt.executeQuery();
+            ResultSet rs = connectionManager.executeQuery(generatedKeySelectSQL);
             try {
                 if (!rs.next()) {
                     return -1;
@@ -96,8 +85,6 @@
             }
         } catch (Exception e) {
             throw new IllegalStateException("Unable to read index: " + e);
-        } finally {
-            resetStatement(stmt);
         }
     }