Return-Path: X-Original-To: apmail-jackrabbit-commits-archive@www.apache.org Delivered-To: apmail-jackrabbit-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id B09911038D for ; Wed, 12 Mar 2014 11:06:22 +0000 (UTC) Received: (qmail 73881 invoked by uid 500); 12 Mar 2014 11:06:18 -0000 Delivered-To: apmail-jackrabbit-commits-archive@jackrabbit.apache.org Received: (qmail 73340 invoked by uid 500); 12 Mar 2014 11:06:16 -0000 Mailing-List: contact commits-help@jackrabbit.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@jackrabbit.apache.org Delivered-To: mailing list commits@jackrabbit.apache.org Received: (qmail 73109 invoked by uid 99); 12 Mar 2014 11:06:12 -0000 Received: from nike.apache.org (HELO nike.apache.org) (192.87.106.230) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 12 Mar 2014 11:06:12 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=5.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 12 Mar 2014 11:05:58 +0000 Received: from eris.apache.org (localhost [127.0.0.1]) by eris.apache.org (Postfix) with ESMTP id 0FA322388C29; Wed, 12 Mar 2014 11:05:12 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r1576690 [9/10] - in /jackrabbit/trunk: examples/jackrabbit-firsthops/ examples/jackrabbit-firsthops/src/main/resources/ jackrabbit-aws-ext/ jackrabbit-aws-ext/src/main/java/org/apache/jackrabbit/aws/ext/ jackrabbit-aws-ext/src/main/java/or... Date: Wed, 12 Mar 2014 11:05:08 -0000 To: commits@jackrabbit.apache.org From: reschke@apache.org X-Mailer: svnmailer-1.0.9 Message-Id: <20140312110512.0FA322388C29@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Modified: jackrabbit/trunk/jackrabbit-data/src/main/java/org/apache/jackrabbit/core/util/db/ConnectionHelper.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-data/src/main/java/org/apache/jackrabbit/core/util/db/ConnectionHelper.java?rev=1576690&r1=1576689&r2=1576690&view=diff ============================================================================== --- jackrabbit/trunk/jackrabbit-data/src/main/java/org/apache/jackrabbit/core/util/db/ConnectionHelper.java (original) +++ jackrabbit/trunk/jackrabbit-data/src/main/java/org/apache/jackrabbit/core/util/db/ConnectionHelper.java Wed Mar 12 11:05:06 2014 @@ -1,600 +1,600 @@ -/* - * 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.util.db; - -import java.sql.Connection; -import java.sql.DatabaseMetaData; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -import javax.sql.DataSource; - -import org.apache.jackrabbit.core.util.db.Oracle10R1ConnectionHelper; -import org.apache.jackrabbit.core.util.db.ResultSetWrapper; -import org.apache.jackrabbit.data.core.TransactionContext; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * This class provides convenience methods to execute SQL statements. They can be either executed in isolation - * or within the context of a JDBC transaction; the so-called batch mode (use the {@link #startBatch()} - * and {@link #endBatch(boolean)} methods for this). - * - *

- * - * This class contains logic to retry execution of SQL statements. If this helper is not in batch mode - * and if a statement fails due to an {@code SQLException}, then it is retried. If the {@code block} argument - * of the constructor call was {@code false} then it is retried only once. Otherwise the statement is retried - * until either it succeeds or the thread is interrupted. This clearly assumes that the only cause of {@code - * SQLExceptions} is faulty {@code Connections} which are restored eventually.
Note: - * This retry logic only applies to the following methods: - *

    - *
  • {@link #exec(String, Object...)}
  • - *
  • {@link #update(String, Object[])}
  • - *
  • {@link #exec(String, Object[], boolean, int)}
  • - *
- * - *

- * - * This class is not thread-safe and if it is to be used by multiple threads then the clients must make sure - * that access to this class is properly synchronized. - * - *

- * - * Implementation note: The {@code Connection} that is retrieved from the {@code DataSource} - * in {@link #getConnection()} may be broken. This is so because if an internal {@code DataSource} is used, - * then this is a commons-dbcp {@code DataSource} with a testWhileIdle validation strategy (see - * the {@code ConnectionFactory} class). Furthermore, if it is a {@code DataSource} obtained through JNDI then we - * can make no assumptions about the validation strategy. This means that our retry logic must either assume that - * the SQL it tries to execute can do so without errors (i.e., the statement is valid), or it must implement its - * own validation strategy to apply. Currently, the former is in place. - */ -public class ConnectionHelper { - - static Logger log = LoggerFactory.getLogger(ConnectionHelper.class); - - private static final int RETRIES = 1; - - private static final int SLEEP_BETWEEN_RETRIES_MS = 100; - - final boolean blockOnConnectionLoss; - - private final boolean checkTablesWithUserName; - - protected final DataSource dataSource; - - private Map batchConnectionMap = Collections.synchronizedMap(new HashMap()); - - /** - * The default fetchSize is '0'. This means the fetchSize Hint will be ignored - */ - private int fetchSize = 0; - - /** - * @param dataSrc the {@link DataSource} on which this instance acts - * @param block whether the helper should transparently block on DB connection loss (otherwise it retries - * once and if that fails throws exception) - */ - public ConnectionHelper(DataSource dataSrc, boolean block) { - dataSource = dataSrc; - checkTablesWithUserName = false; - blockOnConnectionLoss = block; - } - - /** - * @param dataSrc the {@link DataSource} on which this instance acts - * @param checkWithUserName whether the username is to be used for the {@link #tableExists(String)} method - * @param block whether the helper should transparently block on DB connection loss (otherwise it throws exceptions) - */ - protected ConnectionHelper(DataSource dataSrc, boolean checkWithUserName, boolean block) { - dataSource = dataSrc; - checkTablesWithUserName = checkWithUserName; - blockOnConnectionLoss = block; - } - - /** - * @param dataSrc the {@link DataSource} on which this instance acts - * @param checkWithUserName whether the username is to be used for the {@link #tableExists(String)} method - * @param block whether the helper should transparently block on DB connection loss (otherwise it throws exceptions) - * @param fetchSize the fetchSize that will be used per default - */ - protected ConnectionHelper(DataSource dataSrc, boolean checkWithUserName, boolean block, int fetchSize) { - dataSource = dataSrc; - checkTablesWithUserName = checkWithUserName; - blockOnConnectionLoss = block; - this.fetchSize = fetchSize; - } - - /** - * A utility method that makes sure that identifier does only consist of characters that are - * allowed in names on the target database. Illegal characters will be escaped as necessary. - * - * This method is not affected by the - * - * @param identifier the identifier to convert to a db specific identifier - * @return the db-normalized form of the given identifier - * @throws SQLException if an error occurs - */ - public final String prepareDbIdentifier(String identifier) throws SQLException { - if (identifier == null) { - return null; - } - String legalChars = "ABCDEFGHIJKLMNOPQRSTUVWXZY0123456789_"; - legalChars += getExtraNameCharacters(); - String id = identifier.toUpperCase(); - StringBuilder escaped = new StringBuilder(); - for (int i = 0; i < id.length(); i++) { - char c = id.charAt(i); - if (legalChars.indexOf(c) == -1) { - replaceCharacter(escaped, c); - } else { - escaped.append(c); - } - } - return escaped.toString(); - } - - /** - * Called from {@link #prepareDbIdentifier(String)}. Default implementation replaces the illegal - * characters with their hexadecimal encoding. - * - * @param escaped the escaped db identifier - * @param c the character to replace - */ - protected void replaceCharacter(StringBuilder escaped, char c) { - escaped.append("_x"); - String hex = Integer.toHexString(c); - escaped.append("0000".toCharArray(), 0, 4 - hex.length()); - escaped.append(hex); - escaped.append("_"); - } - - /** - * Returns true if we are currently in a batch mode, false otherwise. - * - * @return true if the current thread or the active transaction is running in batch mode, false otherwise. - */ - protected boolean inBatchMode() { - return getTransactionAwareBatchConnection() != null; - } - - /** - * The default implementation returns the {@code extraNameCharacters} provided by the databases metadata. - * - * @return the additional characters for identifiers supported by the db - * @throws SQLException on error - */ - private String getExtraNameCharacters() throws SQLException { - Connection con = dataSource.getConnection(); - try { - DatabaseMetaData metaData = con.getMetaData(); - return metaData.getExtraNameCharacters(); - } finally { - DbUtility.close(con, null, null); - } - } - - /** - * Checks whether the given table exists in the database. - * - * @param tableName the name of the table - * @return whether the given table exists - * @throws SQLException on error - */ - public final boolean tableExists(String tableName) throws SQLException { - Connection con = dataSource.getConnection(); - ResultSet rs = null; - boolean schemaExists = false; - String name = tableName; - try { - DatabaseMetaData metaData = con.getMetaData(); - if (metaData.storesLowerCaseIdentifiers()) { - name = tableName.toLowerCase(); - } else if (metaData.storesUpperCaseIdentifiers()) { - name = tableName.toUpperCase(); - } - String userName = null; - if (checkTablesWithUserName) { - userName = metaData.getUserName(); - } - rs = metaData.getTables(null, userName, name, null); - schemaExists = rs.next(); - } finally { - DbUtility.close(con, null, rs); - } - return schemaExists; - } - - /** - * Starts the batch mode. If an {@link SQLException} is thrown, then the batch mode is not started.

- * Important: clients that call this method must make sure that - * {@link #endBatch(boolean)} is called eventually. - * - * @throws SQLException on error - */ - public final void startBatch() throws SQLException { - if (inBatchMode()) { - throw new SQLException("already in batch mode"); - } - Connection batchConnection = null; - try { - batchConnection = getConnection(false); - batchConnection.setAutoCommit(false); - setTransactionAwareBatchConnection(batchConnection); - } catch (SQLException e) { - removeTransactionAwareBatchConnection(); - // Strive for failure atomicity - if (batchConnection != null) { - DbUtility.close(batchConnection, null, null); - } - throw e; - } - } - - /** - * This method always ends the batch mode. - * - * @param commit whether the changes in the batch should be committed or rolled back - * @throws SQLException if the commit or rollback of the underlying JDBC Connection threw an {@code - * SQLException} - */ - public final void endBatch(boolean commit) throws SQLException { - if (!inBatchMode()) { - throw new SQLException("not in batch mode"); - } - Connection batchConnection = getTransactionAwareBatchConnection(); - try { - if (commit) { - batchConnection.commit(); - } else { - batchConnection.rollback(); - } - } finally { - removeTransactionAwareBatchConnection(); - if (batchConnection != null) { - DbUtility.close(batchConnection, null, null); - } - } - } - - /** - * Executes a general SQL statement and immediately closes all resources. - * - * Note: We use a Statement if there are no parameters to avoid a problem on - * the Oracle 10g JDBC driver w.r.t. :NEW and :OLD keywords that triggers ORA-17041. - * - * @param sql an SQL statement string - * @param params the parameters for the SQL statement - * @throws SQLException on error - */ - public final void exec(final String sql, final Object... params) throws SQLException { - new RetryManager(params) { - - @Override - protected Void call() throws SQLException { - reallyExec(sql, params); - return null; - } - - }.doTry(); - } - - void reallyExec(String sql, Object... params) throws SQLException { - Connection con = null; - Statement stmt = null; - boolean inBatchMode = inBatchMode(); - try { - con = getConnection(inBatchMode); - if (params == null || params.length == 0) { - stmt = con.createStatement(); - stmt.execute(sql); - } else { - PreparedStatement p = con.prepareStatement(sql); - stmt = p; - execute(p, params); - } - } finally { - closeResources(con, stmt, null, inBatchMode); - } - } - - /** - * Executes an update or delete statement and returns the update count. - * - * @param sql an SQL statement string - * @param params the parameters for the SQL statement - * @return the update count - * @throws SQLException on error - */ - public final int update(final String sql, final Object... params) throws SQLException { - return new RetryManager(params) { - - @Override - protected Integer call() throws SQLException { - return reallyUpdate(sql, params); - } - - }.doTry(); - } - - int reallyUpdate(String sql, Object... params) throws SQLException { - Connection con = null; - PreparedStatement stmt = null; - boolean inBatchMode = inBatchMode(); - try { - con = getConnection(inBatchMode); - stmt = con.prepareStatement(sql); - return execute(stmt, params).getUpdateCount(); - } finally { - closeResources(con, stmt, null, inBatchMode); - } - } - - /** - * Executes a SQL query and returns the {@link ResultSet}. The - * returned {@link ResultSet} should be closed by clients. - * - * @param sql an SQL statement string - * @param params the parameters for the SQL statement - * @return a {@link ResultSet} - */ - public final ResultSet query(String sql, Object... params) throws SQLException { - return exec(sql, params, false, 0); - } - - /** - * Executes a general SQL statement and returns the {@link ResultSet} of the executed statement. The - * returned {@link ResultSet} should be closed by clients. - * - * @param sql an SQL statement string - * @param params the parameters for the SQL statement - * @param returnGeneratedKeys whether generated keys should be returned - * @param maxRows the maximum number of rows in a potential {@link ResultSet} (0 means no limit) - * @return a {@link ResultSet} - * @throws SQLException on error - */ - public final ResultSet exec(final String sql, final Object[] params, final boolean returnGeneratedKeys, - final int maxRows) throws SQLException { - return new RetryManager(params) { - - @Override - protected ResultSet call() throws SQLException { - return reallyExec(sql, params, returnGeneratedKeys, maxRows); - } - - }.doTry(); - } - - ResultSet reallyExec(String sql, Object[] params, boolean returnGeneratedKeys, int maxRows) - throws SQLException { - Connection con = null; - PreparedStatement stmt = null; - ResultSet rs = null; - boolean inBatchMode = inBatchMode(); - try { - con = getConnection(inBatchMode); - if (returnGeneratedKeys) { - stmt = con.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); - } else { - stmt = con.prepareStatement(sql); - } - stmt.setMaxRows(maxRows); - int currentFetchSize = this.fetchSize; - if (0 < maxRows && maxRows < currentFetchSize) { - currentFetchSize = maxRows; // JCR-3090 - } - stmt.setFetchSize(currentFetchSize); - execute(stmt, params); - if (returnGeneratedKeys) { - rs = stmt.getGeneratedKeys(); - } else { - rs = stmt.getResultSet(); - } - // Don't wrap null - if (rs == null) { - closeResources(con, stmt, rs, inBatchMode); - return null; - } - if (inBatchMode) { - return ResultSetWrapper.newInstance(null, stmt, rs); - } else { - return ResultSetWrapper.newInstance(con, stmt, rs); - } - } catch (SQLException e) { - closeResources(con, stmt, rs, inBatchMode); - throw e; - } - } - - /** - * Gets a connection based on the {@code batchMode} state of this helper. The connection should be closed - * by a call to {@link #closeResources(Connection, Statement, ResultSet)} which also takes the {@code - * batchMode} state into account. - * - * @param inBatchMode indicates if we are in a batchMode - * @return a {@code Connection} to use, based on the batch mode state - * @throws SQLException on error - */ - protected final Connection getConnection(boolean inBatchMode) throws SQLException { - if (inBatchMode) { - return getTransactionAwareBatchConnection(); - } else { - Connection con = dataSource.getConnection(); - // JCR-1013: Setter may fail unnecessarily on a managed connection - if (!con.getAutoCommit()) { - con.setAutoCommit(true); - } - return con; - } - } - - /** - * Returns the Batch Connection. - * - * @return Connection - */ - private Connection getTransactionAwareBatchConnection() { - Object threadId = TransactionContext.getCurrentThreadId(); - return batchConnectionMap.get(threadId); - } - - /** - * Stores the given Connection to the batchConnectionMap. - * If we are running in a XA Environment the globalTransactionId will be used as Key. - * In Non-XA Environment the ThreadName is used. - * - * @param batchConnection - */ - private void setTransactionAwareBatchConnection(Connection batchConnection) { - Object threadId = TransactionContext.getCurrentThreadId(); - batchConnectionMap.put(threadId, batchConnection); - } - - /** - * Removes the Batch Connection from the batchConnectionMap - */ - private void removeTransactionAwareBatchConnection() { - Object threadId = TransactionContext.getCurrentThreadId(); - batchConnectionMap.remove(threadId); - } - - /** - * Closes the given resources given the {@code batchMode} state. - * - * @param con the {@code Connection} obtained through the {@link #getConnection()} method - * @param stmt a {@code Statement} - * @param rs a {@code ResultSet} - * @param inBatchMode indicates if we are in a batchMode - */ - protected final void closeResources(Connection con, Statement stmt, ResultSet rs, boolean inBatchMode) { - if (inBatchMode) { - DbUtility.close(null, stmt, rs); - } else { - DbUtility.close(con, stmt, rs); - } - } - - /** - * This method is used by all methods of this class that execute SQL statements. This default - * implementation sets all parameters and unwraps {@link StreamWrapper} instances. Subclasses may override - * this method to do something special with the parameters. E.g., the {@link Oracle10R1ConnectionHelper} - * overrides it in order to add special blob handling. - * - * @param stmt the {@link PreparedStatement} to execute - * @param params the parameters - * @return the executed statement - * @throws SQLException on error - */ - protected PreparedStatement execute(PreparedStatement stmt, Object[] params) throws SQLException { - for (int i = 0; params != null && i < params.length; i++) { - Object p = params[i]; - if (p instanceof StreamWrapper) { - StreamWrapper wrapper = (StreamWrapper) p; - stmt.setBinaryStream(i + 1, wrapper.getStream(), (int) wrapper.getSize()); - } else { - stmt.setObject(i + 1, p); - } - } - try { - stmt.execute(); - } catch (SQLException e) { - //Reset Stream for retry ... - for (int i = 0; params != null && i < params.length; i++) { - Object p = params[i]; - if (p instanceof StreamWrapper) { - StreamWrapper wrapper = (StreamWrapper) p; - if(!wrapper.resetStream()) { - wrapper.cleanupResources(); - throw new RuntimeException("Unable to reset the Stream."); - } - } - } - throw e; - } - return stmt; - } - - /** - * This class encapsulates the logic to retry a method invocation if it threw an SQLException. - * The RetryManager must cleanup the Params it will get. - * - * @param the return type of the method which is retried if it failed - */ - public abstract class RetryManager { - - private Object[] params; - - public RetryManager(Object[] params) { - this.params = params; - } - - public final T doTry() throws SQLException { - if (inBatchMode()) { - return call(); - } else { - boolean sleepInterrupted = false; - int failures = 0; - SQLException lastException = null; - while (!sleepInterrupted && (blockOnConnectionLoss || failures <= RETRIES)) { - try { - T object = call(); - cleanupParamResources(); - return object; - } catch (SQLException e) { - lastException = e; - } - log.error("Failed to execute SQL (stacktrace on DEBUG log level): " + lastException); - log.debug("Failed to execute SQL", lastException); - failures++; - if (blockOnConnectionLoss || failures <= RETRIES) { // if we're going to try again - try { - Thread.sleep(SLEEP_BETWEEN_RETRIES_MS); - } catch (InterruptedException e1) { - Thread.currentThread().interrupt(); - sleepInterrupted = true; - log.error("Interrupted: canceling retry"); - } - } - } - cleanupParamResources(); - throw lastException; - } - } - - protected abstract T call() throws SQLException; - - /** - * Cleans up the Parameter resources that are not automatically closed or deleted. - * - * @param params - */ - protected void cleanupParamResources() { - for (int i = 0; params != null && i < params.length; i++) { - Object p = params[i]; - if (p instanceof StreamWrapper) { - StreamWrapper wrapper = (StreamWrapper) p; - wrapper.cleanupResources(); - } - } - } - } -} +/* + * 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.util.db; + +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import javax.sql.DataSource; + +import org.apache.jackrabbit.core.util.db.Oracle10R1ConnectionHelper; +import org.apache.jackrabbit.core.util.db.ResultSetWrapper; +import org.apache.jackrabbit.data.core.TransactionContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class provides convenience methods to execute SQL statements. They can be either executed in isolation + * or within the context of a JDBC transaction; the so-called batch mode (use the {@link #startBatch()} + * and {@link #endBatch(boolean)} methods for this). + * + *

+ * + * This class contains logic to retry execution of SQL statements. If this helper is not in batch mode + * and if a statement fails due to an {@code SQLException}, then it is retried. If the {@code block} argument + * of the constructor call was {@code false} then it is retried only once. Otherwise the statement is retried + * until either it succeeds or the thread is interrupted. This clearly assumes that the only cause of {@code + * SQLExceptions} is faulty {@code Connections} which are restored eventually.
Note: + * This retry logic only applies to the following methods: + *

    + *
  • {@link #exec(String, Object...)}
  • + *
  • {@link #update(String, Object[])}
  • + *
  • {@link #exec(String, Object[], boolean, int)}
  • + *
+ * + *

+ * + * This class is not thread-safe and if it is to be used by multiple threads then the clients must make sure + * that access to this class is properly synchronized. + * + *

+ * + * Implementation note: The {@code Connection} that is retrieved from the {@code DataSource} + * in {@link #getConnection()} may be broken. This is so because if an internal {@code DataSource} is used, + * then this is a commons-dbcp {@code DataSource} with a testWhileIdle validation strategy (see + * the {@code ConnectionFactory} class). Furthermore, if it is a {@code DataSource} obtained through JNDI then we + * can make no assumptions about the validation strategy. This means that our retry logic must either assume that + * the SQL it tries to execute can do so without errors (i.e., the statement is valid), or it must implement its + * own validation strategy to apply. Currently, the former is in place. + */ +public class ConnectionHelper { + + static Logger log = LoggerFactory.getLogger(ConnectionHelper.class); + + private static final int RETRIES = 1; + + private static final int SLEEP_BETWEEN_RETRIES_MS = 100; + + final boolean blockOnConnectionLoss; + + private final boolean checkTablesWithUserName; + + protected final DataSource dataSource; + + private Map batchConnectionMap = Collections.synchronizedMap(new HashMap()); + + /** + * The default fetchSize is '0'. This means the fetchSize Hint will be ignored + */ + private int fetchSize = 0; + + /** + * @param dataSrc the {@link DataSource} on which this instance acts + * @param block whether the helper should transparently block on DB connection loss (otherwise it retries + * once and if that fails throws exception) + */ + public ConnectionHelper(DataSource dataSrc, boolean block) { + dataSource = dataSrc; + checkTablesWithUserName = false; + blockOnConnectionLoss = block; + } + + /** + * @param dataSrc the {@link DataSource} on which this instance acts + * @param checkWithUserName whether the username is to be used for the {@link #tableExists(String)} method + * @param block whether the helper should transparently block on DB connection loss (otherwise it throws exceptions) + */ + protected ConnectionHelper(DataSource dataSrc, boolean checkWithUserName, boolean block) { + dataSource = dataSrc; + checkTablesWithUserName = checkWithUserName; + blockOnConnectionLoss = block; + } + + /** + * @param dataSrc the {@link DataSource} on which this instance acts + * @param checkWithUserName whether the username is to be used for the {@link #tableExists(String)} method + * @param block whether the helper should transparently block on DB connection loss (otherwise it throws exceptions) + * @param fetchSize the fetchSize that will be used per default + */ + protected ConnectionHelper(DataSource dataSrc, boolean checkWithUserName, boolean block, int fetchSize) { + dataSource = dataSrc; + checkTablesWithUserName = checkWithUserName; + blockOnConnectionLoss = block; + this.fetchSize = fetchSize; + } + + /** + * A utility method that makes sure that identifier does only consist of characters that are + * allowed in names on the target database. Illegal characters will be escaped as necessary. + * + * This method is not affected by the + * + * @param identifier the identifier to convert to a db specific identifier + * @return the db-normalized form of the given identifier + * @throws SQLException if an error occurs + */ + public final String prepareDbIdentifier(String identifier) throws SQLException { + if (identifier == null) { + return null; + } + String legalChars = "ABCDEFGHIJKLMNOPQRSTUVWXZY0123456789_"; + legalChars += getExtraNameCharacters(); + String id = identifier.toUpperCase(); + StringBuilder escaped = new StringBuilder(); + for (int i = 0; i < id.length(); i++) { + char c = id.charAt(i); + if (legalChars.indexOf(c) == -1) { + replaceCharacter(escaped, c); + } else { + escaped.append(c); + } + } + return escaped.toString(); + } + + /** + * Called from {@link #prepareDbIdentifier(String)}. Default implementation replaces the illegal + * characters with their hexadecimal encoding. + * + * @param escaped the escaped db identifier + * @param c the character to replace + */ + protected void replaceCharacter(StringBuilder escaped, char c) { + escaped.append("_x"); + String hex = Integer.toHexString(c); + escaped.append("0000".toCharArray(), 0, 4 - hex.length()); + escaped.append(hex); + escaped.append("_"); + } + + /** + * Returns true if we are currently in a batch mode, false otherwise. + * + * @return true if the current thread or the active transaction is running in batch mode, false otherwise. + */ + protected boolean inBatchMode() { + return getTransactionAwareBatchConnection() != null; + } + + /** + * The default implementation returns the {@code extraNameCharacters} provided by the databases metadata. + * + * @return the additional characters for identifiers supported by the db + * @throws SQLException on error + */ + private String getExtraNameCharacters() throws SQLException { + Connection con = dataSource.getConnection(); + try { + DatabaseMetaData metaData = con.getMetaData(); + return metaData.getExtraNameCharacters(); + } finally { + DbUtility.close(con, null, null); + } + } + + /** + * Checks whether the given table exists in the database. + * + * @param tableName the name of the table + * @return whether the given table exists + * @throws SQLException on error + */ + public final boolean tableExists(String tableName) throws SQLException { + Connection con = dataSource.getConnection(); + ResultSet rs = null; + boolean schemaExists = false; + String name = tableName; + try { + DatabaseMetaData metaData = con.getMetaData(); + if (metaData.storesLowerCaseIdentifiers()) { + name = tableName.toLowerCase(); + } else if (metaData.storesUpperCaseIdentifiers()) { + name = tableName.toUpperCase(); + } + String userName = null; + if (checkTablesWithUserName) { + userName = metaData.getUserName(); + } + rs = metaData.getTables(null, userName, name, null); + schemaExists = rs.next(); + } finally { + DbUtility.close(con, null, rs); + } + return schemaExists; + } + + /** + * Starts the batch mode. If an {@link SQLException} is thrown, then the batch mode is not started.

+ * Important: clients that call this method must make sure that + * {@link #endBatch(boolean)} is called eventually. + * + * @throws SQLException on error + */ + public final void startBatch() throws SQLException { + if (inBatchMode()) { + throw new SQLException("already in batch mode"); + } + Connection batchConnection = null; + try { + batchConnection = getConnection(false); + batchConnection.setAutoCommit(false); + setTransactionAwareBatchConnection(batchConnection); + } catch (SQLException e) { + removeTransactionAwareBatchConnection(); + // Strive for failure atomicity + if (batchConnection != null) { + DbUtility.close(batchConnection, null, null); + } + throw e; + } + } + + /** + * This method always ends the batch mode. + * + * @param commit whether the changes in the batch should be committed or rolled back + * @throws SQLException if the commit or rollback of the underlying JDBC Connection threw an {@code + * SQLException} + */ + public final void endBatch(boolean commit) throws SQLException { + if (!inBatchMode()) { + throw new SQLException("not in batch mode"); + } + Connection batchConnection = getTransactionAwareBatchConnection(); + try { + if (commit) { + batchConnection.commit(); + } else { + batchConnection.rollback(); + } + } finally { + removeTransactionAwareBatchConnection(); + if (batchConnection != null) { + DbUtility.close(batchConnection, null, null); + } + } + } + + /** + * Executes a general SQL statement and immediately closes all resources. + * + * Note: We use a Statement if there are no parameters to avoid a problem on + * the Oracle 10g JDBC driver w.r.t. :NEW and :OLD keywords that triggers ORA-17041. + * + * @param sql an SQL statement string + * @param params the parameters for the SQL statement + * @throws SQLException on error + */ + public final void exec(final String sql, final Object... params) throws SQLException { + new RetryManager(params) { + + @Override + protected Void call() throws SQLException { + reallyExec(sql, params); + return null; + } + + }.doTry(); + } + + void reallyExec(String sql, Object... params) throws SQLException { + Connection con = null; + Statement stmt = null; + boolean inBatchMode = inBatchMode(); + try { + con = getConnection(inBatchMode); + if (params == null || params.length == 0) { + stmt = con.createStatement(); + stmt.execute(sql); + } else { + PreparedStatement p = con.prepareStatement(sql); + stmt = p; + execute(p, params); + } + } finally { + closeResources(con, stmt, null, inBatchMode); + } + } + + /** + * Executes an update or delete statement and returns the update count. + * + * @param sql an SQL statement string + * @param params the parameters for the SQL statement + * @return the update count + * @throws SQLException on error + */ + public final int update(final String sql, final Object... params) throws SQLException { + return new RetryManager(params) { + + @Override + protected Integer call() throws SQLException { + return reallyUpdate(sql, params); + } + + }.doTry(); + } + + int reallyUpdate(String sql, Object... params) throws SQLException { + Connection con = null; + PreparedStatement stmt = null; + boolean inBatchMode = inBatchMode(); + try { + con = getConnection(inBatchMode); + stmt = con.prepareStatement(sql); + return execute(stmt, params).getUpdateCount(); + } finally { + closeResources(con, stmt, null, inBatchMode); + } + } + + /** + * Executes a SQL query and returns the {@link ResultSet}. The + * returned {@link ResultSet} should be closed by clients. + * + * @param sql an SQL statement string + * @param params the parameters for the SQL statement + * @return a {@link ResultSet} + */ + public final ResultSet query(String sql, Object... params) throws SQLException { + return exec(sql, params, false, 0); + } + + /** + * Executes a general SQL statement and returns the {@link ResultSet} of the executed statement. The + * returned {@link ResultSet} should be closed by clients. + * + * @param sql an SQL statement string + * @param params the parameters for the SQL statement + * @param returnGeneratedKeys whether generated keys should be returned + * @param maxRows the maximum number of rows in a potential {@link ResultSet} (0 means no limit) + * @return a {@link ResultSet} + * @throws SQLException on error + */ + public final ResultSet exec(final String sql, final Object[] params, final boolean returnGeneratedKeys, + final int maxRows) throws SQLException { + return new RetryManager(params) { + + @Override + protected ResultSet call() throws SQLException { + return reallyExec(sql, params, returnGeneratedKeys, maxRows); + } + + }.doTry(); + } + + ResultSet reallyExec(String sql, Object[] params, boolean returnGeneratedKeys, int maxRows) + throws SQLException { + Connection con = null; + PreparedStatement stmt = null; + ResultSet rs = null; + boolean inBatchMode = inBatchMode(); + try { + con = getConnection(inBatchMode); + if (returnGeneratedKeys) { + stmt = con.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); + } else { + stmt = con.prepareStatement(sql); + } + stmt.setMaxRows(maxRows); + int currentFetchSize = this.fetchSize; + if (0 < maxRows && maxRows < currentFetchSize) { + currentFetchSize = maxRows; // JCR-3090 + } + stmt.setFetchSize(currentFetchSize); + execute(stmt, params); + if (returnGeneratedKeys) { + rs = stmt.getGeneratedKeys(); + } else { + rs = stmt.getResultSet(); + } + // Don't wrap null + if (rs == null) { + closeResources(con, stmt, rs, inBatchMode); + return null; + } + if (inBatchMode) { + return ResultSetWrapper.newInstance(null, stmt, rs); + } else { + return ResultSetWrapper.newInstance(con, stmt, rs); + } + } catch (SQLException e) { + closeResources(con, stmt, rs, inBatchMode); + throw e; + } + } + + /** + * Gets a connection based on the {@code batchMode} state of this helper. The connection should be closed + * by a call to {@link #closeResources(Connection, Statement, ResultSet)} which also takes the {@code + * batchMode} state into account. + * + * @param inBatchMode indicates if we are in a batchMode + * @return a {@code Connection} to use, based on the batch mode state + * @throws SQLException on error + */ + protected final Connection getConnection(boolean inBatchMode) throws SQLException { + if (inBatchMode) { + return getTransactionAwareBatchConnection(); + } else { + Connection con = dataSource.getConnection(); + // JCR-1013: Setter may fail unnecessarily on a managed connection + if (!con.getAutoCommit()) { + con.setAutoCommit(true); + } + return con; + } + } + + /** + * Returns the Batch Connection. + * + * @return Connection + */ + private Connection getTransactionAwareBatchConnection() { + Object threadId = TransactionContext.getCurrentThreadId(); + return batchConnectionMap.get(threadId); + } + + /** + * Stores the given Connection to the batchConnectionMap. + * If we are running in a XA Environment the globalTransactionId will be used as Key. + * In Non-XA Environment the ThreadName is used. + * + * @param batchConnection + */ + private void setTransactionAwareBatchConnection(Connection batchConnection) { + Object threadId = TransactionContext.getCurrentThreadId(); + batchConnectionMap.put(threadId, batchConnection); + } + + /** + * Removes the Batch Connection from the batchConnectionMap + */ + private void removeTransactionAwareBatchConnection() { + Object threadId = TransactionContext.getCurrentThreadId(); + batchConnectionMap.remove(threadId); + } + + /** + * Closes the given resources given the {@code batchMode} state. + * + * @param con the {@code Connection} obtained through the {@link #getConnection()} method + * @param stmt a {@code Statement} + * @param rs a {@code ResultSet} + * @param inBatchMode indicates if we are in a batchMode + */ + protected final void closeResources(Connection con, Statement stmt, ResultSet rs, boolean inBatchMode) { + if (inBatchMode) { + DbUtility.close(null, stmt, rs); + } else { + DbUtility.close(con, stmt, rs); + } + } + + /** + * This method is used by all methods of this class that execute SQL statements. This default + * implementation sets all parameters and unwraps {@link StreamWrapper} instances. Subclasses may override + * this method to do something special with the parameters. E.g., the {@link Oracle10R1ConnectionHelper} + * overrides it in order to add special blob handling. + * + * @param stmt the {@link PreparedStatement} to execute + * @param params the parameters + * @return the executed statement + * @throws SQLException on error + */ + protected PreparedStatement execute(PreparedStatement stmt, Object[] params) throws SQLException { + for (int i = 0; params != null && i < params.length; i++) { + Object p = params[i]; + if (p instanceof StreamWrapper) { + StreamWrapper wrapper = (StreamWrapper) p; + stmt.setBinaryStream(i + 1, wrapper.getStream(), (int) wrapper.getSize()); + } else { + stmt.setObject(i + 1, p); + } + } + try { + stmt.execute(); + } catch (SQLException e) { + //Reset Stream for retry ... + for (int i = 0; params != null && i < params.length; i++) { + Object p = params[i]; + if (p instanceof StreamWrapper) { + StreamWrapper wrapper = (StreamWrapper) p; + if(!wrapper.resetStream()) { + wrapper.cleanupResources(); + throw new RuntimeException("Unable to reset the Stream."); + } + } + } + throw e; + } + return stmt; + } + + /** + * This class encapsulates the logic to retry a method invocation if it threw an SQLException. + * The RetryManager must cleanup the Params it will get. + * + * @param the return type of the method which is retried if it failed + */ + public abstract class RetryManager { + + private Object[] params; + + public RetryManager(Object[] params) { + this.params = params; + } + + public final T doTry() throws SQLException { + if (inBatchMode()) { + return call(); + } else { + boolean sleepInterrupted = false; + int failures = 0; + SQLException lastException = null; + while (!sleepInterrupted && (blockOnConnectionLoss || failures <= RETRIES)) { + try { + T object = call(); + cleanupParamResources(); + return object; + } catch (SQLException e) { + lastException = e; + } + log.error("Failed to execute SQL (stacktrace on DEBUG log level): " + lastException); + log.debug("Failed to execute SQL", lastException); + failures++; + if (blockOnConnectionLoss || failures <= RETRIES) { // if we're going to try again + try { + Thread.sleep(SLEEP_BETWEEN_RETRIES_MS); + } catch (InterruptedException e1) { + Thread.currentThread().interrupt(); + sleepInterrupted = true; + log.error("Interrupted: canceling retry"); + } + } + } + cleanupParamResources(); + throw lastException; + } + } + + protected abstract T call() throws SQLException; + + /** + * Cleans up the Parameter resources that are not automatically closed or deleted. + * + * @param params + */ + protected void cleanupParamResources() { + for (int i = 0; params != null && i < params.length; i++) { + Object p = params[i]; + if (p instanceof StreamWrapper) { + StreamWrapper wrapper = (StreamWrapper) p; + wrapper.cleanupResources(); + } + } + } + } +} Propchange: jackrabbit/trunk/jackrabbit-data/src/main/java/org/apache/jackrabbit/core/util/db/ConnectionHelper.java ------------------------------------------------------------------------------ svn:eol-style = native Modified: jackrabbit/trunk/jackrabbit-data/src/main/java/org/apache/jackrabbit/core/util/db/DataSourceWrapper.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-data/src/main/java/org/apache/jackrabbit/core/util/db/DataSourceWrapper.java?rev=1576690&r1=1576689&r2=1576690&view=diff ============================================================================== --- jackrabbit/trunk/jackrabbit-data/src/main/java/org/apache/jackrabbit/core/util/db/DataSourceWrapper.java (original) +++ jackrabbit/trunk/jackrabbit-data/src/main/java/org/apache/jackrabbit/core/util/db/DataSourceWrapper.java Wed Mar 12 11:05:06 2014 @@ -1,119 +1,119 @@ -/* - * 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.util.db; - -import java.io.PrintWriter; -import java.sql.Connection; -import java.sql.SQLException; -import java.util.logging.Logger; - -import javax.sql.DataSource; - -/** - * This class delegates all calls to the corresponding method on the wrapped {@code DataSource} except for the {@link #getConnection()} method, - * which delegates to {@code DataSource#getConnection(String, String)} with the username and password - * which are given on construction. - */ -public class DataSourceWrapper implements DataSource { - - private final DataSource dataSource; - - private final String username; - - private final String password; - - /** - * @param dataSource the {@code DataSource} to wrap - * @param username the username to use - * @param password the password to use - */ - public DataSourceWrapper(DataSource dataSource, String username, String password) { - this.dataSource = dataSource; - this.username = username; - this.password = password; - } - - /** - * Java 6 method. - * - * {@inheritDoc} - */ - public boolean isWrapperFor(Class arg0) throws SQLException { - throw new UnsupportedOperationException("Java 6 method not supported"); - } - - /** - * Java 6 method. - * - * {@inheritDoc} - */ - public T unwrap(Class arg0) throws SQLException { - throw new UnsupportedOperationException("Java 6 method not supported"); - } - - /** - * Unsupported Java 7 method. - * - * @see JCR-3167 - */ - public Logger getParentLogger() { - throw new UnsupportedOperationException("Java 7 method not supported"); - } - - /** - * {@inheritDoc} - */ - public Connection getConnection() throws SQLException { - return dataSource.getConnection(username, password); - } - - /** - * {@inheritDoc} - */ - public Connection getConnection(String username, String password) throws SQLException { - return dataSource.getConnection(username, password); - } - - /** - * {@inheritDoc} - */ - public PrintWriter getLogWriter() throws SQLException { - return dataSource.getLogWriter(); - } - - /** - * {@inheritDoc} - */ - public int getLoginTimeout() throws SQLException { - return dataSource.getLoginTimeout(); - } - - /** - * {@inheritDoc} - */ - public void setLogWriter(PrintWriter out) throws SQLException { - dataSource.setLogWriter(out); - } - - /** - * {@inheritDoc} - */ - public void setLoginTimeout(int seconds) throws SQLException { - dataSource.setLoginTimeout(seconds); - } - -} +/* + * 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.util.db; + +import java.io.PrintWriter; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.logging.Logger; + +import javax.sql.DataSource; + +/** + * This class delegates all calls to the corresponding method on the wrapped {@code DataSource} except for the {@link #getConnection()} method, + * which delegates to {@code DataSource#getConnection(String, String)} with the username and password + * which are given on construction. + */ +public class DataSourceWrapper implements DataSource { + + private final DataSource dataSource; + + private final String username; + + private final String password; + + /** + * @param dataSource the {@code DataSource} to wrap + * @param username the username to use + * @param password the password to use + */ + public DataSourceWrapper(DataSource dataSource, String username, String password) { + this.dataSource = dataSource; + this.username = username; + this.password = password; + } + + /** + * Java 6 method. + * + * {@inheritDoc} + */ + public boolean isWrapperFor(Class arg0) throws SQLException { + throw new UnsupportedOperationException("Java 6 method not supported"); + } + + /** + * Java 6 method. + * + * {@inheritDoc} + */ + public T unwrap(Class arg0) throws SQLException { + throw new UnsupportedOperationException("Java 6 method not supported"); + } + + /** + * Unsupported Java 7 method. + * + * @see JCR-3167 + */ + public Logger getParentLogger() { + throw new UnsupportedOperationException("Java 7 method not supported"); + } + + /** + * {@inheritDoc} + */ + public Connection getConnection() throws SQLException { + return dataSource.getConnection(username, password); + } + + /** + * {@inheritDoc} + */ + public Connection getConnection(String username, String password) throws SQLException { + return dataSource.getConnection(username, password); + } + + /** + * {@inheritDoc} + */ + public PrintWriter getLogWriter() throws SQLException { + return dataSource.getLogWriter(); + } + + /** + * {@inheritDoc} + */ + public int getLoginTimeout() throws SQLException { + return dataSource.getLoginTimeout(); + } + + /** + * {@inheritDoc} + */ + public void setLogWriter(PrintWriter out) throws SQLException { + dataSource.setLogWriter(out); + } + + /** + * {@inheritDoc} + */ + public void setLoginTimeout(int seconds) throws SQLException { + dataSource.setLoginTimeout(seconds); + } + +} Propchange: jackrabbit/trunk/jackrabbit-data/src/main/java/org/apache/jackrabbit/core/util/db/DataSourceWrapper.java ------------------------------------------------------------------------------ svn:eol-style = native Modified: jackrabbit/trunk/jackrabbit-data/src/main/java/org/apache/jackrabbit/core/util/db/DatabaseAware.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-data/src/main/java/org/apache/jackrabbit/core/util/db/DatabaseAware.java?rev=1576690&r1=1576689&r2=1576690&view=diff ============================================================================== --- jackrabbit/trunk/jackrabbit-data/src/main/java/org/apache/jackrabbit/core/util/db/DatabaseAware.java (original) +++ jackrabbit/trunk/jackrabbit-data/src/main/java/org/apache/jackrabbit/core/util/db/DatabaseAware.java Wed Mar 12 11:05:06 2014 @@ -1,30 +1,30 @@ -/* - * 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.util.db; - -/** - * Bean components (i.e., classes that appear in the repository descriptor) that implement this interface will - * get the repositories {@link ConnectionFactory} instance injected just after construction and before - * initialization. - */ -public interface DatabaseAware { - - /** - * @param connectionFactory - */ - void setConnectionFactory(ConnectionFactory connectionFactory); -} +/* + * 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.util.db; + +/** + * Bean components (i.e., classes that appear in the repository descriptor) that implement this interface will + * get the repositories {@link ConnectionFactory} instance injected just after construction and before + * initialization. + */ +public interface DatabaseAware { + + /** + * @param connectionFactory + */ + void setConnectionFactory(ConnectionFactory connectionFactory); +} Propchange: jackrabbit/trunk/jackrabbit-data/src/main/java/org/apache/jackrabbit/core/util/db/DatabaseAware.java ------------------------------------------------------------------------------ svn:eol-style = native Modified: jackrabbit/trunk/jackrabbit-data/src/main/java/org/apache/jackrabbit/core/util/db/DbUtility.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-data/src/main/java/org/apache/jackrabbit/core/util/db/DbUtility.java?rev=1576690&r1=1576689&r2=1576690&view=diff ============================================================================== --- jackrabbit/trunk/jackrabbit-data/src/main/java/org/apache/jackrabbit/core/util/db/DbUtility.java (original) +++ jackrabbit/trunk/jackrabbit-data/src/main/java/org/apache/jackrabbit/core/util/db/DbUtility.java Wed Mar 12 11:05:06 2014 @@ -1,98 +1,98 @@ -/* - * 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.util.db; - -import java.sql.Connection; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * This class contains some database utility methods. - */ -public final class DbUtility { - - private static final Logger LOG = LoggerFactory.getLogger(DbUtility.class); - - /** - * Private constructor for utility class pattern. - */ - private DbUtility() { - } - - /** - * This is a utility method which closes the given resources without throwing exceptions. Any exceptions - * encountered are logged instead. - * - * @param rs the {@link ResultSet} to close, may be null - */ - public static void close(ResultSet rs) { - close(null, null, rs); - } - - /** - * This is a utility method which closes the given resources without throwing exceptions. Any exceptions - * encountered are logged instead. - * - * @param con the {@link Connection} to close, may be null - * @param stmt the {@link Statement} to close, may be null - * @param rs the {@link ResultSet} to close, may be null - */ - public static void close(Connection con, Statement stmt, ResultSet rs) { - try { - if (rs != null) { - rs.close(); - } - } catch (SQLException e) { - logException("failed to close ResultSet", e); - } finally { - try { - if (stmt != null) { - stmt.close(); - } - } catch (SQLException e) { - logException("failed to close Statement", e); - } finally { - try { - if (con != null && !con.isClosed()) { - con.close(); - } - } catch (SQLException e) { - logException("failed to close Connection", e); - } - } - } - } - - /** - * Logs an SQL exception on error level, and debug level (more detail). - * - * @param message the message - * @param e the exception - */ - public static void logException(String message, SQLException e) { - if (message != null) { - LOG.error(message); - } - LOG.error(" Reason: " + e.getMessage()); - LOG.error(" State/Code: " + e.getSQLState() + "/" + e.getErrorCode()); - LOG.debug(" dump:", e); - } -} +/* + * 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.util.db; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class contains some database utility methods. + */ +public final class DbUtility { + + private static final Logger LOG = LoggerFactory.getLogger(DbUtility.class); + + /** + * Private constructor for utility class pattern. + */ + private DbUtility() { + } + + /** + * This is a utility method which closes the given resources without throwing exceptions. Any exceptions + * encountered are logged instead. + * + * @param rs the {@link ResultSet} to close, may be null + */ + public static void close(ResultSet rs) { + close(null, null, rs); + } + + /** + * This is a utility method which closes the given resources without throwing exceptions. Any exceptions + * encountered are logged instead. + * + * @param con the {@link Connection} to close, may be null + * @param stmt the {@link Statement} to close, may be null + * @param rs the {@link ResultSet} to close, may be null + */ + public static void close(Connection con, Statement stmt, ResultSet rs) { + try { + if (rs != null) { + rs.close(); + } + } catch (SQLException e) { + logException("failed to close ResultSet", e); + } finally { + try { + if (stmt != null) { + stmt.close(); + } + } catch (SQLException e) { + logException("failed to close Statement", e); + } finally { + try { + if (con != null && !con.isClosed()) { + con.close(); + } + } catch (SQLException e) { + logException("failed to close Connection", e); + } + } + } + } + + /** + * Logs an SQL exception on error level, and debug level (more detail). + * + * @param message the message + * @param e the exception + */ + public static void logException(String message, SQLException e) { + if (message != null) { + LOG.error(message); + } + LOG.error(" Reason: " + e.getMessage()); + LOG.error(" State/Code: " + e.getSQLState() + "/" + e.getErrorCode()); + LOG.debug(" dump:", e); + } +} Propchange: jackrabbit/trunk/jackrabbit-data/src/main/java/org/apache/jackrabbit/core/util/db/DbUtility.java ------------------------------------------------------------------------------ svn:eol-style = native Modified: jackrabbit/trunk/jackrabbit-data/src/main/java/org/apache/jackrabbit/core/util/db/DerbyConnectionHelper.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-data/src/main/java/org/apache/jackrabbit/core/util/db/DerbyConnectionHelper.java?rev=1576690&r1=1576689&r2=1576690&view=diff ============================================================================== --- jackrabbit/trunk/jackrabbit-data/src/main/java/org/apache/jackrabbit/core/util/db/DerbyConnectionHelper.java (original) +++ jackrabbit/trunk/jackrabbit-data/src/main/java/org/apache/jackrabbit/core/util/db/DerbyConnectionHelper.java Wed Mar 12 11:05:06 2014 @@ -1,97 +1,97 @@ -/* - * 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.util.db; - -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.SQLException; - -import javax.sql.DataSource; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * - */ -public final class DerbyConnectionHelper extends ConnectionHelper { - - /** name of the embedded driver */ - public static final String DERBY_EMBEDDED_DRIVER = "org.apache.derby.jdbc.EmbeddedDriver"; - - private static Logger log = LoggerFactory.getLogger(DerbyConnectionHelper.class); - - /** - * @param dataSrc the {@code DataSource} on which this helper acts - * @param block whether to block on connection loss until the db is up again - */ - public DerbyConnectionHelper(DataSource dataSrc, boolean block) { - super(dataSrc, block); - } - - /** - * Shuts the embedded Derby database down. - * - * @param driver the driver - * @throws SQLException on failure - */ - public void shutDown(String driver) throws SQLException { - // check for embedded driver - if (!DERBY_EMBEDDED_DRIVER.equals(driver)) { - return; - } - - // prepare connection url for issuing shutdown command - String url = null; - Connection con = null; - - try { - con = dataSource.getConnection(); - try { - url = con.getMetaData().getURL(); - } catch (SQLException e) { - // JCR-1557: embedded derby db probably already shut down; - // this happens when configuring multiple FS/PM instances - // to use the same embedded derby db instance. - log.debug("failed to retrieve connection url: embedded db probably already shut down", e); - return; - } - // we have to reset the connection to 'autoCommit=true' before closing it; - // otherwise Derby would mysteriously complain about some pending uncommitted - // changes which can't possibly be true. - // @todo further investigate - con.setAutoCommit(true); - } - finally { - DbUtility.close(con, null, null); - } - int pos = url.lastIndexOf(';'); - if (pos != -1) { - // strip any attributes from connection url - url = url.substring(0, pos); - } - url += ";shutdown=true"; - - // now it's safe to shutdown the embedded Derby database - try { - DriverManager.getConnection(url); - } catch (SQLException e) { - // a shutdown command always raises a SQLException - log.info(e.getMessage()); - } - } -} +/* + * 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.util.db; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; + +import javax.sql.DataSource; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * + */ +public final class DerbyConnectionHelper extends ConnectionHelper { + + /** name of the embedded driver */ + public static final String DERBY_EMBEDDED_DRIVER = "org.apache.derby.jdbc.EmbeddedDriver"; + + private static Logger log = LoggerFactory.getLogger(DerbyConnectionHelper.class); + + /** + * @param dataSrc the {@code DataSource} on which this helper acts + * @param block whether to block on connection loss until the db is up again + */ + public DerbyConnectionHelper(DataSource dataSrc, boolean block) { + super(dataSrc, block); + } + + /** + * Shuts the embedded Derby database down. + * + * @param driver the driver + * @throws SQLException on failure + */ + public void shutDown(String driver) throws SQLException { + // check for embedded driver + if (!DERBY_EMBEDDED_DRIVER.equals(driver)) { + return; + } + + // prepare connection url for issuing shutdown command + String url = null; + Connection con = null; + + try { + con = dataSource.getConnection(); + try { + url = con.getMetaData().getURL(); + } catch (SQLException e) { + // JCR-1557: embedded derby db probably already shut down; + // this happens when configuring multiple FS/PM instances + // to use the same embedded derby db instance. + log.debug("failed to retrieve connection url: embedded db probably already shut down", e); + return; + } + // we have to reset the connection to 'autoCommit=true' before closing it; + // otherwise Derby would mysteriously complain about some pending uncommitted + // changes which can't possibly be true. + // @todo further investigate + con.setAutoCommit(true); + } + finally { + DbUtility.close(con, null, null); + } + int pos = url.lastIndexOf(';'); + if (pos != -1) { + // strip any attributes from connection url + url = url.substring(0, pos); + } + url += ";shutdown=true"; + + // now it's safe to shutdown the embedded Derby database + try { + DriverManager.getConnection(url); + } catch (SQLException e) { + // a shutdown command always raises a SQLException + log.info(e.getMessage()); + } + } +} Propchange: jackrabbit/trunk/jackrabbit-data/src/main/java/org/apache/jackrabbit/core/util/db/DerbyConnectionHelper.java ------------------------------------------------------------------------------ svn:eol-style = native Modified: jackrabbit/trunk/jackrabbit-data/src/main/java/org/apache/jackrabbit/core/util/db/Oracle10R1ConnectionHelper.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-data/src/main/java/org/apache/jackrabbit/core/util/db/Oracle10R1ConnectionHelper.java?rev=1576690&r1=1576689&r2=1576690&view=diff ============================================================================== --- jackrabbit/trunk/jackrabbit-data/src/main/java/org/apache/jackrabbit/core/util/db/Oracle10R1ConnectionHelper.java (original) +++ jackrabbit/trunk/jackrabbit-data/src/main/java/org/apache/jackrabbit/core/util/db/Oracle10R1ConnectionHelper.java Wed Mar 12 11:05:06 2014 @@ -1,168 +1,168 @@ -/* - * 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.util.db; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.lang.reflect.Method; -import java.sql.Blob; -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.List; - -import javax.sql.DataSource; - -import org.apache.commons.io.IOUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * The connection helper for Oracle databases of version up to 10.1. It has special blob handling. - */ -public final class Oracle10R1ConnectionHelper extends OracleConnectionHelper { - - /** - * the default logger - */ - private static Logger log = LoggerFactory.getLogger(Oracle10R1ConnectionHelper.class); - - private Class blobClass; - - private Integer durationSessionConstant; - - private Integer modeReadWriteConstant; - - /** - * @param dataSrc the {@code DataSource} on which this helper acts - * @param block whether to block on connection loss until the db is up again - */ - public Oracle10R1ConnectionHelper(DataSource dataSrc, boolean block) { - super(dataSrc, block); - } - - /** - * Retrieve the oracle.sql.BLOB class via reflection, and initialize the values for the - * DURATION_SESSION and MODE_READWRITE constants defined there. - * - * @see oracle.sql.BLOB#DURATION_SESSION - * @see oracle.sql.BLOB#MODE_READWRITE - */ - @Override - public void init() throws Exception { - super.init(); - // initialize oracle.sql.BLOB class & constants - - // use the Connection object for using the exact same - // class loader that the Oracle driver was loaded with - Connection con = null; - try { - con = dataSource.getConnection(); - blobClass = con.getClass().getClassLoader().loadClass("oracle.sql.BLOB"); - durationSessionConstant = new Integer(blobClass.getField("DURATION_SESSION").getInt(null)); - modeReadWriteConstant = new Integer(blobClass.getField("MODE_READWRITE").getInt(null)); - } finally { - if (con != null) { - DbUtility.close(con, null, null); - } - } - } - - /** - * Wraps any input-stream parameters in temporary blobs and frees these again after the statement - * has been executed. - * - * {@inheritDoc} - */ - @Override - protected PreparedStatement execute(PreparedStatement stmt, Object[] params) throws SQLException { - List tmpBlobs = new ArrayList(); - try { - for (int i = 0; params != null && i < params.length; i++) { - Object p = params[i]; - if (p instanceof StreamWrapper) { - StreamWrapper wrapper = (StreamWrapper) p; - Blob tmp = createTemporaryBlob(stmt.getConnection(), wrapper.getStream()); - tmpBlobs.add(tmp); - stmt.setBlob(i + 1, tmp); - } else if (p instanceof InputStream) { - Blob tmp = createTemporaryBlob(stmt.getConnection(), (InputStream) p); - tmpBlobs.add(tmp); - stmt.setBlob(i + 1, tmp); - } else { - stmt.setObject(i + 1, p); - } - } - stmt.execute(); - return stmt; - } catch (Exception e) { - throw new SQLException(e.getMessage()); - } finally { - for (Blob blob : tmpBlobs) { - try { - freeTemporaryBlob(blob); - } catch (Exception e) { - log.warn("Could not close temporary blob", e); - } - } - } - } - - /** - * Creates a temporary oracle.sql.BLOB instance via reflection and spools the contents of the specified - * stream. - */ - private Blob createTemporaryBlob(Connection con, InputStream in) throws Exception { - /* - * BLOB blob = BLOB.createTemporary(con, false, BLOB.DURATION_SESSION); - * blob.open(BLOB.MODE_READWRITE); OutputStream out = blob.getBinaryOutputStream(); ... out.flush(); - * out.close(); blob.close(); return blob; - */ - Method createTemporary = - blobClass.getMethod("createTemporary", new Class[]{Connection.class, Boolean.TYPE, Integer.TYPE}); - Object blob = - createTemporary.invoke(null, new Object[]{ConnectionFactory.unwrap(con), Boolean.FALSE, - durationSessionConstant}); - Method open = blobClass.getMethod("open", new Class[]{Integer.TYPE}); - open.invoke(blob, new Object[]{modeReadWriteConstant}); - Method getBinaryOutputStream = blobClass.getMethod("getBinaryOutputStream", new Class[0]); - OutputStream out = (OutputStream) getBinaryOutputStream.invoke(blob); - try { - IOUtils.copy(in, out); - } finally { - try { - out.flush(); - } catch (IOException ioe) { - } - out.close(); - } - Method close = blobClass.getMethod("close", new Class[0]); - close.invoke(blob); - return (Blob) blob; - } - - /** - * Frees a temporary oracle.sql.BLOB instance via reflection. - */ - private void freeTemporaryBlob(Blob blob) throws Exception { - // blob.freeTemporary(); - Method freeTemporary = blobClass.getMethod("freeTemporary", new Class[0]); - freeTemporary.invoke(blob); - } -} +/* + * 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.util.db; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.Method; +import java.sql.Blob; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +import javax.sql.DataSource; + +import org.apache.commons.io.IOUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The connection helper for Oracle databases of version up to 10.1. It has special blob handling. + */ +public final class Oracle10R1ConnectionHelper extends OracleConnectionHelper { + + /** + * the default logger + */ + private static Logger log = LoggerFactory.getLogger(Oracle10R1ConnectionHelper.class); + + private Class blobClass; + + private Integer durationSessionConstant; + + private Integer modeReadWriteConstant; + + /** + * @param dataSrc the {@code DataSource} on which this helper acts + * @param block whether to block on connection loss until the db is up again + */ + public Oracle10R1ConnectionHelper(DataSource dataSrc, boolean block) { + super(dataSrc, block); + } + + /** + * Retrieve the oracle.sql.BLOB class via reflection, and initialize the values for the + * DURATION_SESSION and MODE_READWRITE constants defined there. + * + * @see oracle.sql.BLOB#DURATION_SESSION + * @see oracle.sql.BLOB#MODE_READWRITE + */ + @Override + public void init() throws Exception { + super.init(); + // initialize oracle.sql.BLOB class & constants + + // use the Connection object for using the exact same + // class loader that the Oracle driver was loaded with + Connection con = null; + try { + con = dataSource.getConnection(); + blobClass = con.getClass().getClassLoader().loadClass("oracle.sql.BLOB"); + durationSessionConstant = new Integer(blobClass.getField("DURATION_SESSION").getInt(null)); + modeReadWriteConstant = new Integer(blobClass.getField("MODE_READWRITE").getInt(null)); + } finally { + if (con != null) { + DbUtility.close(con, null, null); + } + } + } + + /** + * Wraps any input-stream parameters in temporary blobs and frees these again after the statement + * has been executed. + * + * {@inheritDoc} + */ + @Override + protected PreparedStatement execute(PreparedStatement stmt, Object[] params) throws SQLException { + List tmpBlobs = new ArrayList(); + try { + for (int i = 0; params != null && i < params.length; i++) { + Object p = params[i]; + if (p instanceof StreamWrapper) { + StreamWrapper wrapper = (StreamWrapper) p; + Blob tmp = createTemporaryBlob(stmt.getConnection(), wrapper.getStream()); + tmpBlobs.add(tmp); + stmt.setBlob(i + 1, tmp); + } else if (p instanceof InputStream) { + Blob tmp = createTemporaryBlob(stmt.getConnection(), (InputStream) p); + tmpBlobs.add(tmp); + stmt.setBlob(i + 1, tmp); + } else { + stmt.setObject(i + 1, p); + } + } + stmt.execute(); + return stmt; + } catch (Exception e) { + throw new SQLException(e.getMessage()); + } finally { + for (Blob blob : tmpBlobs) { + try { + freeTemporaryBlob(blob); + } catch (Exception e) { + log.warn("Could not close temporary blob", e); + } + } + } + } + + /** + * Creates a temporary oracle.sql.BLOB instance via reflection and spools the contents of the specified + * stream. + */ + private Blob createTemporaryBlob(Connection con, InputStream in) throws Exception { + /* + * BLOB blob = BLOB.createTemporary(con, false, BLOB.DURATION_SESSION); + * blob.open(BLOB.MODE_READWRITE); OutputStream out = blob.getBinaryOutputStream(); ... out.flush(); + * out.close(); blob.close(); return blob; + */ + Method createTemporary = + blobClass.getMethod("createTemporary", new Class[]{Connection.class, Boolean.TYPE, Integer.TYPE}); + Object blob = + createTemporary.invoke(null, new Object[]{ConnectionFactory.unwrap(con), Boolean.FALSE, + durationSessionConstant}); + Method open = blobClass.getMethod("open", new Class[]{Integer.TYPE}); + open.invoke(blob, new Object[]{modeReadWriteConstant}); + Method getBinaryOutputStream = blobClass.getMethod("getBinaryOutputStream", new Class[0]); + OutputStream out = (OutputStream) getBinaryOutputStream.invoke(blob); + try { + IOUtils.copy(in, out); + } finally { + try { + out.flush(); + } catch (IOException ioe) { + } + out.close(); + } + Method close = blobClass.getMethod("close", new Class[0]); + close.invoke(blob); + return (Blob) blob; + } + + /** + * Frees a temporary oracle.sql.BLOB instance via reflection. + */ + private void freeTemporaryBlob(Blob blob) throws Exception { + // blob.freeTemporary(); + Method freeTemporary = blobClass.getMethod("freeTemporary", new Class[0]); + freeTemporary.invoke(blob); + } +}