jackrabbit-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From resc...@apache.org
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 GMT
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 <i>batch mode</i> (use the {@link #startBatch()}
- * and {@link #endBatch(boolean)} methods for this).
- *
- * <p/>
- *
- * This class contains logic to retry execution of SQL statements. If this helper is <i>not</i> 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. <br/> <strong>Note</strong>:
- * This retry logic only applies to the following methods:
- * <ul>
- * <li>{@link #exec(String, Object...)}</li>
- * <li>{@link #update(String, Object[])}</li>
- * <li>{@link #exec(String, Object[], boolean, int)}</li>
- * </ul>
- *
- * <p/>
- *
- * 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.
- *
- * <p/>
- *
- * <strong>Implementation note</strong>: 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 <code>testWhileIdle</code> 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<Object, Connection> batchConnectionMap = Collections.synchronizedMap(new HashMap<Object, Connection>());
-
-    /**
-     * 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 <code>identifier</code> 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 <i>batch mode</i>. If an {@link SQLException} is thrown, then the batch mode is not started. <p/>
-     * <strong>Important:</strong> 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 <i>batch mode</i>.
-     *
-     * @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<Void>(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<Integer>(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<ResultSet>(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 <T> the return type of the method which is retried if it failed
-     */
-    public abstract class RetryManager<T> {
-
-    	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 <i>batch mode</i> (use the {@link #startBatch()}
+ * and {@link #endBatch(boolean)} methods for this).
+ *
+ * <p/>
+ *
+ * This class contains logic to retry execution of SQL statements. If this helper is <i>not</i> 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. <br/> <strong>Note</strong>:
+ * This retry logic only applies to the following methods:
+ * <ul>
+ * <li>{@link #exec(String, Object...)}</li>
+ * <li>{@link #update(String, Object[])}</li>
+ * <li>{@link #exec(String, Object[], boolean, int)}</li>
+ * </ul>
+ *
+ * <p/>
+ *
+ * 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.
+ *
+ * <p/>
+ *
+ * <strong>Implementation note</strong>: 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 <code>testWhileIdle</code> 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<Object, Connection> batchConnectionMap = Collections.synchronizedMap(new HashMap<Object, Connection>());
+
+    /**
+     * 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 <code>identifier</code> 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 <i>batch mode</i>. If an {@link SQLException} is thrown, then the batch mode is not started. <p/>
+     * <strong>Important:</strong> 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 <i>batch mode</i>.
+     *
+     * @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<Void>(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<Integer>(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<ResultSet>(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 <T> the return type of the method which is retried if it failed
+     */
+    public abstract class RetryManager<T> {
+
+    	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> T unwrap(Class<T> arg0) throws SQLException {
-        throw new UnsupportedOperationException("Java 6 method not supported");
-    }
-
-    /**
-     * Unsupported Java 7 method.
-     *
-     * @see <a href="https://issues.apache.org/jira/browse/JCR-3167">JCR-3167</a>
-     */
-    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> T unwrap(Class<T> arg0) throws SQLException {
+        throw new UnsupportedOperationException("Java 6 method not supported");
+    }
+
+    /**
+     * Unsupported Java 7 method.
+     *
+     * @see <a href="https://issues.apache.org/jira/browse/JCR-3167">JCR-3167</a>
+     */
+    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 <code>oracle.sql.BLOB</code> class via reflection, and initialize the values for the
-     * <code>DURATION_SESSION</code> and <code>MODE_READWRITE</code> 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<Blob> tmpBlobs = new ArrayList<Blob>();
-        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 <code>oracle.sql.BLOB</code> class via reflection, and initialize the values for the
+     * <code>DURATION_SESSION</code> and <code>MODE_READWRITE</code> 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<Blob> tmpBlobs = new ArrayList<Blob>();
+        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);
+    }
+}



Mime
View raw message