Index: jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/OraclePersistenceManager.java =================================================================== --- jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/OraclePersistenceManager.java (revision 629174) +++ jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/OraclePersistenceManager.java (working copy) @@ -20,8 +20,10 @@ import java.sql.SQLException; import org.apache.jackrabbit.core.persistence.PMContext; +import org.apache.jackrabbit.core.persistence.bundle.util.DbNameIndex; import org.apache.jackrabbit.core.persistence.bundle.util.NGKDbNameIndex; -import org.apache.jackrabbit.core.persistence.bundle.util.DbNameIndex; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Extends the {@link BundleDbPersistenceManager} by Oracle specific code. @@ -47,6 +49,11 @@ static final String CVS_ID = "$URL$ $Rev$ $Date$"; /** + * the default logger + */ + private static Logger log = LoggerFactory.getLogger(OraclePersistenceManager.class); + + /** * Creates a new oracle persistence manager */ public OraclePersistenceManager() { @@ -75,17 +82,20 @@ setSchemaObjectPrefix(context.getHomeDir().getName() + "_"); } super.init(context); -/* + // check driver version - DatabaseMetaData metaData = con.getMetaData(); - if (metaData.getDriverMajorVersion() < 10) { - // oracle drivers prior to version 10 only support - // writing BLOBs up to 32k in size... - log.warn("unsupported driver version detected: " - + metaData.getDriverName() - + " v" + metaData.getDriverVersion()); + try { + DatabaseMetaData metaData = connectionManager.getConnection().getMetaData(); + if (metaData.getDriverMajorVersion() < 10) { + // Oracle drivers prior to version 10 only support + // writing BLOBs up to 32k in size... + log.warn("Unsupported driver version detected: " + + metaData.getDriverName() + + " v" + metaData.getDriverVersion()); + } + } catch (SQLException e) { + log.warn("Can not retrieve driver version", e); } -*/ } /** @@ -94,7 +104,7 @@ * @throws SQLException if an SQL error occurs. */ protected DbNameIndex createDbNameIndex() throws SQLException { - return new NGKDbNameIndex(con, schemaObjectPrefix); + return new NGKDbNameIndex(connectionManager, schemaObjectPrefix); } /** @@ -114,7 +124,7 @@ * @inheritDoc */ protected void prepareSchemaObjectPrefix() throws Exception { - DatabaseMetaData metaData = con.getMetaData(); + DatabaseMetaData metaData = connectionManager.getConnection().getMetaData(); String legalChars = metaData.getExtraNameCharacters(); legalChars += "ABCDEFGHIJKLMNOPQRSTUVWXZY0123456789_"; Index: jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/DerbyPersistenceManager.java =================================================================== --- jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/DerbyPersistenceManager.java (revision 629174) +++ jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/DerbyPersistenceManager.java (working copy) @@ -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 +} Index: jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/H2PersistenceManager.java =================================================================== --- jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/H2PersistenceManager.java (revision 629174) +++ jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/H2PersistenceManager.java (working copy) @@ -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 +} Index: jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/BundleDbPersistenceManager.java =================================================================== --- jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/BundleDbPersistenceManager.java (revision 629174) +++ jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/BundleDbPersistenceManager.java (working copy) @@ -26,6 +26,7 @@ import org.apache.jackrabbit.core.state.NodeReferences; import org.apache.jackrabbit.core.persistence.PMContext; import org.apache.jackrabbit.core.persistence.AbstractPersistenceManager; +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; @@ -54,15 +55,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; @@ -83,6 +83,7 @@ *
  • <param name="{@link #setSchema(String) schema}" value=""/> *
  • <param name="{@link #setSchemaObjectPrefix(String) schemaObjectPrefix}" value=""/> *
  • <param name="{@link #setErrorHandling(String) errorHandling}" value=""/> + *
  • <param name="{@link #setBlockOnConnectionLoss(String) blockOnConnectionLoss}" value="false"/> * */ public class BundleDbPersistenceManager extends AbstractBundlePersistenceManager { @@ -136,21 +137,25 @@ /** inidicates if uses (filesystem) blob store */ protected boolean externalBLOBs; + /** indicates whether to block if the database connection is lost */ + protected boolean blockOnConnectionLoss = false; - /** jdbc conection */ - protected Connection con; + /** + * The class that manages statement execution and recovery from connection loss. + */ + protected ConnectionRecoveryManager connectionManager; - // shared prepared statements for bundle management - protected PreparedStatement bundleInsert; - protected PreparedStatement bundleUpdate; - protected PreparedStatement bundleSelect; - protected PreparedStatement bundleDelete; + // SQL statements for bundle management + protected String bundleInsertSQL; + protected String bundleUpdateSQL; + protected String bundleSelectSQL; + protected String bundleDeleteSQL; - // 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; @@ -372,6 +377,14 @@ return errorHandling.toString(); } + public void setBlockOnConnectionLoss(String block) { + this.blockOnConnectionLoss = Boolean.valueOf(block).booleanValue(); + } + + public String getBlockOnConnectionLoss() { + return Boolean.toString(blockOnConnectionLoss); + } + /** * Returns true if the blobs are stored in the DB. * @return true if the blobs are stored in the DB. @@ -405,7 +418,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) { @@ -440,10 +453,11 @@ * @return true if the tables exist; * false otherwise. * - * @throws SQLException if an SQL erro occurs. + * @throws SQLException if a database error occurs. + * @throws RepositoryException if a repository exception occurs. */ - protected boolean checkTablesExist() throws SQLException { - DatabaseMetaData metaData = con.getMetaData(); + protected boolean checkTablesExist() throws SQLException, RepositoryException { + DatabaseMetaData metaData = connectionManager.getConnection().getMetaData(); String tableName = schemaObjectPrefix + "BUNDLE"; if (metaData.storesLowerCaseIdentifiers()) { tableName = tableName.toLowerCase(); @@ -477,37 +491,37 @@ * * Basically wrapps a JDBC transaction around super.store(). */ - public synchronized void store(ChangeLog changeLog) - throws ItemStateException { - - 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 + public synchronized void store(ChangeLog changeLog) throws ItemStateException { + int trials = 2; + Throwable lastException = null; + do { + trials--; + Connection con = null; try { - con.rollback(); - } catch (SQLException e1) { - String msg = "rollback of change log failed"; - log.error(msg, e1); + con = connectionManager.getConnection(); + connectionManager.setAutoReconnect(false); + con.setAutoCommit(false); + super.store(changeLog); + con.commit(); + con.setAutoCommit(true); + return; + } catch (Throwable th) { + lastException = th; + try { + if (con != null) { + con.rollback(); + } + } catch (SQLException e) { + logException("rollback failed", e); + } + if (th instanceof SQLException || th.getCause() instanceof SQLException) { + connectionManager.close(); + } + } finally { + connectionManager.setAutoReconnect(true); } - // 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); - } + } while(blockOnConnectionLoss || trials > 0); + throw new ItemStateException(lastException.getMessage()); } /** @@ -521,14 +535,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); - con.setAutoCommit(true); + connectionManager = new ConnectionRecoveryManager(blockOnConnectionLoss, + getDriver(), getUrl(), getUser(), getPassword()); // make sure schemaObjectPrefix consists of legal name characters only prepareSchemaObjectPrefix(); @@ -539,28 +547,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 = ?"); + buildSQLStatements(); - 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 = ?"); - } // load namespaces binding = new BundleBinding(errorHandling, blobStore, getNsIndex(), getNameIndex()); binding.setMinBlobSize(minBlobSize); @@ -613,7 +601,7 @@ * @throws SQLException if an SQL error occurs. */ protected DbNameIndex createDbNameIndex() throws SQLException { - return new DbNameIndex(con, schemaObjectPrefix); + return new DbNameIndex(connectionManager, schemaObjectPrefix); } /** @@ -667,20 +655,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; @@ -751,7 +735,6 @@ } finally { closeStream(din); closeResultSet(rs); - closeStatement(stmt); } if (consistencyFix && !modifications.isEmpty()) { @@ -781,7 +764,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_"; @@ -811,24 +794,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; @@ -859,16 +828,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; @@ -893,7 +909,6 @@ } finally { closeStream(in); closeResultSet(rs); - resetStatement(stmt); } } @@ -901,13 +916,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) { @@ -916,7 +928,6 @@ throw new ItemStateException(msg, e); } finally { closeResultSet(rs); - resetStatement(stmt); } } @@ -924,27 +935,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); } } @@ -952,10 +955,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) { @@ -965,8 +966,6 @@ String msg = "failed to delete bundle: " + bundle.getId(); log.error(msg, e); throw new ItemStateException(msg, e); - } finally { - resetStatement(stmt); } } @@ -979,12 +978,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()); @@ -1005,7 +1003,6 @@ } finally { closeStream(in); closeResultSet(rs); - resetStatement(stmt); } } @@ -1023,34 +1020,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 + Object[] params = createParams(refs.getTargetId().getUUID(), out.toByteArray(), true); + connectionManager.executeStmt(sql, params); - stmt.setBytes(1, out.toByteArray()); - setKey(stmt, refs.getTargetId().getUUID(), 2); - stmt.execute(); - // 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); } } @@ -1062,10 +1050,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; @@ -1073,8 +1060,6 @@ String msg = "failed to delete references: " + refs.getTargetId(); log.error(msg, e); throw new ItemStateException(msg, e); - } finally { - resetStatement(stmt); } } @@ -1086,22 +1071,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); } } @@ -1188,6 +1172,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 { @@ -1226,28 +1238,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 = ?"; } /** @@ -1269,39 +1271,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); } - } + }; } /** @@ -1309,45 +1303,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 } } - } Index: jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/AbstractBundlePersistenceManager.java =================================================================== --- jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/AbstractBundlePersistenceManager.java (revision 629174) +++ jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/AbstractBundlePersistenceManager.java (working copy) @@ -16,40 +16,40 @@ */ package org.apache.jackrabbit.core.persistence.bundle; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +import javax.jcr.PropertyType; + +import org.apache.jackrabbit.core.NamespaceRegistryImpl; +import org.apache.jackrabbit.core.NodeId; +import org.apache.jackrabbit.core.PropertyId; +import org.apache.jackrabbit.core.fs.FileSystem; import org.apache.jackrabbit.core.fs.FileSystemResource; -import org.apache.jackrabbit.core.fs.FileSystem; +import org.apache.jackrabbit.core.nodetype.PropDefId; +import org.apache.jackrabbit.core.persistence.PMContext; +import org.apache.jackrabbit.core.persistence.PersistenceManager; +import org.apache.jackrabbit.core.persistence.bundle.util.BundleCache; +import org.apache.jackrabbit.core.persistence.bundle.util.HashMapIndex; +import org.apache.jackrabbit.core.persistence.bundle.util.LRUNodeIdCache; +import org.apache.jackrabbit.core.persistence.bundle.util.NamespaceIndex; +import org.apache.jackrabbit.core.persistence.bundle.util.NodePropBundle; +import org.apache.jackrabbit.core.persistence.bundle.util.StringIndex; +import org.apache.jackrabbit.core.state.ChangeLog; import org.apache.jackrabbit.core.state.ItemState; -import org.apache.jackrabbit.core.state.ChangeLog; import org.apache.jackrabbit.core.state.ItemStateException; +import org.apache.jackrabbit.core.state.NoSuchItemStateException; import org.apache.jackrabbit.core.state.NodeReferences; -import org.apache.jackrabbit.core.state.NoSuchItemStateException; import org.apache.jackrabbit.core.state.NodeReferencesId; +import org.apache.jackrabbit.core.state.NodeState; import org.apache.jackrabbit.core.state.PropertyState; -import org.apache.jackrabbit.core.state.NodeState; -import org.apache.jackrabbit.core.NodeId; -import org.apache.jackrabbit.core.PropertyId; -import org.apache.jackrabbit.core.NamespaceRegistryImpl; -import org.apache.jackrabbit.core.nodetype.PropDefId; import org.apache.jackrabbit.core.value.InternalValue; -import org.apache.jackrabbit.core.persistence.PMContext; -import org.apache.jackrabbit.core.persistence.PersistenceManager; -import org.apache.jackrabbit.core.persistence.bundle.util.StringIndex; -import org.apache.jackrabbit.core.persistence.bundle.util.NamespaceIndex; -import org.apache.jackrabbit.core.persistence.bundle.util.NodePropBundle; -import org.apache.jackrabbit.core.persistence.bundle.util.BundleCache; -import org.apache.jackrabbit.core.persistence.bundle.util.LRUNodeIdCache; -import org.apache.jackrabbit.core.persistence.bundle.util.HashMapIndex; import org.apache.jackrabbit.name.QName; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Set; - -import javax.jcr.PropertyType; - /** * The AbstractBundlePersistenceManager acts as base for all * persistence managers that store the state in a {@link NodePropBundle}. Index: jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/ConnectionRecoveryManager.java =================================================================== --- jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/ConnectionRecoveryManager.java (revision 629174) +++ jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/ConnectionRecoveryManager.java (working copy) @@ -26,6 +26,8 @@ import java.sql.Statement; import java.util.HashMap; +import javax.jcr.RepositoryException; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -124,8 +126,9 @@ * @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 + * @throws RepositoryException if the database driver could not be loaded */ - public ConnectionRecoveryManager(boolean block, String driver, String url, String user, String password) { + public ConnectionRecoveryManager(boolean block, String driver, String url, String user, String password) throws RepositoryException { this.block = block; this.driver = driver; this.url = url; @@ -147,8 +150,9 @@ * * @return the database connection that is managed * @throws SQLException on error + * @throws RepositoryException if the database driver could not be loaded */ - public synchronized Connection getConnection() throws SQLException { + public synchronized Connection getConnection() throws SQLException, RepositoryException { if (isClosed) { if (autoReconnect) { reestablishConnection(); @@ -172,13 +176,38 @@ } /** + * Executes the given SQL query. Retries once or blocks (when the + * block parameter has been set to true on construction) + * if this fails and autoReconnect is enabled. + * + * @param sql the SQL query to execute + * @return the executed ResultSet + * @throws SQLException on error + * @throws RepositoryException if the database driver could not be loaded + */ + public synchronized ResultSet executeQuery(String sql) throws SQLException, RepositoryException { + int trials = 2; + SQLException lastException = null; + do { + trials--; + try { + return executeQueryInternal(sql); + } catch (SQLException e) { + lastException = e; + } + } while(autoReconnect && (block || trials > 0)); + throw lastException; + } + + /** * Executes the given SQL query. * * @param sql query to execute * @return a ResultSet object * @throws SQLException if an error occurs + * @throws RepositoryException if the database driver could not be loaded */ - public synchronized ResultSet executeQuery(String sql) throws SQLException { + private ResultSet executeQueryInternal(String sql) throws SQLException, RepositoryException { PreparedStatement stmt = null; try { stmt = (PreparedStatement) preparedStatements.get(sql); @@ -203,20 +232,34 @@ * @param params parameters to set * @return the Statement object that had been executed * @throws SQLException if an error occurs + * @throws RepositoryException if the database driver could not be loaded */ - 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); + public Statement executeStmt(String sql, Object[] params) throws SQLException, RepositoryException { + return executeStmt(sql, params, Statement.NO_GENERATED_KEYS); + } + + /** + * 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 Statement object that had been executed + * @throws SQLException if an error occurs + * @throws RepositoryException if the database driver could not be loaded + */ + public synchronized Statement executeStmt(String sql, Object[] params, int autoGeneratedKeys) throws SQLException, RepositoryException { + int trials = 2; + SQLException lastException = null; + do { + trials--; + try { + return executeStmtInternal(sql, params, autoGeneratedKeys); + } catch (SQLException e) { + lastException = e; } - return executeStmtInternal(params, stmt); - } catch (SQLException e) { - logException("could not execute statement", e); - close(); - throw e; - } + } while(autoReconnect && (block || trials > 0)); + throw lastException; } /** @@ -227,8 +270,9 @@ * @param autoGeneratedKeys the constant that tells the driver to make auto generated keys available * @return the Statement object that had been executed * @throws SQLException if an error occurs + * @throws RepositoryException if the database driver could not be loaded */ - public synchronized Statement executeStmt(String sql, Object[] params, int autoGeneratedKeys) throws SQLException { + private synchronized Statement executeStmtInternal(String sql, Object[] params, int autoGeneratedKeys) throws SQLException, RepositoryException { try { PreparedStatement stmt = (PreparedStatement) preparedStatements.get(sql + "_" + autoGeneratedKeys); if (stmt == null) { @@ -267,14 +311,24 @@ * Creates the database connection. * * @throws SQLException on error + * @throws RepositoryException if the database driver could not be loaded */ - private void setupConnection() throws SQLException { + private void setupConnection() throws SQLException, RepositoryException { try { - Class.forName(driver).newInstance(); - } catch (Exception e) { - throw new SQLException("could not load driver: " + e.getMessage()); + Class driverClass = Class.forName(driver); + // Workaround for Apache Derby: + // The JDBC specification recommends the Class.ForName method without the .newInstance() method call, + // but adding the newInstance() guarantees that Derby will be booted on any Java Virtual Machine. + driverClass.newInstance(); + } catch (Throwable e) { + throw new RepositoryException("Could not load or initialize the database driver class " + driver, e); } - connection = DriverManager.getConnection(url, user, password); + try { + connection = DriverManager.getConnection(url, user, password); + } catch (SQLException e) { + log.warn("Could not connect; driver: " + driver + " url: " + url + " user: " + user + " error: " + e.toString(), e); + throw e; + } connection.setAutoCommit(true); try { DatabaseMetaData meta = connection.getMetaData(); @@ -309,8 +363,9 @@ * Re-establishes the database connection. * * @throws SQLException if reconnecting failed + * @throws RepositoryException */ - private void reestablishConnection() throws SQLException { + private void reestablishConnection() throws SQLException, RepositoryException { long trials = TRIALS; SQLException exception = null; Index: jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/DbNameIndex.java =================================================================== --- jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/DbNameIndex.java (revision 629174) +++ jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/DbNameIndex.java (working copy) @@ -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 null 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,42 +195,10 @@ 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 PreparedStatement by clearing the - * parameters and warnings contained. - * - * @param stmt The PreparedStatement to reset. If - * null this method does nothing. - */ - protected void resetStatement(PreparedStatement stmt) { - if (stmt != null) { - try { - stmt.clearParameters(); - stmt.clearWarnings(); - } catch (SQLException se) { - // ignore - } - } - } - - /** * Closes the result set * @param rs the result set. */ @@ -248,4 +211,4 @@ } } } -} \ No newline at end of file +} Index: jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/NGKDbNameIndex.java =================================================================== --- jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/NGKDbNameIndex.java (revision 629174) +++ jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/NGKDbNameIndex.java (working copy) @@ -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 +} Index: jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/Oracle9PersistenceManager.java =================================================================== --- jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/Oracle9PersistenceManager.java (revision 629174) +++ jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/Oracle9PersistenceManager.java (working copy) @@ -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; /** * OracleLegacyPersistenceManager 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) { } } }