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><param name="{@link #setSchema(String) schema}" value=""/>
* <li><param name="{@link #setSchemaObjectPrefix(String) schemaObjectPrefix}" value=""/>
* <li><param name="{@link #setErrorHandling(String) errorHandling}" value=""/>
+ * <li><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);
}
}